6.7 Expressions
expr |
::= |
value-path |
|
∣ |
constant |
|
∣ |
( expr ) |
|
∣ |
begin expr end |
|
∣ |
( expr : typexpr ) |
|
∣ |
expr , expr { , expr } |
|
∣ |
constr expr |
|
∣ |
`tag-name expr |
|
∣ |
expr :: expr |
|
∣ |
[ expr { ; expr } ] |
|
∣ |
[| expr { ; expr } |] |
|
∣ |
{ field = expr { ; field = expr } } |
|
∣ |
{ expr with field = expr { ; field = expr } } |
|
∣ |
expr { argument }+ |
|
∣ |
prefix-symbol expr |
|
∣ |
expr infix-op expr |
|
∣ |
expr . field |
|
∣ |
expr . field <- expr |
|
∣ |
expr .( expr ) |
|
∣ |
expr .( expr ) <- expr |
|
∣ |
expr .[ expr ] |
|
∣ |
expr .[ expr ] <- expr |
|
∣ |
if expr then expr [ else expr ] |
|
∣ |
while expr do expr done |
|
∣ |
for ident = expr ( to ∣ downto ) expr do expr done |
|
∣ |
expr ; expr |
|
∣ |
match expr with pattern-matching |
|
∣ |
function pattern-matching |
|
∣ |
fun multiple-matching |
|
∣ |
try expr with pattern-matching |
|
∣ |
let [rec] let-binding { and let-binding } in expr |
|
∣ |
new class-path |
|
∣ |
object [( pattern [: typexpr] )] { class-field } end |
|
∣ |
expr # method-name |
|
∣ |
inst-var-name |
|
∣ |
inst-var-name <- expr |
|
∣ |
( expr :> typexpr ) |
|
∣ |
( expr : typexpr :> typexpr ) |
|
∣ |
{< inst-var-name = expr { ; inst-var-name = expr } >} |
|
∣ |
assert expr |
|
∣ |
lazy expr |
argument |
::= |
expr |
|
∣ |
~ label-name |
|
∣ |
~ label-name : expr |
|
∣ |
? label-name |
|
∣ |
? label-name : expr |
pattern-matching |
::= |
[ | ] pattern [when expr] -> expr
{ | pattern [when expr] -> expr } |
multiple-matching |
::= |
{ parameter }+ [when expr] -> expr |
let-binding |
::= |
pattern = expr |
|
∣ |
value-name { parameter } [: typexpr] = expr |
parameter |
::= |
pattern |
|
∣ |
~ label-name |
|
∣ |
~ ( label-name [: typexpr] ) |
|
∣ |
~ label-name : pattern |
|
∣ |
? label-name |
|
∣ |
? ( label-name [: typexpr] [= expr] ) |
|
∣ |
? label-name : pattern |
|
∣ |
? label-name : ( pattern [: typexpr] [= expr] ) |
The table below shows the relative precedences and associativity of
operators and non-closed constructions. The constructions with higher
precedence come first. For infix and prefix symbols, we write
“*...” to mean “any symbol starting with *”.
Construction or operator |
Associativity |
prefix-symbol |
– |
. .( .[ |
– |
function application, constructor application, assert, lazy |
left |
- -. (prefix) |
– |
**... lsl lsr asr |
right |
*... /... %... mod land lor lxor |
left |
+... -... |
left |
:: |
right |
@... ^... |
right |
comparisons (= == < etc.), all other infix symbols |
left |
& && |
left |
or || |
left |
, |
– |
<- := |
right |
if |
– |
; |
right |
let match fun function try |
– |
6.7.1 Basic expressions
Constants
Expressions consisting in a constant evaluate to this constant.
Value paths
Expressions consisting in an access path evaluate to the value bound to
this path in the current evaluation environment. The path can
be either a value name or an access path to a value component of a module.
Parenthesized expressions
The expressions ( expr ) and begin expr end have the same
value as expr. Both constructs are semantically equivalent, but it
is good style to use begin … end inside control structures:
if ... then begin ... ; ... end else begin ... ; ... end
and ( … ) for the other grouping situations.
Parenthesized expressions can contain a type constraint, as in (
expr : type ). This constraint forces the type of expr to be
compatible with type.
Parenthesized expressions can also contain coercions ( expr [: type] :> type) (see subsection 6.7.5 below).
Function application
Function application is denoted by juxtaposition of (possibly labeled)
expressions. The expression expr argument1 … argumentn
evaluates the expression expr and those appearing in argument1
to argumentn. The expression expr must evaluate to a
functional value f, which is then applied to the values of
argument1, …, argumentn.
The order in which the expressions expr, argument1, …,
argumentn are evaluated is not specified.
Arguments and parameters are matched according to their respective
labels. Argument order is irrelevant, except among arguments with the
same label, or no label.
If a parameter is specified as optional (label prefixed by ?) in the
type of expr, the corresponding argument will be automatically
wrapped with the constructor Some, except if the argument itself is
also prefixed by ?, in which case it is passed as is.
If a non-labeled argument is passed, and its corresponding parameter
is preceded by one or several optional parameters, then these
parameters are defaulted, i.e. the value None will be
passed for them.
All other missing parameters (without corresponding argument), both
optional and non-optional, will be kept, and the result of the
function will still be a function of these missing parameters to the
body of f.
As a special case, if the function has a known arity, all the
arguments are unlabeled, and their number matches the number of
non-optional parameters, then labels are ignored and non-optional
parameters are matched in their definition order. Optional arguments
are defaulted.
In all cases but exact match of order and labels, without optional
parameters, the function type should be known at the application
point. This can be ensured by adding a type constraint. Principality
of the derivation can be checked in the -principal mode.
Function definition
Two syntactic forms are provided to define functions. The first form
is introduced by the keyword function:
function |
pattern1 |
-> |
expr1 |
| |
… |
| |
patternn |
-> |
exprn |
This expression evaluates to a functional value with one argument.
When this function is applied to a value v, this value is
matched against each pattern pattern1 to patternn.
If one of these matchings succeeds, that is, if the value v
matches the pattern patterni for some i,
then the expression expri associated to the selected pattern
is evaluated, and its value becomes the value of the function
application. The evaluation of expri takes place in an
environment enriched by the bindings performed during the matching.
If several patterns match the argument v, the one that occurs
first in the function definition is selected. If none of the patterns
matches the argument, the exception Match_failure is raised.
The other form of function definition is introduced by the keyword fun:
fun parameter1 … parametern -> expr
This expression is equivalent to:
fun parameter1 -> … fun parametern -> expr
Functions of the form fun optlabel ( pattern = expr0 ) ->
expr are equivalent to
fun optlabel x ->
let pattern =
match x with Some x -> x | None -> expr0
in expr |
where x is a fresh variable. When expr0 will be evaluated is left
unspecified.
After these two transformations, expressions are of the form
fun [label1] pattern1 -> … fun [labeln] patternn -> expr
If we ignore labels, which will only be meaningful at function
application, this is equivalent to
function pattern1 -> … function patternn -> expr
That is, the fun expression above evaluates to a curried function
with n arguments: after applying this function n times to the
values v1 ... vm, the values will be matched
in parallel against the patterns pattern1 … patternn.
If the matching succeeds, the function returns the value of expr in
an environment enriched by the bindings performed during the matchings.
If the matching fails, the exception Match_failure is raised.
Guards in pattern-matchings
Cases of a pattern matching (in the function, fun, match and
try constructs) can include guard expressions, which are
arbitrary boolean expressions that must evaluate to true for the
match case to be selected. Guards occur just before the -> token and
are introduced by the when keyword:
function |
pattern1 [when cond1] |
-> |
expr1 |
| |
… |
| |
patternn [when condn] |
-> |
exprn |
Matching proceeds as described before, except that if the value
matches some pattern patterni which has a guard condi, then the
expression condi is evaluated (in an environment enriched by the
bindings performed during matching). If condi evaluates to true,
then expri is evaluated and its value returned as the result of the
matching, as usual. But if condi evaluates to false, the matching
is resumed against the patterns following patterni.
Local definitions
The let and let rec constructs bind value names locally.
The construct
let pattern1 = expr1 and … and patternn = exprn in expr
evaluates expr1 … exprn in some unspecified order, then matches
their values against the patterns pattern1 … patternn. If the
matchings succeed, expr is evaluated in the environment enriched by
the bindings performed during matching, and the value of expr is
returned as the value of the whole let expression. If one of the
matchings fails, the exception Match_failure is raised.
An alternate syntax is provided to bind variables to functional
values: instead of writing
let ident = fun parameter1 … parameterm -> expr
in a let expression, one may instead write
let ident parameter1 … parameterm = expr
Recursive definitions of names are introduced by let rec:
let rec pattern1 = expr1 and … and patternn = exprn
in expr
The only difference with the let construct described above is
that the bindings of names to values performed by the
pattern-matching are considered already performed when the expressions
expr1 to exprn are evaluated. That is, the expressions expr1
to exprn can reference identifiers that are bound by one of the
patterns pattern1, …, patternn, and expect them to have the
same value as in expr, the body of the let rec construct.
The recursive definition is guaranteed to behave as described above if
the expressions expr1 to exprn are function definitions
(fun … or function …), and the patterns pattern1
… patternn are just value names, as in:
let rec name1 = fun …
and …
and namen = fun …
in expr
This defines name1 … namen as mutually recursive functions
local to expr.
The behavior of other forms of let rec definitions is
implementation-dependent. The current implementation also supports
a certain class of recursive definitions of non-functional values,
as explained in section 7.3.
6.7.2 Control structures
Sequence
The expression expr1 ; expr2 evaluates expr1 first, then
expr2, and returns the value of expr2.
Conditional
The expression if expr1 then expr2 else expr3 evaluates to
the value of expr2 if expr1 evaluates to the boolean true,
and to the value of expr3 if expr1 evaluates to the boolean
false.
The else expr3 part can be omitted, in which case it defaults to
else ().
Case expression
The expression
match |
expr |
with |
pattern1 |
-> |
expr1 |
| |
… |
| |
patternn |
-> |
exprn |
matches the value of expr against the patterns pattern1 to
patternn. If the matching against patterni succeeds, the
associated expression expri is evaluated, and its value becomes the
value of the whole match expression. The evaluation of
expri takes place in an environment enriched by the bindings
performed during matching. If several patterns match the value of
expr, the one that occurs first in the match expression is
selected. If none of the patterns match the value of expr, the
exception Match_failure is raised.
Boolean operators
The expression expr1 && expr2 evaluates to true if both
expr1 and expr2 evaluate to true; otherwise, it evaluates to
false. The first component, expr1, is evaluated first. The
second component, expr2, is not evaluated if the first component
evaluates to false. Hence, the expression expr1 && expr2 behaves
exactly as
if expr1 then expr2 else false.
The expression expr1 || expr2 evaluates to true if one of
expr1 and expr2 evaluates to true; otherwise, it evaluates to
false. The first component, expr1, is evaluated first. The
second component, expr2, is not evaluated if the first component
evaluates to true. Hence, the expression expr1 || expr2 behaves
exactly as
if expr1 then true else expr2.
The boolean operator & is synonymous for &&. The boolean operator
or is synonymous for ||.
Loops
The expression while expr1 do expr2 done repeatedly
evaluates expr2 while expr1 evaluates to true. The loop
condition expr1 is evaluated and tested at the beginning of each
iteration. The whole while … done expression evaluates to
the unit value ().
The expression for name = expr1 to expr2 do expr3 done
first evaluates the expressions expr1 and expr2 (the boundaries)
into integer values n and p. Then, the loop body expr3 is
repeatedly evaluated in an environment where name is successively
bound to the values
n, n+1, ..., p−1, p.
The loop body is never evaluated if n > p.
The expression for name = expr1 downto expr2 do expr3 done
evaluates similarly, except that name is successively bound to the values
n, n−1, ..., p+1, p.
The loop body is never evaluated if n < p.
In both cases, the whole for expression evaluates to the unit
value ().
Exception handling
The expression
try |
expr |
with |
pattern1 |
-> |
expr1 |
| |
… |
| |
patternn |
-> |
exprn |
evaluates the expression expr and returns its value if the
evaluation of expr does not raise any exception. If the evaluation
of expr raises an exception, the exception value is matched against
the patterns pattern1 to patternn. If the matching against
patterni succeeds, the associated expression expri is evaluated,
and its value becomes the value of the whole try expression. The
evaluation of expri takes place in an environment enriched by the
bindings performed during matching. If several patterns match the value of
expr, the one that occurs first in the try expression is
selected. If none of the patterns matches the value of expr, the
exception value is raised again, thereby transparently “passing
through” the try construct.
6.7.3 Operations on data structures
Products
The expression expr1 , … , exprn evaluates to the
n-tuple of the values of expressions expr1 to exprn. The
evaluation order for the subexpressions is not specified.
Variants
The expression constr expr evaluates to the variant value whose
constructor is constr, and whose argument is the value of expr.
For lists, some syntactic sugar is provided. The expression
expr1 :: expr2 stands for the constructor ( :: )
applied to the argument ( expr1 , expr2 ), and therefore
evaluates to the list whose head is the value of expr1 and whose tail
is the value of expr2. The expression [ expr1 ; … ;
exprn ] is equivalent to expr1 :: … :: exprn ::
[], and therefore evaluates to the list whose elements are the
values of expr1 to exprn.
Polymorphic variants
The expression `tag-name expr evaluates to the variant value whose
tag is tag-name, and whose argument is the value of expr.
Records
The expression { field1 = expr1 ; … ; fieldn =
exprn } evaluates to the record value
{ field1 = v1 ; … ; fieldn = vn },
where vi is the value of expri for i = 1,… , n.
The fields field1 to fieldn must all belong to the same record
types; all fields belonging to this record type must appear exactly
once in the record expression, though they can appear in any
order. The order in which expr1 to exprn are evaluated is not
specified.
The expression
{ expr with field1 = expr1 ; … ; fieldn = exprn }
builds a fresh record with fields field1 … fieldn equal to
expr1 … exprn, and all other fields having the same value as
in the record expr. In other terms, it returns a shallow copy of
the record expr, except for the fields field1 … fieldn,
which are initialized to expr1 … exprn.
The expression expr1 . field evaluates expr1 to a record
value, and returns the value associated to field in this record
value.
The expression expr1 . field <- expr2 evaluates expr1 to a record
value, which is then modified in-place by replacing the value
associated to field in this record by the value of
expr2. This operation is permitted only if field has been
declared mutable in the definition of the record type. The whole
expression expr1 . field <- expr2 evaluates to the unit value
().
Arrays
The expression [| expr1 ; … ; exprn |] evaluates to
a n-element array, whose elements are initialized with the values of
expr1 to exprn respectively. The order in which these
expressions are evaluated is unspecified.
The expression expr1 .( expr2 ) returns the value of element
number expr2 in the array denoted by expr1. The first element
has number 0; the last element has number n−1, where n is the
size of the array. The exception Invalid_argument is raised if the
access is out of bounds.
The expression expr1 .( expr2 ) <- expr3 modifies in-place
the array denoted by expr1, replacing element number expr2 by
the value of expr3. The exception Invalid_argument is raised if
the access is out of bounds. The value of the whole expression is ().
Strings
The expression expr1 .[ expr2 ] returns the value of character
number expr2 in the string denoted by expr1. The first character
has number 0; the last character has number n−1, where n is the
length of the string. The exception Invalid_argument is raised if the
access is out of bounds.
The expression expr1 .[ expr2 ] <- expr3 modifies in-place
the string denoted by expr1, replacing character number expr2 by
the value of expr3. The exception Invalid_argument is raised if
the access is out of bounds. The value of the whole expression is ().
Symbols from the class infix-symbols, as well as the keywords
*, =, or and &, can appear in infix position (between two
expressions). Symbols from the class prefix-symbols
can appear in prefix position (in front of an expression).
Infix and prefix symbols do not have a fixed meaning: they are simply
interpreted as applications of functions bound to the names
corresponding to the symbols. The expression prefix-symbol expr is
interpreted as the application ( prefix-symbol )
expr. Similarly, the expression expr1 infix-symbol expr2 is
interpreted as the application ( infix-symbol ) expr1 expr2.
The table below lists the symbols defined in the initial environment
and their initial meaning. (See the description of the standard
library module Pervasive in chapter 20 for more
details). Their meaning may be changed at any time using
let ( infix-op ) name1 name2 = …
Operator |
Initial meaning |
+ |
Integer addition. |
- (infix) |
Integer subtraction. |
- (prefix) |
Integer negation. |
* |
Integer multiplication. |
/ |
Integer division.
Raise Division_by_zero if second argument is zero. |
mod |
Integer modulus. Raise
Division_by_zero if second argument is zero. |
land |
Bitwise logical “and” on integers. |
lor |
Bitwise logical “or” on integers. |
lxor |
Bitwise logical “exclusive or” on integers. |
lsl |
Bitwise logical shift left on integers. |
lsr |
Bitwise logical shift right on integers. |
asr |
Bitwise arithmetic shift right on integers. |
+. |
Floating-point addition. |
-. (infix) |
Floating-point subtraction. |
-. (prefix) |
Floating-point negation. |
*. |
Floating-point multiplication. |
/. |
Floating-point division. |
** |
Floating-point exponentiation. |
@ |
List concatenation. |
^ |
String concatenation. |
! |
Dereferencing (return the current
contents of a reference). |
:= |
Reference assignment (update the
reference given as first argument with the value of the second
argument). |
= |
Structural equality test. |
<> |
Structural inequality test. |
== |
Physical equality test. |
!= |
Physical inequality test. |
< |
Test “less than”. |
<= |
Test “less than or equal”. |
> |
Test “greater than”. |
>= |
Test “greater than or equal”. |
Object creation
When class-path evaluates to a class body, new class-path
evaluates to an object containing the instance variables and
methods of this class.
When class-path evaluates to a class function, new class-path
evaluates to a function expecting the same number of arguments and
returning a new object of this class.
Immediate object creation
Creating directly an object through the object class-body end
construct is operationally equivalent to defining locally a class
myclass = object class-body end —see sections
6.9.2 and following for the syntax of class-body—
and immediately creating a single object from it by new myclass.
The typing of immediate objects is slightly different from explicitely
defining a class in two respects. First, the inferred object type may
contain free type variables. Second, since the class body of an
immediate object will never be extended, its self type can be unified
with a closed object type.
Message sending
The expression expr # method-name invokes the method
method-name of the object denoted by expr.
If method-name is a polymorphic method, its type should be known at
the invocation site. This is true for instance if expr is the name
of a fresh object (let ident = new class-path ... ) or if
there is a type constraint. Principality of the derivation can be
checked in the -principal mode.
Accessing and modifying instance variables
The instance variables of a class are visible only in the body of the
methods defined in the same class or a class that inherits from the
class defining the instance variables. The expression inst-var-name
evaluates to the value of the given instance variable. The expression
inst-var-name <- expr assigns the value of expr to the instance
variable inst-var-name, which must be mutable. The whole expression
inst-var-name <- expr evaluates to ().
Coercion
The type of an object can be coerced (weakened) to a supertype.
The expression ( expr :> typexpr ) coerces the expression expr
to type typexpr.
The expression ( expr : typexpr1 :> typexpr2 ) coerces the
expression expr from type typexpr1 to type typexpr2.
The former operator will sometimes fail to coerce an expression expr
from a type t1 to a type t2 even if type t1 is a subtype of type
t2: in the current implementation it only expands two levels of
type abbreviations containing objects and/or variants, keeping only
recursion when it is explicit in the class type. In case of failure,
the latter operator should be used.
In a class definition, coercion to the type this class defines is the
identity, as this type abbreviation is not yet completely defined.
Object duplication
An object can be duplicated using the library function Oo.copy
(see
Module Oo). Inside a method, the expression
{< inst-var-name = expr { ; inst-var-name = expr } >}
returns a copy of self with the given instance variables replaced by
the values of the associated expressions; other instance variables
have the same value in the returned object as in self.