ヒープに確保された線形クロージャ関数

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)) )

キーワード -<cloptr1> はその関数型が線形クロージャ関数であること示すことに注意してください。 もし純粋な線形クロージャ関数の型が必要であれば、キーワード -<cloptr0> が使えます。 関数型の前にある記号 !list_map_cloptr の線形の第二引数が値渡しであり、list_map_cloptr が返った後もそれが有効であることを意味します。

線形クロージャ関数が生成され、呼び出され、最後に解放されるような具体的なコードをいくつか見てみましょう:

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] *)

関数 cloptr_free には次のインターフェイスが与えられます:

fun cloptr_free{a:t0p}(pclo: cloptr (a)):<!wrt> void

また、$UNSAFE.castvwtp0{cloptr(void)}(f) に含まれるキャストは安全なキャストです。

ATS1 での線形クロージャ関数のサポートは、高階関数が必要だけれど実行時のガベージコレクション (GC) が使えないような場面で重要でした。 ATS2 では、線形クロージャ関数は GC のない高階関数を使ったプログラミングでも重要ではなくなりました。 好都合なことにテンプレートを使うことができるのです。 けれども、もしメモリリークを引き起こさずにデータ構造にクロージャ関数を埋め込みたい場合で、 さらにメモリを再利用するために GC に頼ることもできない場合には、 線形クロージャ関数の使用が必要になります。