In ATS, a closure-function can be assiged a linear type, allowing it to be properly tracked within the type system and also explicitly freed by the programmer.
The following code implements a higher-order function list_map_cloptr which takes a linear closure-function as its second argument:
fun{ a:t@ype}{b:vt@ype } list_map_cloptr{n:int} ( xs: list (a, n), f: !(a) -<cloptr1> b ) : list_vt (b, n) = ( case+ xs of | list_nil () => list_vt_nil () | list_cons (x, xs) => list_vt_cons (f (x), list_map_cloptr<a><b> (xs, f)) )
Let us now see some concrete code in which a linear closure-function is created, called, and finally freed:
implement main0 () = { // val xs = $list_vt{int}(0, 1, 2, 3, 4) // val len = list_vt_length (xs) // val f = lam (x: int): int =<cloptr1> x * len // val ys = list_map_cloptr<int><int> ($UNSAFE.list_vt2t(xs), f) // val () = cloptr_free($UNSAFE.castvwtp0{cloptr(void)}(f)) // val () = println! ("xs = ", xs) // xs = 0, 1, 2, 3, 4 val () = println! ("ys = ", ys) // ys = 0, 5, 10, 15, 20 // val ((*freed*)) = list_vt_free (xs) val ((*freed*)) = list_vt_free (ys) // } (* end of [main0] *)
The support for linear closure-functions in ATS1 is crucial in a setting where higher-order functions are needed but run-time garbage collection (GC) is not allowed or supported. In ATS2, linear closure-functions become much less important as programming with higher-order functions in a setting without GC can be more conveniently achieved through the use of templates. However, if one wants to store closure-functions in a data structure without causing memory leaks, it is necessary to use linear closure-functions unless GC can be relied upon to reclaim memory.