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
The syntax for streams and stream parsers is no longer part of the
Objective Caml language, but available through a Camlp4 syntax
extension. See the Camlp4 reference manual for more information.
Support for basic operations on streams is still available through the
Stream[Stream] module of the standard library.
Objective Caml programs
that use the stream parser syntax should 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:
-
e has no free occurrence of any of name1 … namen
- e is a variable
- e has the form fun … -> …
- e has the form function … -> …
- e has the form lazy ( … )
- e has one of the following forms, where each one of
expr1 … exprm is statically constructive with respect to
name1 … namen, and expr0 is statically constructive with
respect to name1 … namen, xname1 … xnamem:
-
let [rec] xname1 = expr1 and …
and xnamem = exprm in expr0
- let module … in expr1
- constr ( expr1, … , exprm)
- `tag-name ( expr1, … , exprm)
- [| expr1; … ; exprm |]
- { field1 = expr1; … ; fieldm = exprm }
- { expr1 with field2 = expr2; … ;
fieldm = exprm } where expr1 is not immediately
linked to name1 … namen
- ( expr1, … , exprm )
- expr1; … ; exprm
An expression e is said to be immediately linked to the variable
name in the following cases:
-
e is name
- e has the form expr1; … ; exprm where exprm
is immediately linked to name
- e has the form let [rec] xname1 = expr1 and …
and xnamem = exprm in expr0 where expr0 is immediately
linked to name or to one of the xnamei such that expri
is immediately linked to name.
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
StringSet.elements
(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
end
= struct
type t = A | B of int
let a = A
let b n = assert (n > 0); B n
end
Here, the private declaration ensures that in any value of type
M.t, the argument to the B constructor is always a positive integer.
With respect to the variance of their parameters, private types are
handled like abstract types. That is, if a private type has
parameters, their variance is the one explicitly given by prefixing
the parameter by a `+' or a `-', it is invariant otherwise.
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 rec A : sig
type t = Leaf of string | Node of ASet.t
val compare: t -> t -> int
end
= struct
type t = Leaf of string | Node of ASet.t
let compare t1 t2 =
match (t1, t2) with
(Leaf s1, Leaf s2) -> Pervasives.compare s1 s2
| (Leaf _, Node _) -> 1
| (Node _, Leaf _) -> -1
| (Node n1, Node n2) -> ASet.compare n1 n2
end
and ASet : Set.S with type elt = A.t
= Set.Make(A)
It can be given the following specification:
module rec A : sig
type t = Leaf of string | Node of ASet.t
val compare: t -> t -> int
end
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.
7.10 Private row types
type-equation |
::= |
... |
|
∣ |
= private typexpr |
Private row types are type abbreviations where part of the
structure of the type is left abstract. Concretely typexpr in the
above should denote either an object type or a polymorphic variant
type, with some possibility of refinement left. If the private
declaration is used in an interface, the corresponding implementation
may either provide a ground instance, or a refined private type.
module M : sig type c = private < x : int; .. > val o : c end =
struct
class c = object method x = 3 method y = 2 end
let o = new c
end
This declaration does more than hiding the y method, it also makes
the type c incompatible with any other closed object type, meaning
that only o will be of type c. In that respect it behaves
similarly to private record types. But private row types are
more flexible with respect to incremental refinement. This feature can
be used in combination with functors.
module F(X : sig type c = private < x : int; .. > end) =
struct
let get_x (o : X.c) = o#x
end
module G(X : sig type c = private < x : int; y : int; .. > end) =
struct
include F(X)
let get_y (o : X.c) = o#y
end
Polymorphic variant types can be refined in two ways, either to allow
the addition of new constructors, or to allow the disparition of
declared constructors. The second case corresponds to private variant
types (one cannot create a value of the private type), while the first
case requires default cases in pattern-matching to handle addition.
type t = [ `A of int | `B of bool ]
type u = private [< t > `A ]
type v = private [> t ]
With type u, it is possible to create values of the form (`A n),
but not (`B b). With type v, construction is not restricted but
pattern-matching must have a default case.
Like for abstract and private types, the variance of type parameters
is not infered, and must be given explicitly.