ATS では、クロージャ関数には線形型を割り当てることができます。 これは型システムによって適切に追跡され、またプログラマによって明示的に解放されます。
次のコードは、第二引数に線形クロージャ関数を取る高階関数 list_map_cloptr を実装しています:
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)) )
線形クロージャ関数が生成され、呼び出され、最後に解放されるような具体的なコードをいくつか見てみましょう:
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] *)
ATS1 での線形クロージャ関数のサポートは、高階関数が必要だけれど実行時のガベージコレクション (GC) が使えないような場面で重要でした。 ATS2 では、線形クロージャ関数は GC のない高階関数を使ったプログラミングでも重要ではなくなりました。 好都合なことにテンプレートを使うことができるのです。 けれども、もしメモリリークを引き起こさずにデータ構造にクロージャ関数を埋め込みたい場合で、 さらにメモリを再利用するために GC に頼ることもできない場合には、 線形クロージャ関数の使用が必要になります。