References

A reference is just an array containing one element. Given a type T, a reference for storing a value of the type T is given the type ref(T). The following program makes use of all the essential functionalities on references:

val intr = ref<int> (0) // create a ref and init. it with 0
val () = !intr := !intr + 1 // increase the integer at [intr] by 1

The first line creates a reference for storing an integer and initializes it with the value 0 and then names it intr. Note that the creation of a reference cannot be separated from its initialization. The second line updates the reference intr with its current value plus 1. In general, given a reference r of type ref(T) for some T, the expression !r means to fetch the value stored at r, which is of the type T. However, !r can also be used as a left-value. For instance, the assignment (!r := exp) means to evaluate exp into a value and then store the value into r. Therefore, the value stored in intr is 1 after the second line in the above program is evaluated.

Various functions and function templates on references are declared in the file prelude/SATS/reference.sats, which is automatically loaded by atsopt. In particular, it is also possible to read from and write to a reference by using the function templates ref_get_elt and ref_set_elt of the following interfaces, respectively:

fun{a:t@ype} ref_get_elt (r: ref a): a // !r
fun{a:t@ype} ref_set_elt (r: ref a, x: a): void // !r := x

If you implement a program that makes use of references, please do not forget to include the following line somewhere in the program:

staload _(*anon*) = "prelude/DATS/reference.dats"

This line allows the ATS compiler atsopt to gain access to the defintion of various functions and function templates on references.

References are often misused in practice, especially, by beginners in functional programming who had some previous exposure to imperative programming languages such C and Java. Such programmers often think that they can just "translate" their programs in C or Java into functional programs. For example, the following defined function sum is such an example, which sums up all the integers between 1 and a given integer, inclusively:

fun sum
  (n: int): int = let
  val i = ref<int> (1)
  val res = ref<int> (0)
  fun loop ():<cloref1> void =
    if !i <= n then (!res := !res + !i; !i := !i + 1; loop ())
  // end of [loop]
in
  loop (); !res
end // end of [sum]

This is a correct but poor implementation, and its style, though not the worst of its kind, is deplorable. As references are allocated in heap, reading from or writing to a reference can be much more time-consuming than reading from or writing to a register. So, this implementation of sum is unlikely to be time-efficient. Every call to sum creates two references in heap and leaves them there when it returns, and the memory allocated for such references can only be reclaimed by a garbage collector (GC). So, this implementation of sum is not memory-efficient. More importantly, a program making heavy use of references is often difficult to reason about.

I consider references a dangerous feature in functional programming. If you want to run your program without GC, please do not create references in the body of a function (besides many other restrictions). If you find that you are in need of references to "translate" imperative programs into functional ones, then it is most likely that you are lost and you have not learned well to program in a functional style yet.