# letc
=
ref
0
;;val c : int ref = {contents=0}
# letreset_symb
=
function
()
->
c
:=
0
;;
val reset_symb : unit -> unit = <fun>
# letnew_symb
=
function
s
->
c
:=!
c+
1
;
s
^
(string_of_int
!
c);;
val new_symb : string -> string = <fun>
# new_symb
"VAR"
;;
- : string = "VAR1"
# new_symb
"VAR"
;;
- : string = "VAR2"
# reset_symb()
;;
- : unit = ()
# new_symb
"WAR"
;;
- : string = "WAR1"
# new_symb
"WAR"
;;
- : string = "WAR2"
# let(reset_s
,
new_s)
=
let
c
=
ref
0
in
let
f1
()
=
c
:=
0
and
f2
s
=
c
:=
!
c+
1
;
s
^
(string_of_int
!
c)
in
(f1
,
f2);;
val reset_s : unit -> unit = <fun>
val new_s : string -> string = <fun>
# new_s
"VAR"
;;- : string = "VAR1"
# new_s
"VAR"
;;- : string = "VAR2"
# reset_s();;- : unit = ()
# new_s
"WAR"
;;- : string = "WAR1"
# new_s
"WAR"
;;- : string = "WAR2"
These two closures share the same environment, containing the value of c. When either one modifies the reference c, it modifies the contents of an area of memory that is shared with the other closure.
The following example shows a little computation where division by zero occurs together with
# let(
++
)x
=
x
:=!
x+
1
;x;;
val ++ : int ref -> int ref = <fun>
The variable x is not modified during the computation of the expression in (*1*), while it is modified in the computation of (*2*). Unless one saves the initial values, the form try .. with .. must not have a with .. part that depends on modifiable variables implicated in the expression that raised the exception.
# letx
=
ref
2
;;val x : int ref = {contents=2}
(* 1 *)
#!
((++
)x)
*
(
1
/
0
);;
Uncaught exception: Division_by_zero
# x;;- : int ref = {contents=2}
(* 2 *)
# (1
/
0
)
*
!
((++
)x)
;;
Uncaught exception: Division_by_zero
# x;;- : int ref = {contents=3}
# letnil_assoc
=
function
x
->
raise
Not_found
;;
val nil_assoc : 'a -> 'b = <fun>
# letadd_assoc
(k
,
v)l
=
function
x
->
if
x
=
k
then
v
else
l
x
;;
val add_assoc : 'a * 'b -> ('a -> 'b) -> 'a -> 'b = <fun>
# letl
=
add_assoc
(
'1'
,
1
)(add_assoc
(
'2'
,
2
)nil_assoc)
;;
val l : char -> int = <fun>
# l
'2'
;;
- : int = 2
# l
'x'
;;
Uncaught exception: Not_found
# letmem_assoc
k
l
=
try
(l
k)
;
true
with
Not_found
->
false
;;
val mem_assoc : 'a -> ('a -> 'b) -> bool = <fun>
# mem_assoc
'2'
l
;;
- : bool = true
# mem_assoc
'x'
l
;;
- : bool = false
# letrem_assoc
k
l
=
function
x
->
if
x
=
kthen
raise
Not_found
else
l
x
;;
val rem_assoc : 'a -> ('a -> 'b) -> 'a -> 'b = <fun>
# letl
=
rem_assoc
'2'
l
;;
val l : char -> int = <fun>
# l
'2'
;;
Uncaught exception: Not_found
# letadd_assoc_again
(k
,
v)l
=
l
:=
(function
x
->
if
x
=
kthen
v
else
!
lx)
;;
val add_assoc_again : 'a * 'b -> ('a -> 'b) ref -> unit = <fun>
!
l is within the scope of the closure
function
x
->. The value of !
l is not evaluated
during compilation, but at run-time. At that time, l points
to the value that has already been modified by add_assoc. We
must therefore correct our definition using the closure created by our
original definition of add_assoc:
# letadd_assoc_again
(k
,
v)
l
=
l
:=
add_assoc
(k
,
v)
!
l;;
val add_assoc_again : 'a * 'b -> ('a -> 'b) ref -> unit = <fun>
# letl
=
ref
nil_assoc
;;
val l : ('_a -> '_b) ref = {contents=<fun>}
# add_assoc_again(
'1'
,
1
)l
;;
- : unit = ()
# add_assoc_again(
'2'
,
2
)l
;;
- : unit = ()
#!
l
'1'
;;
- : int = 1
#!
l
'x'
;;
Uncaught exception: Not_found
# type'a
v
=
Imm
of
'a
|
Deferred
of
(unit
->
'a);;
# type'a
vm
=
{mutable
c
:
'a
v
};;
# leteval
e
=
match
e
.
cwith
Imm
a
->
a
|
Deferred
f
->
let
u
=
f
()
in
e
.
c
<-
Imm
u
;
u
;;
val eval : 'a vm -> 'a = <fun>
# letif_deferred
c
e1
e2
=
if
eval
c
then
eval
e1
else
eval
e2;;
val if_deferred : bool vm -> 'a vm -> 'a vm -> 'a = <fun>
# letrec
facr
n
=
if_deferred
{c
=
Deferred(fun()
->
n
=
0
)}
{c
=
Deferred(fun()
->
1
)}
{c
=
Deferred(fun()
->
n
*
(facr(n-
1
)))};;val facr : int -> int = <fun>
# facr
5
;;- : int = 120
# letif_function
c
e1
e2
=
if
c
then
e1
else
e2;;
val if_function : bool -> 'a -> 'a -> 'a = <fun>
-
1
) is always
evaluated, even when n has the value 0.
# letrec
fact
n
=
if_function
(n
=
0
)
1
(n
*
fact(n-
1
));;
val fact : int -> int = <fun>
# fact
5
;;
Stack overflow during evaluation (looping recursion?).
When this function is applied to arguments, the Objective CAML evaluation strategy evaluates the expression e passed as argument before constructing the closure fun
# letfreeze
e
=
{
c
=
Deferred
(fun
()
->
e)
};;
val freeze : 'a -> 'a vm = <fun>
()
->
e. The next example
shows this:
# freeze(print_string
"trace"
;print_newline();
4
*
5
);;trace
- : int vm = {c=Deferred <fun>}
# letx
=
lazy
(print_string
"Hello"
;
3
*
4
);;
val x : int Lazy.status ref = {contents=Lazy.Delayed <fun>}
"Hello"
) has not been
evaluated, because no message has been printed. The function
force of module Lazy allows one to force evaluation:
Now the value x has altered:
# Lazy.forcex
;;
Hello- : int = 12
It has become the value of the expression that had been frozen, namely
# x;;
- : int Lazy.t = {contents=Lazy.Value 12}
1
2
. The string
# Lazy.forcex
;;
- : int = 12
"Hello"
is no longer prefixed.
# type'a
enum
=
{
mutable
i
:
'a;
f
:
'a->
'a
}
;;
type 'a enum = { mutable i: 'a; f: 'a -> 'a }
# letnext
e
=
let
x
=
e
.
iin
e
.
i
<-
(e
.
fe
.
i);
x
;;
val next : 'a enum -> 'a = <fun>
Another example gives the elements of the Fibonnacci sequence, which has the definition:
# letnat
=
{
i
=
0
;f
=
funx
->
x
+
1
};;
val nat : int enum = {i=0; f=<fun>}
# nextnat;;
- : int = 0
# nextnat;;
- : int = 1
# nextnat;;
- : int = 2
ì í î |
|
# letfib
=
let
fx
=
let
c
=
ref
0
in
fun
v
->
let
r
=
!
c
+
v
in
c
:=
v;
r
in
{
i
=
1
;
f
=
fx}
;;
val fib : int enum = {i=1; f=<fun>}
# fori
=
0
to
1
0
do
print_int
(next
fib);
print_string
" "
done
;;
1 1 2 3 5 8 13 21 34 55 89 - : unit = ()