#010 オプショナル引数
suiginto さんの記事に関連して, OCaml でのオプショナル引数の利用に関する二つの制限についてメモ*1.
OCaml のように多相型と関数型を許した型体系でのオプショナル引数には, 意味論を健全にするためなどの理由からいくつかの制限が設けられている. まずは,一つ目の制限について.例えば,
let f x ?i:(i=1) = x + iで定義される関数
f
を考えよう((?i:(i=1)
は ?(i=1)
と書いてもよいが,混乱を避けるためラベル引数の略記法は使わないものにする.)).
これに対し,f 3
の意味は,
i
に相当するラベル引数がないので,デフォルトの値1
が使われて4
- ただの部分適用なので
(fun ?i:(i=1) -> 3 + i)
とほぼ同じ振る舞いをする関数
let f ?i:(i=1) x = x + iと引数の順序を変えて記述すればよい.これにより,
f 3
は 4
を返す.
LISP などを使っている人にとってはオプショナル引数は最後にあるのが自然かもしれないが,
型を静的に与える OCaml では上記の理由からうまくいかないので注意する必要がある.
(OLabl の頃は前者の解釈が行われていたが,意味論がおかしくなるとの理由から OCaml に合流する際に不採用となった.)
--- ここから先の部分に誤りがあるので修正予定.---
二つ目の制限は,オプショナル引数を持つ関数が多相型を返す場合に起こる矛盾を回避するためのものである.例えば,
let apply_3 g = g 3で定義される関数
apply_3
を考えよう.この関数は,(int ->'a) -> 'a
型を持つ.
これに対し,let f ?i:(i=1) x = xという
?i:int -> 'a -> 'a
型の関数 f
を考えると,
f 3
は 3
を返すものの,
apply_3 f
は型エラーになってしまう.これは,f
の型と apply_3
の引数の型を照合する際,
apply_3
でf
を呼ぶときにはオプショナル引数を省略しているから,apply_3 f
はint
型.apply_3
でf
を呼んでも部分適用かもしれないので,apply_3 f
は?i:int -> int
型.
f
や apply_3
の返す型が単相型(あるいは上の解釈を曖昧にしない多相型)であれば,
どちらの解釈かはっきりするために型エラーは起こらない.すなわち,(fun g -> g 3) (fun ?i:(i=1) x -> x)は型エラーを引き起こすが,
(fun g -> (g 3 : int)) (fun ?i:(i=1) x -> x);; (fun g -> g 3) (fun ?i:(i=1) x -> (x:int));; (fun g -> g 3) (fun ?i:(i=1) (x:int) -> x);;はいずれも型エラーを起こさず,前者の振る舞いが可能となる.なお,後者の振る舞いを強制したい場合は,
(fun g -> g 3) (fun x ?i:(i=1) -> x)と引数の順序を変えればよい.
*1:二つ目の制限についての解説は JG 氏からの助言に基づいたものである.