これらをどう処理すればよいだろうか? まず、明らかなことは、「関数を評価した結果は、関数がそのまま返ってくる」 ということである。たとえば、(fun x -> 1 + 1) という式を OCaml で評価し ても、(1+1)は計算されず、「関数」が計算結果である。(OCaml は 関数の中身をプリントしないので、計算が進んだのかどうかわからない かもしれないが、実際は、関数の中身に計算できる部分があっても、計算は しない。たとえば、(fun x -> 1 / 0 ) のような式を評価しても、 (1/0) というエラーが起きないことから、「関数に引数を与えるまでは、 関数の中身を計算しない」 ことがわかる。
結局のところ、計算の結果、つまり、「値」の世界に「関数」をいれる必要が ある。
(* 値の型 *) type value = | IntVal of int | BoolVal of bool | FunVal of string * expたとえば、(fun x -> 1) という式は、exp型の式としては Fun ("x", IntLit 1) と表現するが、 value型の式としては、 FunVal ("x", IntLit 1) と表現することになる。
しかし、これだけで良いだろうか?実は、もう少し考えないといけないことが ある。問題点を理解するために、普通の OCaml プログラムで、以下のものを 考えよう。
let x = 1 in let f = fun y -> x + y in let x = 2 in f (x + 3)頭の中で、このプログラムを実行して、答えを予測してみよう。
問題となるのは、変数 x の値が、一番外側でセットされた 1 であるのか、 内側の 2 であるのか、という点である。 まず、一番下の行の (x + 3) における x は、2 である。これは問題がない。 問題があるのは、関数 f の本体 x + y に含まれる x であり、これが 1 なのか 2 なのかである。これが 1 であれば、上記の実行結果は 1 + (2 + 3) = 6 となり、 これが 2 であれば、上記の実行結果は 2 + (2 + 3) = 7 となる。
実は、結果は 6 である。つまり、前者が正しい。ここまでは、 おそらく、多くの人の予想通りだろうが、その理由を正しく言えるだろうか?
プログラム中の変数への束縛がどのように行われ るか、というのは、古くからプログラム言語の意味に関して検討されてきてい る重要なテーマであり、基本的な束縛の方式は以下の2つである。
言葉だけではわかりにくいので、以下のプログラムをもう一度取りあげよう。
let x = 1 in let f = fun y -> x + y in let x = 2 in f (x + 3)静的束縛では、プログラムを書いた時に、スコープが決定される。つまり、 外側の let x = 1 in のスコープは、それ以降全部であり、 内側の let x = 2 in のスコープは、f (x + 3) である。 スコープが重なっている部分では、内側の束縛が勝つので、一番下の行の x は 2である。 一方、関数 f の定義の中にある x は、外側のスコープにのみはいっているの で、その値は 1 であり、OCaml での計算結果 6 が導かれる。
一方、上記のプログラムを動的束縛の言語で実行したらどうなるだろう? 動的束縛では、スコープは、最初から決まるのではなく、実行時に決まる。 すなわち、以下のように実行される。
let x = 10 in let f = fun y -> x + y in let y = f x in let x = 20 in f (x * y)
亀山 幸義