Previous Up Next
Chapter 7 Language extensions

This chapter describes language extensions and convenience features that are implemented in Objective Caml, but not described in the Objective Caml reference manual.

7.1 Integer literals for types int32, int64 and nativeint

int32 -literal ::= integer-literal l
int64 -literal ::= integer-literal L
nativeint-literal ::= integer-literal n
An integer literal can be followed by one of the letters l, L or n to indicate that this integer has type int32, int64 or nativeint respectively, instead of the default type int for integer literals. The library modules Int32[Int32], Int64[Int64] and Nativeint[Nativeint] provide operations on these integer types.

7.2 Streams and stream parsers

Streams and stream parsers are no longer part of the Objective Caml language, but available through a Camlp4 syntax extension. See the Camlp4 reference manual for more information. Objective Caml programs that use streams and stream parsers can be compiled with the -pp camlp4o option to ocamlc and ocamlopt. For interactive use, run ocaml and issue the #load "camlp4o.cma";; command.

7.3 Recursive definitions of values

As mentioned in section 6.7.1, the let rec binding construct, in addition to the definition of recursive functions, also supports a certain class of recursive definitions of non-functional values, such as
let rec name1 = 1 ::  name2 and  name2 = 2 ::  name1 in  expr
which binds name1 to the cyclic list 1::2::1::2::..., and name2 to the cyclic list 2::1::2::1::...Informally, the class of accepted definitions consists of those definitions where the defined names occur only inside function bodies or as argument to a data constructor.

More precisely, consider the expression:
let rec name1 =  expr1 and ... and  namen =  exprn in  expr
It will be accepted if each one of expr1 ...  exprn is statically constructive with respect to name1 ...  namen and not immediately linked to any of name1 ...  namen

An expression e is said to be statically constructive with respect to the variables name1 ...  namen if at least one of the following conditions is true: An expression e is said to be immediately linked to the variable name in the following cases:
7.4 Range patterns

In patterns, Objective Caml recognizes the form ' c ' .. '  d ' (two character literals separated by ..) as shorthand for the pattern
' c ' | '  c1 ' | '  c2 ' | ... | '  cn ' | '  d '
where c1, c2, ..., cn are the characters that occur between c and d in the ASCII character set. For instance, the pattern '0'..'9' matches all characters that are digits.

7.5 Assertion checking

Objective Caml supports the assert construct to check debugging assertions. The expression assert expr evaluates the expression expr and returns () if expr evaluates to true. Otherwise, the exception Assert_failure is raised with the source file name and the location of expr as arguments. Assertion checking can be turned off with the -noassert compiler option.

As a special case, assert false is reduced to raise (Assert_failure ...), which is polymorphic (and is not turned off by the -noassert option).

7.6 Lazy evaluation

The expression lazy expr returns a value v of type Lazy.t that encapsulates the computation of expr. The argument expr is not evaluated at this point in the program. Instead, its evaluation will be performed the first time Lazy.force is applied to the value v, returning the actual value of expr. Subsequent applications of Lazy.force to v do not evaluate expr again. For more information, see the description of module Lazy in the standard library (see Module Lazy).

7.7 Local modules

The expression let module module-name =  module-expr in  expr locally binds the module expression module-expr to the identifier module-name during the evaluation of the expression expr. It then returns the value of expr. For example:
        let remove_duplicates comparison_fun string_list =
          let module StringSet =
            Set.Make(struct type t = string
                            let compare = comparison_fun end) in
            (List.fold_right StringSet.add string_list StringSet.empty)
7.8 Private types

type-representation ::= ...
  | = private constr-decl  { | constr-decl }
  | = private { field-decl  { ; field-decl } }
Private types are variant or record types. Values of these types can be de-structured normally in pattern-matching or via the expr .  field notation for record accesses. However, values of these types cannot be constructed directly by constructor application or record construction. Moreover, assignment on a mutable field of a private record type is not allowed.

The typical use of private types is in the export signature of a module, to ensure that construction of values of the private type always go through the functions provided by the module, while still allowing pattern-matching outside the defining module. For example:
        module M : sig
                     type t = private A | B of int
                     val a : t
                     val b : int -> t
                 = struct
                     type t = A | B of int
                     let a = A
                     let b n = assert (n > 0); B n
Here, the private declaration ensures that in any value of type M.t, the argument to the B constructor is always a positive integer.

7.9 Recursive modules

definition ::= ...
  | module rec module-name :  module-type =  module-expr  { and module-name:  module-type =  module-expr }
specification ::= ...
  | module rec module-name :  module-type  { and module-name:  module-type }

Recursive module definitions, introduced by the 'module rec' ...'and' ... construction, generalize regular module definitions module module-name =  module-expr and module specifications module module-name :  module-type by allowing the defining module-expr and the module-type to refer recursively to the module identifiers being defined. A typical example of a recursive module definition is:
        module A : sig
                     type t = Leaf of string | Node of ASet.t
                     val compare: t -> t -> int
                 = struct
                     type t = Leaf of string | Node of ASet.t
                     let compare t1 t2 =
                       match (t1, t2) with
                         (Leaf s1, Leaf s2) -> s1 s2
                       | (Leaf _, Node _) -> 1
                       | (Node _, Leaf _) -> -1
                       | (Node n1, Node n2) -> n1 n2
        and ASet : Set.S with type elt = A.t
                 = Set.Make(A)
It can be given the following specification:
        module A : sig
                     type t = Leaf of string | Node of ASet.t
                     val compare: t -> t -> int
        and ASet : Set.S with type elt = A.t
This is an experimental extension of Objective Caml: the class of recursive definitions accepted, as well as its dynamic semantics are not final and subject to change in future releases.

Currently, the compiler requires that all dependency cycles between the recursively-defined module identifiers go through at least one ``safe'' module. A module is ``safe'' if all value definitions that it contains have function types ty1 ->  ty2. Evaluation of a recursive module definition proceeds by building initial values for the safe modules involved, binding all (functional) values to fun x -> raise Undefined_recursive_module. The defining module expressions are then evaluated, and the initial values for the safe modules are replaced by the values thus computed. If a function component of a safe module is applied during this computation (which corresponds to an ill-founded recursive definition), the Undefined_recursive_module exception is raised.

Previous Up Next