無環境 (envless) という単語は一般的な単語ではなく、 私は "環境の無い (environmentless)" の略記として使いますが、 その意味をあなたは問題なく想像できるでしょう。
無環境関数は、この関数の呼び出しを実行するオブジェクトコードが配置されているコードセグメントを指し示すポインタによって表現されます。 C言語において、全ての関数は無環境です。 クロージャ関数もまたポインタで表現されますが、 そのポインタはヒープ中の(実行時に)割り当てられたタプルがある場所を指し示しています。 通常、このタプルの1つ目の要素は無環境関数を表わすポインタで、残りの要素は束縛群を表わしています。 そのようなタプルはしばしばクロージャ関数もしくは単純にクロージャと呼ばれ、 環境とペアになった無環境関数であると考えることができます。 クロージャ関数の環境が空であっても問題ありませんが、 これは無環境関数のクロージャ関数と同じではありません。 ML や Haskell のような関数型言語の全ての関数はクロージャ関数です。
次の例では、型 (int) -> int が割り当てられた関数 sum は1から与えられた自然数までの全ての整数を合計します:
fun sum (n: int): int = let fun loop ( i: int, res: int ) :<cloref1> int = if i <= n then loop (i+1, res+i) else res // end of [loop] in loop (1(*i*), 0(*res*)) end // end of [sum]
もし構文 :<cloref1> を単独なコロン記号 : に置き換えたとしてもコードは型検査を通りますが、loop をC言語のトップレベル関数にコンパイルできないことを示す警告もしくはエラーを引き起こす可能性があります。 この警告/エラーの理由は、トップレベルでもなく loop 関数自身の引数でもない値 n が loop の本体に含まれていることにあります。 n を元の関数への追加の引数として loop を無環境関数にするのは簡単です:
fun sum (n: int): int = let fun loop ( n:int, i: int, res: int ) : int = if i <= n then loop (n, i+1, res+i) else res // end of [loop] in loop (n, 1(*i*), 0(*res*)) end // end of [sum]
関数が直接適用されない時でも、しばしばクロージャを作る必要があります。 例えば、関数呼び出しの返値もまた関数であるかもしれません。 次のコードで定義された関数 addx は与えられた整数 x に適用した別の関数を返します。 そしてこの返される関数はクロージャ関数で、 常に自分の引数に整数 x を加算します:
// fun addx (x: int): int -<cloref1> int = lam y => x + y // val plus1 = addx (1) // [plus1] is of the type int -<cloref1> int val plus2 = addx (2) // [plus2] is of the type int -<cloref1> int //
クロージャはしばしば高階関数と呼ばれる関数への引数として渡されることがあります。 クロージャがデータ構造に埋め込まれることも一般的です。