Language extensions introduced in Batteries Included

Several language extensions and convenience features are introduced in Batteries Included. To take advantage of these features, please see the corresponding chapter. Note that most of these extensions were not developed specifically for Batteries Included and may be used separately.

Opening of modules

(activated by default, implemented as findlib package batteries.pa_openin.syntax)

Local opening of modules

(forked from openin)
You can open local modules, inside an expression if needed.
For instance:
open File in
 (*Here, File is opened*)
 lines_of "some_file"

(*Here, File is not opened anymore*)
These modules do not need to be named. For instance, you may write
open Hashtbl.Make(String) in

Initialization of modules

(designed for Batteries)
As a convenience, you can specify an expression to be evaluated when opening a module. For instance,
open Random with self_init ()
This extract is equivalent to
open Random
let _ = self_init ()
This feature is provided as it may sometimes increase readability.

Opening several modules

(designed for Batteries)
As a convenience, you can specify a comma-separated list of modules to be opened in one operation. Consider for instance
open List, Array
This extract is equivalent to
open List
open Array
This feature is provided as it may sometimes increase readability.

Aliasing several modules to one name

(designed for Batteries)
As a convenience, you can specify that a module name should serve to represent the union of several modules. Consider for instance
module E = Enum with Labels, LExceptionless
This extract defines a local module name E with all the values and types of modules Enum, Labels and LExceptionless. In particular, some values defined in Enum may be overridden with values defined in Labels and/or LExceptionless.
module Enum =
 include Enum
 include Labels
 include LExceptionless
This feature is provided as it may sometimes increase readability.
One of the main uses of this feature is to permit opening in one operation modules which define some functions and modules which override these functions. This is the case of module IO, which defines operations for input and output: this module contains a submodule BigEndian, which defines operations which should be used to communicate with specific architectures. One of these functions is read_float: IO.read_float is adapted to small-endian architectures, while IO.BigEndian.read_float is adapted to big-endian architectures The functions of BigEndian have the same name as functions of IO. The following extract will therefore open both IO and BigEndian in one operation:
open IO, BigEndian
This extract is equivalent to
open IO
open BigEndian

"where" construction

(activated by default, implemented as findlib package batteries.pa_where.syntax)
(part of pa_where, distributed with Batteries)
By default, OCaml provides a construction This extension adds a dual construction ... where ..., once again, to increase readability when appropriate.
Where lets you define a value and use it immediately, ... where... lets you use a value and define it immediately, as is often done in mathematic notations. Consider
let fibo n = fst fibo_aux n
  where rec fibo_aux = function
    | 0 -> (1, 1)
    | 1 -> (1, 2)
    | n -> (a, a + b) where let (a, b) = fibo_aux ( n - 1 )
This extract is equivalent to
let fibo n =
  let rec fibo_aux =
    | 0 -> (1, 1)
    | 1 -> (1, 2)
    | n -> let (a, b) = fibo_aux (n - 1) in (a, (a + b))
  in fst (fibo_aux n)
More generally, <expression> where <definitions> is equivalent to let <definitions> in <expression>. Note that you can define (mutually) recursive functions with where just as well as with let.
As a convenience, you may wrote where let instead of where.
a where b where c is equivalent to (a where b) where c
let a = b where c and d is equivalent to let a = (b where c and d)

Comprehension expressions

(activated by default, implemented as findlib package batteries.pa_comprehension.syntax)
(designed for Batteries)
list comprehension syntax for batteries, using enumerations by default.

Output, generators and guards

The global form is [? output | comp_item ; comp_item ; ... ].
output is an expression and a comp_item is either a guard (a boolean expression), or a generator of the form pattern <- expression. Variables bound in the pattern can be used in the following comprehension items, and in the output expression.

Example :
let pythagorean_triples n =
  [? (a,b,c) | a <- 1--n; b <- a--n; c <- b--n; a*a + b*b = c*c ]

Module parametrization

By default, the output in an enumeration, and generator expressions are assumed to be enumerations. It is possible to choose a different data structure with the module : expression syntax.

Examples :
let positive_array_of_enum e =
  [? Array : n | n <- e; n > 0 ]

let combine la lb =
  [? List : (a, b) | a <- List : la; b <- List : lb ]

Comprehension expressions rely on the presence in the given module of the following operations (where 'a t represents the data-structure type : 'a array, 'a Enum.t...) :

val filter : ('a -> bool) -> 'a t -> 'a t
val concat : 'a t t -> 'a t
val map : ('a -> 'b) -> 'a t -> 'b t
val filter_map : ('a -> 'b option) -> 'a t -> 'b t (* used for refutable patterns in generators *)

val enum : 'a t -> 'a Enum.t
val of_enum : 'a Enum.t -> 'a t

If your module does not provide the first four operations but only the enum conversion functions, you could still benefit from the comprehension syntax by using eg. foo <- Mod.enum bar instead of foo <- Mod : bar.

String syntax

(activated by default, implemented as findlib package batteries.pa_string.syntax)
(part of estring, adapted for Batteries by the author)
As Batteries offers several new types of strings, this extension provides the necessary syntactic sugar to manipulate unicode strings naturally.

Text values

Declaring a Rope is as simple as

let foo = r"Some Unicode text (Latin-1 encoded)"
let bar = ur"Some Unicode text (UTF-8 encoded)"

This defines a new value foo, with type Rope.t, the type of (immutable) Unicode ropes. Of course, this manipulation doesn't have to happen at the highest-level:

let append_string_to_rope x = Rope.append x (r"Some more Unicode text")

Note that ropes, being immutable, are automatically optimized, i.e. this is equivalent to

let some_unique_name = Rope.of_latin1 "Some more Unicode text"
let append_string_to_rope x = Rope.append x some_unique_name
It is possible to use the same syntax to define
Mutable UTF-8 strings
e.g. u"Some UTF-8 string", with type UTF8.t
Immutable Latin 1 strings (with capabilities)
e.g. ro"Some read-only string", with type [`Read] String.Cap.t
Mutable Latin 1 strings (with capabilities)
e.g. rw"Some read-write string", with type [`Read | `Write] String.Cap.t
Write-only Latin 1 strings (with capabilities)
e.g. wo"Some write-only string", with type [`Write] String.Cap.t
Again, immutable latin-1 strings are automatically optimized.

Text patterns

The same syntax may be used to pattern-match against ropes, UTF-8 strings and string with capabilities.

Boilerplate construction

(deactivated by default, implemented as findlib package sexplib.syntax)

Serialization to S-Expressions

(feature provided by Sexplib)
S-Expressions are a human-readable format for the transmission and storage of data, comparable to JSON.
A new construction with sexp is added to type definitions. This construction may be used to automatically (or semi-automatically, if need arises) generate functions to transform values into S-Expressions.
Let us consider the following extract:
TYPE_CONV_PATH "Current.Module.Name" (*Required*)

type color = Spade
           | Heart
           | Club
           | Diamond        with sexp

type value = Number of int
           | Ace
           | King
           | Queen
           | Jack           with sexp

type card = color * value   with sexp
This extract defines types color, value and card, as well as functions:
val sexp_of_color: color -> SExpr.t
val color_of_sexp: SExpr.t -> color

val sexp_of_value: value -> SExpr.t
val value_of_sexp: SExpr.t -> value

val sexp_of_card : card -> SExpr.t
val card_of_sexp : SExpr.t -> card

Module Languages.SExpr may be used to manipulate S-Expressions, to save them to files, load them from files, display them...
Most of the types known to Batteries Included are compatible with with sexp. If you encounter a type which is not compatible with this boilerplate generator, you may define manually functions sexp_of_foo and foo_of_sexp. These functions will integrate nicely with the rest of S-Expressions.
For more information, see the manual of Sexplib

Summary of new keywords