プログラミング言語SML#解説 4.0.0版
19

19.10 関数適用式 appexp atexp

以上に定義した原子式は,構文自体に式の区切りを含んいる式の最小単位 である. 式はこの原子式を式構成子によって組み合わせて構成されている. 組み合わせの中で最も結合力が強い式構成子が,関数適用である. ラムダ式の伝統を引き継ぐSML#では,関数適用は,文法 appexp atexp に示すように,関数を表す式appexpと引数を表す式atexp を単に式を並べて記述する. exp1 exp1 exp3のように連続して 並べられた式は,この文法から,左結合する関数適用のネスト ((exp1 exp1exp3) と解釈される.

関数適用式appexp atexpの評価は以下のように行われる.

  1. 1.

    appexpを静的に評価し,型を求める. もし型が関数型ty1 -> ty2でなければ, 型エラーとなる.

  2. 2.

    atexpを静的に評価し,ty3型を求める. ty1ty3が同一でなければ型エラーとなる.

  3. 3.

    appexpを動的に評価し,値Clsを求める. 値は関数クロージャである.

  4. 4.

    atexpを動的に評価し,値vを求める. 関数クロージャClsに保存された環境に,仮引数の値vへの束縛を追加し, その環境の下で,関数クロージャClsに保存されたコードを実行し,動的な値vを求める. 型ty2vが,関数適用式の型と値である.

以下に関数適用式を含む例を示す. 最後の例では,#name f("joe", 21)((#name f) ("joe", 21))と解釈され,型エラーとなる.

# val f = fn x => fn y => fn z => (x,y,z);;
val f = fn : [’a. ’a -> [’b. ’b -> [’c. ’c -> ’a * ’b * ’c]]]
# f 1 2 (3,4);
val it = (1,2,(3,4)) : int * int * (int * int)
# fun f (x,y) = {name=x,age=y};
val f = fn : [’a, ’b. ’a * ’b -> age: ’b, name: ’a]
# #name (f ("joe", 21));
val it = "joe" : string
# #name f("joe", 21);
(interactive):5.0-5.6 Error:
(type inference 028) operator and operand don’t agree
operator domain: ’BCUJ#{name: ’BCUI}
          operand: [’a, ’b. ’a * ’b -> {age: ’b, name: ’a}]