val r = ref 0 (*create an int cell initialized to 0 *) val s = ref 0 (*create an int cell initialized to 0 *) val _ = r:= 3 (*update value of cell pointed to by r to 3 *) val x = !s + !r (*adds values in cells pointed to by s and t*) val t = r (*t and r now point to the same cell *) val _ = t := 5 (*update cell pointed to by t (and r) *) val y = !s + !t
exp1;exp2
which is sugar for
let exp1 in exp2
val r = ref 0 val s = ref 0 val x = (r := 3; !s + !r) val t = r val y = (t:= 5; !s + !t)
fun f (a:int ref, b: int ref) = (a := !a +1; !b) val s = ref 0 val t = s f(s,t) (*will return 1*)
fun f (ref a) = a val x = ref 0 f x (*will return 0 *)
fun impFact (n:int) = let val result = ref 1 val i = ref 0 fun loop () = if (!i = n) then () else (i := !i+1); result := !result * !i; loop()) in (loop(); !result) endPoint: factorial is really a pure function that does not need storage or assignment.
Suppose we want to be able to count how many times a function is invoked in the program.
First attempt: wrap the function to produce another function that increments counter and then calls the function
val counter = ref 0; fun wrap f = (counter := !counter +1; f) val wrapAdd = wrap add; wrapAdd 5 6 !counter (*will return 1 *) wrapAdd 6 7 !counter (*will return 1*)Problem: counter is only incremented when we wrap the function. We want it to be incremented whenever we invoke the function.
Second attempt:
fun wrap f = fn x => (counter := !counter +1; f x); val wrapAdd = wrap add; wrapAdd 4 5;Problem: this works correctly but we have only one counter for every function we want to wrap.
Third attempt:
fun wrap f = let val counter = ref 0 in fn x => (counter := !counter + 1; f x) endProblem: counter is not visible to the rest of the program, so we have no way to read counter value when we are done invoking the function!
Fourth attempt:
fun wrap f = let val counter = ref 0 in (fn x => (counter := !counter + 1; f x), fn () => !counter ) end fun add x y = x + y val (wrapAdd, readAdd) = wrap add readAdd() wrapAdd 3 4 readAdd() ...
val array: int * 'a => 'a array val length: 'a array => int val sub: 'a array * int =? 'a val update: 'a array * int * 'a -> unit val a = Array.array(10,0) (*array subscripts go from 0 to 9) val n = Array.array(a) val e = Array.sub(a,5) (*may raise subscript out of bounds exception*) val _ = Array.update(a,4,3)
val l = [ref 0, ref 0, ref 0] (* type of l is int ref list *) hd(l) := 4 l (* will return [ref 4, ref 0, ref 0] *) hd(tl(l)) := !(hd(l)) + 4 ....
From our previous discussions we know that we can only declare recursive functions using the fun, or val rec statements. We also know that we can use val statements to declare simple (non-recursive) functions. We show the power of references by implementing recursive functions using val statements.
We illustrate our method using the canonical example of recursive functions, the factorial:
fun fact(n: int): int = if n = 0 then 1 else n * (fact(n - 1))If we were to use a val statement to declare fact, the declaration would fail because of the recursive call fact(n - 1). To avoid this problem, we replace the recursive call with a call to a function specified using a reference:
val x: (int -> int) ref = ref (fn _: int => 1) val fact: int -> int = (fn (n: int) => if n = 0 then 1 else n * (!x)(n - 1))Function fact does not really implement the factorial yet:
/ | 1, if n = 0, fact(n) = -| | n, otherwise. \We can "convince" fact to implement the factorial by resetting reference x to fact itself:
val () = x := factWith this transformation, we now have a correct implementation of the factorial function:
- fact 0; val it = 1 : int - fact 1; val it = 1 : int - fact 3; val it = 6 : int - fact 10; val it = 3628800 : int