The keywords for introducing value identifiers and variable identifiers are val and var, respectiveily. The essential difference between a value identifier and a variable identifier is that the value referred to by the former cannot be changed during the course of evaluation while the value referred to by the latter can. This difference is clearly reflected in the following two styles of implementation of the factorial function:
// functional style fn fact_val (x: int): int = loop (x, 1) where { fun loop (x: int, res: int): int = if x > 0 then loop (x-1, x * res) else res // end of [loop] } // end of [fact_val] // imperative style fn fact_var (x: int): int = let var x: int = x; var res: int = 1 val () = while (x > 0) (res := x * res; x := x - 1) in res end // end of [fact_var]In a functional language such as ML, where variable identifiers are not available, an imperative style of implementation of the factorial function may have to be written as follows:
// imperative style based on persistent references, which looks // awkward and runs inefficiently (in terms of both time and memory) fn fact_ref (x: int): int = let val x = ref<int> (x); val res = ref<int> (1) val () = while (!x > 0) (!res := !x * !res; !x := !x - 1) in !res end // end of [fact_ref]An implementation as such is often written by a beginner in functional programming who has previously programmed in imperative languages (e.g., C, C++, Java). The function fact_ref is clearly inefficient as each call to it needs to allocate two references on heap (corresponding to x and res), which can only be reclaimed by GC later.
We give another implementation of the factorial function as follows that involves passing the addresses of local variables as function parameters:
fun loop {x,res:addr} (pf_x: !int @ x, pf_res: !int @ res | p_x: ptr x, p_res: ptr res): void = if !p_x > 0 then begin !p_res := !p_x * !p_res; !p_x := !p_x - 1; loop (pf_x, pf_res | p_x, p_res) end // end of [loop] // end of [loop] fn fact_var2 (x: int): int = let var x: int = x; var res: int = 1 in loop (view@ x, view@ res | &x, &res); res end // end of [fact_var2]Each variable identifier is assoicated with two pieces of properties: its address L, which is referred to by the same identifier, and a proof of VT@L, where VT is the viewtype of the content stored at L. For instance, in the implementation of fact_var2, we use view@ x for the proof associated with the variable x and &x for the address of x.
For each variable that is declared of viewtype VT, it is required that a proof of VT?@L is available at the end of the legal scope of the variable, where L is the address of the variable. This requirement guarantees that a local variable, while its address can be taken out of its scope, can never be accessed out of its scope due to the unavailability of a proof needed for accessing the address.
The code used for illustration is available here.