クロージャ

http://d.hatena.ne.jp/lethevert/20050811/p3

環境をクローズする話をしているのですから、高階関数云々は不要だったと思います。すみません。

本題ですが、クロージャはもちろんあります。もともと変数のスコープがネストを許すことがほとんどなので、わざわざクロージャということはあまり無いような気がしますが。
[Scheme]

(let ((y 2))
     (map (lambda (x) (+ x y)) '(1 2 3)))        => '(3 4 5)

(lambda (x) (+ x y))クロージャで、外の環境の変数yを参照しています。

[OCaml]

let y = 2 in
  List.map (fun x -> x + y) [1;2;3]                => [3;4;5]

(fun x -> x + y)クロージャで、外の環境の変数yを参照しています。

Haskellは知りません。すみません。

ついでに。elispはdynamic scopeですので、クロージャの中のxという変数の値は、クロージャが評価された環境でのxの値になります。schemeOCaml(ML)はstatic scopeですので、クロージャの中のxという変数の値は、クロージャが定義された環境のxの値になります。

さらに言えば、schemeset!を使えば外側の環境の変数の値を更新できます。OCaml(ML)は変数のリファレンスを使えば外側の環境の変数の値を更新できます。Haskellは純粋関数型言語ということで副作用がないはずですので、多分できません。

いまいちクロージャっぽくないと思ったので、もう一つ例を。1ずつインクリメントするカウンターをSchemeOCamlで書いてみた。Haskellだと無理かな…

[Scheme]

(define counter 
        (let ((x 0))
             (lambda () (begin (set! x (+ x 1)) x))))
(counter)                        => 1
(counter)                        => 2

[OCaml]

let counter = 
  let c = ref 0 in
    fun () -> begin
                c := !c + 1;
                !c;
              end
counter()                        => 1
counter()                        => 2

感想

クロージャを説明するのはなかなか難しいと思った。「計算を表現するオブジェクト」の説明はなんとかなると思うが、「環境を閉じ込めて持ち歩けること」を説明する例を探すのは意外と大変かも。副作用無しだと特に。