Movatterモバイル変換


[0]ホーム

URL:


Chapter 11 The OCaml language

7 Expressions

expr::=value-path
 constant
 (expr)
 beginexprend
 (expr:typexpr)
 expr { ,expr }+
 constrexpr
 `tag-nameexpr
 expr::expr
 [expr { ;expr } [;] ]
 [|expr { ;expr } [;] |]
 {field [:typexpr] [=expr]{ ;field [:typexpr] [=expr] } [;] }
 {exprwithfield [:typexpr] [=expr]{ ;field [:typexpr] [=expr] } [;] }
 expr { argument }+
 prefix-symbolexpr
 -expr
 -.expr
 exprinfix-opexpr
 expr.field
 expr.field<-expr
 expr.(expr)
 expr.(expr)<-expr
 expr.[expr]
 expr.[expr]<-expr
 ifexprthenexpr [ elseexpr ]
 whileexprdoexprdone
 forvalue-name=expr ( to ∣ downto ) exprdoexprdone
 expr;expr
 matchexprwithpattern-matching
 functionpattern-matching
 fun { parameter }+ [ :typexpr ] ->expr
 tryexprwithpattern-matching
 let [rec] let-binding { andlet-binding } inexpr
 letexceptionconstr-declinexpr
 letmodulemodule-name { (module-name:module-type) }[ :module-type ]  =module-exprinexpr
 (expr:>typexpr)
 (expr:typexpr:>typexpr)
 assertexpr
 lazyexpr
 local-open
 object-expr
 
argument::=expr
 ~label-name
 ~label-name:expr
 ?label-name
 ?label-name:expr
 
pattern-matching::=[ | ] pattern [whenexpr] ->expr{ |pattern [whenexpr] ->expr }
 
let-binding::=pattern=expr
 value-name { parameter } [:typexpr] [:>typexpr] =expr
 value-name:poly-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] )
 
local-open::= 
 letopenmodule-pathinexpr
 module-path.(expr)
 module-path.[expr]
 module-path.[|expr|]
 module-path.{expr}
 module-path.{<expr>}
 
object-expr::= 
 newclass-path
 objectclass-bodyend
 expr#method-name
 inst-var-name
 inst-var-name<-expr
 {< [ inst-var-name [=expr] { ;inst-var-name [=expr] } [;] ] >}

See also the following language extensions:first-class modules,overriding in open statements,syntax for Bigarray access,attributes,extension nodes andextended indexing operators.

7.1 Precedence and associativity

The table below shows the relative precedences and associativity ofoperators and non-closed constructions. The constructions with higherprecedence come first. For infix and prefix symbols, we write“*…” to mean “any symbol starting with*”.

Construction or operatorAssociativity
prefix-symbol
. .( .[ .{ (see section 12.11)
#left
function application, constructor application, tagapplication,assert,lazyleft
- -. (prefix)
** lsl lsr asrright
* / % mod land lor lxorleft
+ -left
::right
@ ^right
= < > | & $ !=left
& &&right
or ||right
,
<- :=right
if
;right
let match fun function try

It is simple to test or refresh one’s understanding:

# 3 + 3mod 2, 3 + (3mod 2), (3 + 3)mod 2;;
- : int * int * int = (4, 4, 0)

7.2 Basic expressions

Constants

An expression consisting in a constant evaluates to this constant. For example,3.14 or[||].

Value paths

An expression consisting in an access path evaluates to the value bound tothis path in the current evaluation environment. The path canbe either a value name or an access path to a value component of a module.

# Float.ArrayLabels.to_list;;
- : Float.ArrayLabels.t -> float list = <fun>

Parenthesized expressions

The expressions(expr) andbeginexprend have the samevalue asexpr. The two constructs are semantically equivalent, but itis good style to usebeginend inside control structures:

        if … then begin … ; … end else begin … ; … end

and() for the other grouping situations.

#let x = 1 + 2 * 3let y = (1 + 2) * 3;;
val x : int = 7val y : int = 9
#let f a b =if a = bthen print_endline"Equal"elsebegin print_string"Not Equal: "; print_int a; print_string" and "; print_int b; print_newline ()end;;
val f : int -> int -> unit = <fun>

Parenthesized expressions can contain a type constraint, as in(expr:typexpr). This constraint forces the type ofexpr to becompatible withtypexpr.

Parenthesized expressions can also contain coercions(expr [:typexpr]:>typexpr) (seesubsection 11.7.7 below).

Function application

Function application is denoted by juxtaposition of (possibly labeled)expressions. The expressionexprargument1argumentnevaluates the expressionexpr and those appearing inargument1toargumentn. The expressionexpr must evaluate to afunctional valuef, which is then applied to the values ofargument1, …,argumentn.

The order in which the expressionsexpr,argument1, …,argumentn are evaluated is not specified.

# List.fold_left ( + ) 0 [1; 2; 3; 4; 5];;
- : int = 15

Arguments and parameters are matched according to their respectivelabels. Argument order is irrelevant, except among arguments with thesame label, or no label.

# ListLabels.fold_left ~f:( @ ) ~init:[] [[1; 2; 3]; [4; 5; 6]; [7; 8; 9]];;
- : int list = [1; 2; 3; 4; 5; 6; 7; 8; 9]

If a parameter is specified as optional (label prefixed by?) in thetype ofexpr, the corresponding argument will be automaticallywrapped with the constructorSome, except if the argument itself isalso prefixed by?, in which case it is passed as is.

#let fullname ?title first second =match titlewith | Some t -> t ^" " ^ first ^" " ^ second | None -> first ^" " ^ secondlet name = fullname ~title:"Mrs""Jane""Fisher"let address ?title first second town = fullname ?title first second ^"\n" ^ town;;
val fullname : ?title:string -> string -> string -> string = <fun>val name : string ="Mrs Jane Fisher"val address : ?title:string -> string -> string -> string -> string = <fun>

If a non-labeled argument is passed, and its corresponding parameteris preceded by one or several optional parameters, then theseparameters aredefaulted,i.e. the valueNone will bepassed for them.All other missing parameters (without corresponding argument), bothoptional and non-optional, will be kept, and the result of thefunction will still be a function of these missing parameters to thebody off.

#let fullname ?title first second =match titlewith | Some t -> t ^" " ^ first ^" " ^ second | None -> first ^" " ^ secondlet name = fullname"Jane""Fisher";;
val fullname : ?title:string -> string -> string -> string = <fun>val name : string ="Jane Fisher"

In all cases but exact match of order and labels, without optionalparameters, the function type should be known at the applicationpoint. This can be ensured by adding a type constraint. Principalityof the derivation can be checked in the-principal mode.

As a special case, OCaml supportslabels-omitted full applications:if the function has a known arity, all the arguments are unlabeled,and their number matches the number of non-optional parameters, thenlabels are ignored and non-optional parameters are matched in theirdefinition order. Optional arguments are defaulted. This omission oflabels is discouraged and results in a warning, see13.5.1.

Function definition

Two syntactic forms are provided to define functions. The first formis introduced by the keywordfunction:

functionpattern1->expr1
|… 
|patternn->exprn

This expression evaluates to a functional value with one argument.When this function is applied to a valuev, this value ismatched against each patternpattern1 topatternn.If one of these matchings succeeds, that is, if the valuevmatches the patternpatterni for somei,then the expressionexpri associated to the selected patternis evaluated, and its value becomes the value of the functionapplication. The evaluation ofexpri takes place in anenvironment enriched by the bindings performed during the matching.

If several patterns match the argumentv, the one that occursfirst in the function definition is selected. If none of the patternsmatches the argument, the exceptionMatch_failure is raised.

# (function (0, 0) ->"both zero" | (0, _) ->"first only zero" | (_, 0) ->"second only zero" | (_, _) ->"neither zero") (7, 0);;
- : string ="second only zero"

The other form of function definition is introduced by the keywordfun:

funparameter1parametern->expr

This expression is equivalent to:

funparameter1->funparametern->expr
#let f = (fun a ->fun b ->fun c -> a + b + c)let g = (fun a b c -> a + b + c);;
val f : int -> int -> int -> int = <fun>val g : int -> int -> int -> int = <fun>

An optional type constrainttypexpr can be added before-> to enforcethe type of the result to be compatible with the constrainttypexpr:

funparameter1parametern:typexpr->expr

is equivalent to

funparameter1->funparametern-> (expr:typexpr )

Beware of the small syntactic difference between a type constraint onthe last parameter

funparameter1 … (parametern:typexpr)->expr

and one on the result

funparameter1parametern:typexpr->expr
#let eq =fun (a : int) (b : int) -> a = blet eq2 =fun a b : bool -> a = blet eq3 =fun (a : int) (b : int) : bool -> a = b;;
val eq : int -> int -> bool = <fun>val eq2 : 'a -> 'a -> bool = <fun>val eq3 : int -> int -> bool = <fun>

The parameter patterns~lab and~(lab [:typ])are shorthands for respectively~lab:lab and~lab:(lab [:typ]), and similarly for their optionalcounterparts.

#let bool_map ~cmp:(cmp : int -> int -> bool) l = List.map cmp llet bool_map' ~(cmp : int -> int -> bool) l = List.map cmp l;;
val bool_map : cmp:(int -> int -> bool) -> int list -> (int -> bool) list = <fun>val bool_map' : cmp:(int -> int -> bool) -> int list -> (int -> bool) list = <fun>

A function of the formfun?lab:(pattern=expr0)->expr is equivalent to

fun?lab:ident->letpattern=matchidentwithSomeident->ident|None->expr0inexpr

whereidentis a fresh variable, except that it is unspecified whenexpr0 is evaluated.

#let open_file_for_input ?binary filename =match binarywith | Sometrue -> open_in_bin filename | Somefalse | None -> open_in filenamelet open_file_for_input' ?(binary=false) filename =if binarythen open_in_bin filenameelse open_in filename;;
val open_file_for_input : ?binary:bool -> string -> in_channel = <fun>val open_file_for_input' : ?binary:bool -> string -> in_channel = <fun>

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 functionapplication, this is equivalent to

functionpattern1->functionpatternn->expr

That is, thefun expression above evaluates to a curried functionwithn arguments: after applying this functionn times to thevaluesv1vn, the values will be matchedin parallel against the patternspattern1patternn.If the matching succeeds, the function returns the value ofexpr inan environment enriched by the bindings performed during the matchings.If the matching fails, the exceptionMatch_failure is raised.

Guards in pattern-matchings

The cases of a pattern matching (in thefunction,match andtry constructs) can include guard expressions, which arearbitrary boolean expressions that must evaluate totrue for thematch case to be selected. Guards occur just before the-> token andare introduced by thewhen keyword:

functionpattern1   [when   cond1]->expr1
|… 
|patternn    [when   condn]->exprn

Matching proceeds as described before, except that if the valuematches some patternpatterni which has a guardcondi, then theexpressioncondi is evaluated (in an environment enriched by thebindings performed during matching). Ifcondi evaluates totrue,thenexpri is evaluated and its value returned as the result of thematching, as usual. But ifcondi evaluates tofalse, the matchingis resumed against the patterns followingpatterni.

#letrec repeat f =function | 0 -> () | nwhen n > 0 -> f (); repeat f (n - 1) | _ -> raise (Invalid_argument"repeat");;
val repeat : (unit -> 'a) -> int -> unit = <fun>

Local definitions

Thelet andletrec constructs bind value names locally.The construct

letpattern1=expr1andandpatternn=exprninexpr

evaluatesexpr1exprn in some unspecified order and matchestheir values against the patternspattern1patternn. If thematchings succeed,expr is evaluated in the environment enriched bythe bindings performed during matching, and the value ofexpr isreturned as the value of the wholelet expression. If one of thematchings fails, the exceptionMatch_failure is raised.

#let v =let x = 1in [x; x; x]let v' =let a, b = (1, 2)in a + blet v'' =let a = 1and b = 2in a + b;;
val v : int list = [1; 1; 1]val v' : int = 3val v'' : int = 3

An alternate syntax is provided to bind variables to functionalvalues: instead of writing

letident=funparameter1parameterm->expr

in alet expression, one may instead write

letidentparameter1parameterm=expr
#let f =fun x ->fun y ->fun z -> x + y + zlet f' =fun x y z -> x + y + zlet f'' x y z = x + y + z;;
val f : int -> int -> int -> int = <fun>val f' : int -> int -> int -> int = <fun>val f'' : int -> int -> int -> int = <fun>

Recursive definitions of names are introduced byletrec:

letrecpattern1=expr1andandpatternn=exprninexpr

The only difference with thelet construct described above isthat the bindings of names to values performed by thepattern-matching are considered already performed when the expressionsexpr1 toexprn are evaluated. That is, the expressionsexpr1toexprn can reference identifiers that are bound by one of thepatternspattern1, …,patternn, and expect them to have thesame value as inexpr, the body of theletrec construct.

#letrec even =function 0 ->true | n -> odd (n - 1)and odd =function 0 ->false | n -> even (n - 1)in even 1000;;
- : bool =true

The recursive definition is guaranteed to behave as described above ifthe expressionsexpr1 toexprn are function definitions(fun … orfunction …), and the patternspattern1patternn are just value names, as in:

letrecname1=funandandnamen=funinexpr

This definesname1namen as mutually recursive functionslocal toexpr.

The behavior of other forms ofletrec definitions isimplementation-dependent. The current implementation also supportsa certain class of recursive definitions of non-functional values,as explained in section 12.1.

Local exceptions

(Introduced in OCaml 4.04)

It is possible to define local exceptions in expressions:letexceptionconstr-declinexpr .

#let map_empty_on_negative f l =letexception Negativeinlet aux x =if x < 0then raise Negativeelse f xintry List.map aux lwith Negative -> [];;
val map_empty_on_negative : (int -> 'a) -> int list -> 'a list = <fun>

The syntactic scope of the exception constructor is the innerexpression, but nothing prevents exception values created with thisconstructor from escaping this scope. Two executions of the definitionabove result in two incompatible exception constructors (as for anyexception definition). For instance:

#let gen () =letexception Ain Alet () =assert(gen () = gen ());;
Exception: Assert_failure ("expr.etex", 3, 9).

Explicit polymorphic type annotations

(Introduced in OCaml 3.12)

Polymorphic type annotations inlet-definitions behave in a waysimilar to polymorphic methods:

letpattern1:typ1typn.typexpr=expr

These annotations explicitly require the defined value to be polymorphic,and allow one to use this polymorphism in recursive occurrences(when usingletrec). Note however that this is a normal polymorphictype, unifiable with any instance of itself.

7.3 Control structures

Sequence

The expressionexpr1;expr2 evaluatesexpr1 first, thenexpr2, and returns the value ofexpr2.

#let print_pair (a, b) = print_string"("; print_string (string_of_int a); print_string","; print_string (string_of_int b); print_endline")";;
val print_pair : int * int -> unit = <fun>

Conditional

The expressionifexpr1thenexpr2elseexpr3 evaluates tothe value ofexpr2 ifexpr1 evaluates to the booleantrue,and to the value ofexpr3 ifexpr1 evaluates to the booleanfalse.

#letrec factorial x =if x <= 1then 1else x * factorial (x - 1);;
val factorial : int -> int = <fun>

Theelseexpr3 part can be omitted, in which case it defaults toelse().

#let debug =reffalselet log msg =if !debugthen prerr_endline msg;;
val debug : boolref = {contents =false}val log : string -> unit = <fun>

Case expression

The expression

matchexpr
withpattern1->expr1
|… 
|patternn->exprn

matches the value ofexpr against the patternspattern1 topatternn. If the matching againstpatterni succeeds, theassociated expressionexpri is evaluated, and its value becomes thevalue of the wholematch expression. The evaluation ofexpri takes place in an environment enriched by the bindingsperformed during matching. If several patterns match the value ofexpr, the one that occurs first in thematch expression isselected.

#letrec sum l =match lwith | [] -> 0 | h :: t -> h + sum t;;
val sum : int list -> int = <fun>

If none of the patterns match the value ofexpr, theexceptionMatch_failure is raised.

#let unoption o =match o with | Some x -> x ;;
Warning 8 [partial-match]: this pattern-matching is not exhaustive.Here is an example of a case that is not matched:Noneval unoption : 'a option -> 'a = <fun>
#let l = List.map unoption [Some 1; Some 10; None; Some 2];;
Exception: Match_failure ("expr.etex", 2, 2).

Boolean operators

The expressionexpr1&&expr2 evaluates totrue if bothexpr1 andexpr2 evaluate totrue; otherwise, it evaluates tofalse. The first component,expr1, is evaluated first. Thesecond component,expr2, is not evaluated if the first componentevaluates tofalse. Hence, the expressionexpr1&&expr2 behavesexactly as

ifexpr1thenexpr2elsefalse.

The expressionexpr1||expr2 evaluates totrue if one ofthe expressionsexpr1 andexpr2 evaluates totrue; otherwise, it evaluates tofalse. The first component,expr1, is evaluated first. Thesecond component,expr2, is not evaluated if the first componentevaluates totrue. Hence, the expressionexpr1||expr2 behavesexactly as

ifexpr1thentrueelseexpr2.

The boolean operators& andor are deprecated synonyms for(respectively)&& and||.

#let xor a b = (a || b) && not (a && b);;
val xor : bool -> bool -> bool = <fun>

Loops

The expressionwhileexpr1doexpr2done repeatedlyevaluatesexpr2 whileexpr1 evaluates totrue. The loopconditionexpr1 is evaluated and tested at the beginning of eachiteration. The wholewhiledone expression evaluates tothe unit value().

#let chars_of_string s =let i =ref 0inlet chars =ref []inwhile !i < String.length sdo chars := s.[!i] :: !chars; i := !i + 1done; List.rev !chars;;
val chars_of_string : string -> char list = <fun>

As a special case,whiletruedoexprdone is given apolymorphic type, allowing it to be used in place of any expression(for example as a branch of any pattern-matching).

The expressionforname=expr1toexpr2doexpr3donefirst evaluates the expressionsexpr1 andexpr2 (the boundaries)into integer valuesn andp. Then, the loop bodyexpr3 isrepeatedly evaluated in an environment wherename is successivelybound to the valuesn,n+1, …,p−1,p.The loop body is never evaluated ifn >p.

#let chars_of_string s =let l =ref []infor p = 0to String.length s - 1do l := s.[p] :: !ldone; List.rev !l;;
val chars_of_string : string -> char list = <fun>

The expressionforname=expr1downtoexpr2doexpr3doneevaluates similarly, except thatname is successively bound to the valuesn,n−1, …,p+1,p.The loop body is never evaluated ifn <p.

#let chars_of_string s =let l =ref []infor p = String.length s - 1downto 0do l := s.[p] :: !ldone; !l;;
val chars_of_string : string -> char list = <fun>

In both cases, the wholefor expression evaluates to the unitvalue().

Exception handling

The expression

try expr
withpattern1->expr1
|… 
|patternn->exprn

evaluates the expressionexpr and returns its value if theevaluation ofexpr does not raise any exception. If the evaluationofexpr raises an exception, the exception value is matched againstthe patternspattern1 topatternn. If the matching againstpatterni succeeds, the associated expressionexpri is evaluated,and its value becomes the value of the wholetry expression. Theevaluation ofexpri takes place in an environment enriched by thebindings performed during matching. If several patterns match the value ofexpr, the one that occurs first in thetry expression isselected. If none of the patterns matches the value ofexpr, theexception value is raised again, thereby transparently “passingthrough” thetry construct.

#let find_opt p l =try Some (List.find p l)with Not_found -> None;;
val find_opt : ('a -> bool) -> 'a list -> 'a option = <fun>

7.4 Operations on data structures

Products

The expressionexpr1,,exprn evaluates to then-tuple of the values of expressionsexpr1 toexprn. Theevaluation order of the subexpressions is not specified.

# (1 + 2 * 3, (1 + 2) * 3, 1 + (2 * 3));;
- : int * int * int = (7, 9, 7)

Variants

The expressionconstrexpr evaluates to the unary variant valuewhose constructor isconstr, and whose argument is the value ofexpr. Similarly, the expressionconstr(expr1,,exprn) evaluates to the n-ary variant value whose constructor isconstr and whose arguments are the values ofexpr1, …,exprn.

The expressionconstr(expr1, …,exprn) evaluates to thevariant value whose constructor isconstr, and whose arguments arethe values ofexpr1exprn.

#type t = Varof string | Notof t | Andof t * t | Orof t * tlet test = And (Var"x", Not (Or (Var"y", Var"z")));;
type t = Varof string | Notof t | Andof t * t | Orof t * tval test : t = And (Var"x", Not (Or (Var"y", Var"z")))

For lists, some syntactic sugar is provided. The expressionexpr1::expr2 stands for the constructor(::) applied to the arguments(expr1,expr2), and thereforeevaluates to the list whose head is the value ofexpr1 and whose tailis the value ofexpr2. The expression[expr1;;exprn] is equivalent toexpr1::::exprn::[], and therefore evaluates to the list whose elements are thevalues ofexpr1 toexprn.

# 0 :: [1; 2; 3] = 0 :: 1 :: 2 :: 3 :: [];;
- : bool =true

Polymorphic variants

The expression`tag-nameexpr evaluates to the polymorphic variantvalue whose tag istag-name, and whose argument is the value ofexpr.

#let with_counter x = `V (x,ref 0);;
val with_counter : 'a -> [> `Vof 'a * intref ] = <fun>

Records

The expression{field1 [=expr1];;fieldn [=exprn]} evaluates to the record value{field1 =v1; …;fieldn =vn }wherevi is the value ofexpri fori = 1,… ,n.A single identifierfieldk stands forfieldk=fieldk,and a qualified identifiermodule-path.fieldk stands formodule-path.fieldk=fieldk.The fieldsfield1 tofieldn must all belong to the same recordtype; each field of this record type must appear exactlyonce in the record expression, though they can appear in anyorder. The order in whichexpr1 toexprn are evaluated is notspecified. Optional type constraints can be added after each field{field1:typexpr1=expr1;;fieldn:typexprn=exprn}to force the type offieldk to be compatible withtypexprk.

#type t = {house_no : int; street : string; town : string; postcode : string}let address x = Printf.sprintf"The occupier\n%i %s\n%s\n%s" x.house_no x.street x.town x.postcode;;
type t = { house_no : int; street : string; town : string; postcode : string;}val address : t -> string = <fun>

The expression{exprwithfield1 [=expr1];;fieldn [=exprn]}builds a fresh record with fieldsfield1fieldn equal toexpr1exprn, and all other fields having the same value asin the recordexpr. In other terms, it returns a shallow copy ofthe recordexpr, except for the fieldsfield1fieldn,which are initialized toexpr1exprn. As previously,single identifierfieldk stands forfieldk=fieldk,a qualified identifiermodule-path.fieldk stands formodule-path.fieldk=fieldk and it ispossible to add an optional type constraint on each field being updatedwith{exprwithfield1:typexpr1=expr1;;fieldn:typexprn=exprn}.

#type t = {house_no : int; street : string; town : string; postcode : string}let uppercase_town address = {addresswith town = String.uppercase_ascii address.town};;
type t = { house_no : int; street : string; town : string; postcode : string;}val uppercase_town : t -> t = <fun>

The expressionexpr1.field evaluatesexpr1 to a recordvalue, and returns the value associated tofield in this recordvalue.

The expressionexpr1.field<-expr2 evaluatesexpr1 to a recordvalue, which is then modified in-place by replacing the valueassociated tofield in this record by the value ofexpr2. This operation is permitted only iffield has beendeclaredmutable in the definition of the record type. The wholeexpressionexpr1.field<-expr2 evaluates to the unit value().

#type t = {mutable upper : int;mutable lower : int;mutable other : int}let stats = {upper = 0; lower = 0; other = 0}let collect = String.iter (function | 'A'..'Z' -> stats.upper <- stats.upper + 1 | 'a'..'z' -> stats.lower <- stats.lower + 1 | _ -> stats.other <- stats.other + 1);;
type t = {mutable upper : int;mutable lower : int;mutable other : int; }val stats : t = {upper = 0; lower = 0; other = 0}val collect : string -> unit = <fun>

Arrays

The expression[|expr1;;exprn|] evaluates toan-element array, whose elements are initialized with the values ofexpr1 toexprn respectively. The order in which theseexpressions are evaluated is unspecified.

The expressionexpr1.(expr2) returns the value of elementnumberexpr2 in the array denoted byexpr1. The first elementhas number 0; the last element has numbern−1, wheren is thesize of the array. The exceptionInvalid_argument is raised if theaccess is out of bounds.

The expressionexpr1.(expr2)<-expr3 modifies in-placethe array denoted byexpr1, replacing element numberexpr2 bythe value ofexpr3. The exceptionInvalid_argument is raised ifthe access is out of bounds. The value of the whole expression is().

#let scale arr n =for x = 0to Array.length arr - 1do arr.(x) <- arr.(x) * ndonelet x = [|1; 10; 100|]let _ = scale x 2;;
val scale : int array -> int -> unit = <fun>val x : int array = [|2; 20; 200|]

Strings

The expressionexpr1.[expr2] returns the value of characternumberexpr2 in the string denoted byexpr1. The first characterhas number 0; the last character has numbern−1, wheren is thelength of the string. The exceptionInvalid_argument is raised if theaccess is out of bounds.

#let iter f s =for x = 0to String.length s - 1do f s.[x]done;;
val iter : (char -> 'a) -> string -> unit = <fun>

The expressionexpr1.[expr2]<-expr3 modifies in-placethe string denoted byexpr1, replacing character numberexpr2 bythe value ofexpr3. The exceptionInvalid_argument is raised ifthe access is out of bounds. The value of the whole expression is().Note: this possibility is offered only for backwardcompatibility with older versions of OCaml and will be removed in afuture version. New code should use byte sequences and theBytes.setfunction.

7.5 Operators

Symbols from the classinfix-symbol, as well as the keywords*,+,-,-.,=,!=,<,>,or,||,&,&&,:=,mod,land,lor,lxor,lsl,lsr,andasr can appear in infix position (between twoexpressions). Symbols from the classprefix-symbol, as well asthe keywords- and-.can appear in prefix position (in front of an expression).

# (( * ), ( := ), ( || ));;
- : (int -> int -> int) * ('aref -> 'a -> unit) * (bool -> bool -> bool) =(<fun>, <fun>, <fun>)

Infix and prefix symbols do not have a fixed meaning: they are simplyinterpreted as applications of functions bound to the namescorresponding to the symbols. The expressionprefix-symbolexpr isinterpreted as the application(prefix-symbol)expr. Similarly, the expressionexpr1infix-symbolexpr2 isinterpreted as the application(infix-symbol)expr1expr2.

The table below lists the symbols defined in the initial environmentand their initial meaning. (See the description of the corelibrary moduleStdlib in chapter 28 for moredetails). Their meaning may be changed at any time usinglet(infix-op)name1name2=

#let ( + ), ( - ), ( * ), ( / ) = Int64.(add, sub, mul, div);;
val ( + ) : int64 -> int64 -> int64 = <fun>val ( - ) : int64 -> int64 -> int64 = <fun>val ( * ) : int64 -> int64 -> int64 = <fun>val ( / ) : int64 -> int64 -> int64 = <fun>

Note: the operators&&,||, and~- are handled speciallyand it is not advisable to change their meaning.

The keywords- and-. can appear both as infix andprefix operators. When they appear as prefix operators, they areinterpreted respectively as the functions(~-) and(~-.).

OperatorInitial meaning
+Integer addition.
- (infix)Integer subtraction.
~- - (prefix)Integer negation.
*Integer multiplication.
/Integer division.RaiseDivision_by_zero if second argument is zero.
modInteger modulus. RaiseDivision_by_zero if second argument is zero.
landBitwise logical “and” on integers.
lorBitwise logical “or” on integers.
lxorBitwise logical “exclusive or” on integers.
lslBitwise logical shift left on integers.
lsrBitwise logical shift right on integers.
asrBitwise 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 currentcontents of a reference).
:=Reference assignment (update thereference given as first argument with the value of the secondargument).
=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”.
&& &Boolean conjunction.
|| orBoolean disjunction.

7.6 Objects

Object creation

Whenclass-path evaluates to a class body,newclass-pathevaluates to a new object containing the instance variables andmethods of this class.

#class of_list (lst : int list) =objectvalmutable l = lstmethod next =match lwith | [] -> raise (Failure"empty list"); | h::t -> l <- t; hendlet a =new of_list [1; 1; 2; 3; 5; 8; 13]let b =new of_list;;
class of_list : int list ->objectvalmutable l : int listmethod next : intendval a : of_list = <obj>val b : int list -> of_list = <fun>

Whenclass-path evaluates to a class function,newclass-pathevaluates to a function expecting the same number of arguments andreturning a new object of this class.

Immediate object creation

Creating directly an object through theobjectclass-bodyendconstruct is operationally equivalent to defining locally aclassclass-name=objectclass-bodyend —see sections11.9.2 and following for the syntax ofclass-body—and immediately creating a single object from it bynewclass-name.

#let o =objectval secret = 99val password ="unlock"method get guess =if guess <> passwordthen Noneelse Some secretend;;
val o : < get : string -> int option > = <obj>

The typing of immediate objects is slightly different from explicitlydefining a class in two respects. First, the inferred object type maycontain free type variables. Second, since the class body of animmediate object will never be extended, its self type can be unifiedwith a closed object type.

Method invocation

The expressionexpr#method-name invokes the methodmethod-name of the object denoted byexpr.

#class of_list (lst : int list) =objectvalmutable l = lstmethod next =match lwith | [] -> raise (Failure"empty list"); | h::t -> l <- t; hendlet a =new of_list [1; 1; 2; 3; 5; 8; 13]let third = ignore a#next; ignore a#next; a#next;;
class of_list : int list ->objectvalmutable l : int listmethod next : intendval a : of_list = <obj>val third : int = 2

Ifmethod-name is a polymorphic method, its type should be known atthe invocation site. This is true for instance ifexpr is the nameof a fresh object (letident =newclass-path … ) or ifthere is a type constraint. Principality of the derivation can bechecked in the-principal mode.

Accessing and modifying instance variables

The instance variables of a class are visible only in the body of themethods defined in the same class or a class that inherits from theclass defining the instance variables. The expressioninst-var-nameevaluates to the value of the given instance variable. The expressioninst-var-name<-expr assigns the value ofexpr to the instancevariableinst-var-name, which must be mutable. The whole expressioninst-var-name<-expr evaluates to().

#class of_list (lst : int list) =objectvalmutable l = lstmethod next =match lwith(* access instance variable *) | [] -> raise (Failure"empty list"); | h::t -> l <- t; h(* modify instance variable *)end;;
class of_list : int list ->objectvalmutable l : int listmethod next : intend

Object duplication

An object can be duplicated using the library functionOo.copy(see moduleOo). Inside a method, the expression{< [inst-var-name [=expr] {;inst-var-name [=expr] }]>}returns a copy of self with the given instance variables replaced bythe values of the associated expressions. A single instance variablenameid stands forid=id. Other instance variables have the samevalue in the returned object as in self.

#let o =objectval secret = 99val password ="unlock"method get guess =if guess <> passwordthen Noneelse Some secretmethod with_new_secret s = {< secret = s >}end;;
val o : < get : string -> int option; with_new_secret : int -> 'a >as 'a = <obj>

7.7 Coercions

Expressions whose type contains object or polymorphic variant typescan be explicitly coerced (weakened) to a supertype.The expression(expr:>typexpr) coerces the expressionexprto typetypexpr.The expression(expr:typexpr1:>typexpr2) coerces theexpressionexpr from typetypexpr1 to typetypexpr2.

The former operator will sometimes fail to coerce an expressionexprfrom a typetyp1 to a typetyp2even if typetyp1 is a subtype of typetyp2: in the current implementation it only expands two levels oftype abbreviations containing objects and/or polymorphic variants,keeping only recursion when it is explicit in the class type (for objects).As an exception to the above algorithm, if both the inferred type ofexprandtyp are ground (i.e. do not contain type variables), theformer operator behaves as the latter one, taking the inferred type ofexpr astyp1. In case of failure with the former operator,the latter one should be used.

It is only possible to coerce an expressionexpr from typetyp1 to typetyp2, if the type ofexpr is an instance oftyp1 (like for a type annotation), andtyp1 is a subtypeoftyp2. The type of the coerced expression is aninstance oftyp2. If the types contain variables,they may be instantiated by the subtyping algorithm, but this is onlydone after determining whethertyp1 is a potential subtype oftyp2. This means that typing may fail during this latterunification step, even if some instance oftyp1 is a subtype ofsome instance oftyp2.In the following paragraphs we describe the subtyping relation used.

Object types

A fixed object type admits as subtype any object type that includes allits methods. The types of the methods shall be subtypes of those inthe supertype. Namely,

<met1:typ1;;metn:typn>

is a supertype of

<met1:typ1;;metn:typn;metn+1:typn+1;;metn+m:typn+m [;..]>

which may contain an ellipsis.. if everytypi is a supertype ofthe correspondingtypi.

A monomorphic method type can be a supertype of a polymorphic methodtype. Namely, iftyp is an instance oftyp′, then'a1'an.typ′ is a subtype oftyp.

Inside a class definition, newly defined types are not available forsubtyping, as the type abbreviations are not yet completelydefined. There is an exception for coercingself to the (exact)type of its class: this is allowed if the type ofself does notappear in a contravariant position in the class type,i.e. ifthere are no binary methods.

Polymorphic variant types

A polymorphic variant typetyp is a subtype of another polymorphicvariant typetyp′ if the upper bound oftyp (i.e. themaximum set of constructors that may appear in an instance oftyp)is included in the lower bound oftyp′, and the types of argumentsfor the constructors oftyp are subtypes of those intyp′. Namely,

[[<]`C1oftyp1||`Cnoftypn]

which may be a shrinkable type, is a subtype of

[[>]`C1oftyp1||`Cnoftypn|`Cn+1oftypn+1||`Cn+moftypn+m]

which may be an extensible type, if everytypi is a subtype oftypi.

Variance

Other types do not introduce new subtyping, but they may propagate thesubtyping of their arguments. For instance,typ1*typ2 is asubtype oftyp1*typ2 whentyp1 andtyp2 arerespectively subtypes oftyp1 andtyp2.For function types, the relation is more subtle:typ1->typ2 is a subtype oftyp1 ->typ2iftyp1 is a supertype oftyp1 andtyp2 is asubtype oftyp2. For this reason, function types are covariant intheir second argument (like tuples), but contravariant in their firstargument. Mutable types, likearray orref are neither covariantnor contravariant, they are nonvariant, that is they do not propagatesubtyping.

For user-defined types, the variance is automatically inferred: aparameter is covariant if it has only covariant occurrences,contravariant if it has only contravariant occurrences,variance-free if it has no occurrences, and nonvariant otherwise.A variance-free parameter may change freely through subtyping, it doesnot have to be a subtype or a supertype.For abstract and private types, the variance must be given explicitly(see section 11.8.1),otherwise the default is nonvariant. This is also the case forconstrained arguments in type definitions.

7.8 Other

Assertion checking

OCaml supports theassert construct to check debugging assertions.The expressionassertexpr evaluates the expressionexpr andreturns() ifexpr evaluates totrue. If it evaluates tofalse the exceptionAssert_failure is raised with the source file name and thelocation ofexpr as arguments. Assertionchecking can be turned off with the-noassert compiler option. Inthis case,expr is not evaluated at all.

#let f a b c =assert (a <= b && b <= c); (b -. a) /. (c -. b);;
val f : float -> float -> float -> float = <fun>

As a special case,assert false is reduced toraise(Assert_failure ...), which gives it a polymorphictype. This means that it can be used in place of any expression (forexample as a branch of any pattern-matching). It also means thattheassert false “assertions” cannot be turned off by the-noassert option.

#let min_known_nonempty =function | [] ->assertfalse | l -> List.hd (List.sort compare l);;
val min_known_nonempty : 'a list -> 'a = <fun>

Lazy expressions

The expressionlazyexpr returns a valuev of typeLazy.t thatencapsulates the computation ofexpr. The argumentexpr is notevaluated at this point in the program. Instead, its evaluation willbe performed the first time the functionLazy.force is applied to the valuev, returning the actual value ofexpr. Subsequent applicationsofLazy.force tov do not evaluateexpr again. ApplicationsofLazy.force may be implicit through pattern matching (see 11.6).

#let lazy_greeter =lazy (print_string"Hello, World!\n");;
val lazy_greeter : unit lazy_t = <lazy>
# Lazy.force lazy_greeter;;
Hello, World!- : unit = ()

Local modules

The expressionletmodulemodule-name=module-exprinexprlocally binds the module expressionmodule-expr to the identifiermodule-name during the evaluation of the expressionexpr.It then returns the value ofexpr. For example:

#let remove_duplicates comparison_fun string_list =letmodule StringSet = Set.Make(structtype t = stringlet compare = comparison_funend)in StringSet.elements (List.fold_right StringSet.add string_list StringSet.empty);;
val remove_duplicates : (string -> string -> int) -> string list -> string list = <fun>

Local opens

The expressionsletopenmodule-pathinexpr andmodule-path.(expr) are strictly equivalent. Theseconstructions locally open the module referred to by the module pathmodule-path in the respective scope of the expressionexpr.

#let map_3d_matrix f m =letopen Arrayin map (map (map f)) mlet map_3d_matrix' f = Array.(map (map (map f)));;
val map_3d_matrix : ('a -> 'b) -> 'a array array array -> 'b array array array = <fun>val map_3d_matrix' : ('a -> 'b) -> 'a array array array -> 'b array array array = <fun>

When the body of a local open expression is delimited by[],[||], or{}, the parentheses can be omitted.For expression, parentheses can also be omitted for{<>}.For example,module-path.[expr] is equivalent tomodule-path.([expr]), andmodule-path.[|expr|] isequivalent tomodule-path.([|expr|]).

#let vector = Random.[|int 255; int 255; int 255; int 255|];;
val vector : int array = [|220; 90; 247; 144|]
« PatternsType and exception definitions »
Copyright © 2025 Institut National deRecherche en Informatique et en Automatique

[8]ページ先頭

©2009-2025 Movatter.jp