クロージャは関数を拡張した概念で、環境(スコープのようなもの)を持った関数のことです。と言っても関数型言語に触った人間でなければなかなか分かりづらい概念だと思います。
Java言語などではクロージャはクラスで実装されています。これに習ってクラスを使ってクロージャを作ってみましょう。
足し合わせを行うクロージャ
まずは、決められた数を足し合わせるクロージャです。
; クロージャのインターフェース
class ClosureIntToInt()
+func body(n: int): int
ret 0
end func
end class
; クロージャを生成する関数
func increment(delta: int): @ClosureIntToInt
class Increment(@ClosureIntToInt)
+var delta1: int
+*func body(n: int): int
ret n + me.delta1
end func
end class
var res: Increment :: #Increment
do res.delta1 :: delta
ret res
end func
func main()
var f: @ClosureIntToInt :: @increment(3)
do dbg@print(f.body(1).toStr() ~ "\n")
do dbg@print(f.body(3).toStr() ~ "\n")
do dbg@print(f.body(5).toStr() ~ "\n")
end func
クロージャを生成する関数の外側にインターフェースを作るのがポイントです。Kuinにはインターフェースを作成する機能はないのでクラスで代用します。クロージャのメソッドを呼び出すことで計算を行います。
クロージャを生成する関数の中にクロージャクラスの定義を書きます。生成関数はクロージャクラスではなく、先ほどのインターフェースの参照を送出することでクロージャの実装を隠蔽します。具体的に言うと、@increment(3)
で3を足し合わせるクロージャを生成するのですが、その値を保持するdelta1
フィールドにはアクセスすることができません。なぜならば、increment
関数の外側でIncrement
クラスを指定することができないからです。試しにmain
関数内にdo dbg@print((f $ @Increment).delta1)
と記述するとコンパイルエラーになります。
クロージャによってイミュータブル(immutable)なオブジェクトを生成することができます。イミュータブルは「書き換え不能な」という意味です。Kuinでもイミュータブルなオブジェクトを作ることは可能なのです。
カウントを行うクロージャ
次に、カウントを行うクロージャです。
; クロージャのインターフェース
class ClosureVoidToInt()
+func body(): int
ret 0
end func
end class
; クロージャを生成する関数
func counter(): @ClosureVoidToInt
class Counter(@ClosureVoidToInt)
+var cnt: int
+*func body(): int
do me.cnt :+ 1
ret me.cnt
end func
end class
var res: Counter :: #Counter
do res.cnt :: 0
ret res
end func
func main()
var f: @ClosureVoidToInt :: @counter()
do dbg@print(f.body().toStr() ~ "\n")
do dbg@print(f.body().toStr() ~ "\n")
do dbg@print(f.body().toStr() ~ "\n")
end func
プログラムを実行して結果を確認してみましょう。クロージャを呼び出すたびにカウント値が1つずつ増えていきます。
結論
Kuin言語でもクロージャを生成することが可能であることが分かりました。また、Kuinの入れ子構造と継承機能を使えばファイルスコープを使わなくともイミュータブルなオブジェクトを作ったりカプセル化を行うことが可能であることが分かりました。