ラムダ式(匿名関数/無名関数)

F#ではラムダ式(lambda expression)または関数式(function expression)(1.1)と呼ばれる 関数を表現するための式があり、 匿名関数または無名関数(anonymous function)とも呼ばれます。 まずは、ラムダ式の構文をみてみましょう(6.3.7)(より正確な構文はページ下部)
function expression
fun pat1 ... patn -> expr
funがラムダ式が始まることを示すキーワードで exprは様々な式のこと、つまり関数の本体です。 pat1 ... patnは関数に渡すパラメータのことで 後に説明する様々なパターンが使えます。 このパラメータは引数(ひきすう)と呼ばれることもあります。 C言語などの関数とは異なり、ラムダ式は常に引数を持つため 引数は最低でも1つは必要です。 パターンには様々なものがあるので 個別のパターンが出てきた時に別途説明することにして このページでは、基本的な使い方を説明します。
1を足す関数
> fun x -> x + 1;; //注意。+と1のスペースがないと別の意味
val it : int -> int = <fun:clo@20-12>
これは、入力されたものに1を足す関数です。 引数はx一つだけで、x+1が関数の本体部分です。 また、結果の型について、 関数というのは、「入力」と「出力」との対応関係であるので 関数の入力値に対応した型 -> 関数の出力値に対応した型という風に表記され、 これ全体が関数の型を表します。 つまり、int -> intという型は 「int型の数値を入力として受け取り、int型の数値を返す」関数を表す型ということになります。 コンパイルされた際は入力値に対応した型をty1,出力値に対応した型をty2とすると Microsoft.FSharp.Core.FastFunc<ty1,ty2>という型になるそうです(5.1.1) 次は関数に値を適用させてみます。 関数を適用するための構文は次のようになります(6.4.1)
関数適用の構文
expr expr | expr(expr)
この通り、単に式を並べるだけになっています。 右側を括弧でくくった場合は、右側を先に計算するという意味になります
関数の適用
> (fun x -> x + 1) 10;;	//OK
val it : int = 11
> fun x -> x + 1 10;;	//fun x -> x + (1 10)と解釈された
  fun x -> x + 1 10;;
  -------------^
stdin(22,14): error FS0003: This value is not a function and cannot be applied
気をつけるべき点は、式全体を括弧で囲む必要がある点です。 下の例では、変なところで 関数適用しようとしているラムダ式と見なされてしまい エラーが起きてしまっています。 引数に関しては、 funの側にあるxと 実際に与える10の2種類がありますが 前者を仮引数、後者を実引数と呼び分ける場合もあります。 また、関数に値を適用した結果の値のことを 返り値または戻り値(return value)と呼びます。 C言語などにあるようなreturn文(明示的に返り値を指定する構文)というものはなく 関数本体部分の一番最後の式の値が返り値になります。

色々な例

引数の名前は、関数自体の意味には影響を与えません。
引数の名前
> fun y -> y + 1;;	//上で説明したものと全く同じ関数
val it : int -> int = <fun:clo@23-13>
> (fun y -> y + 1) 10;;
val it : int = 11
> fun longname -> longname + 1;;	//これも同じ関数。長い名前でもOK
val it : int -> int = <fun:clo@24-14>
> (fun longname -> longname + 1) 10;;
val it : int = 11
関数は入力と出力の対応関係であるので このように引数の名前は何にしても問題ありません。
引数が2つの関数
> fun x y -> x + y;;
val it : int -> int -> int = <fun:clo@27-15>
> ((fun x y -> x + y) 10) 20;;
val it : int = 30
> (fun x y -> x + y) 10 20;;
val it : int = 30
今度は引数が2つの関数です。 結果の型を見るとint -> int -> intとなっています。 これは 1つめの入力(int型) -> 2つめの入力(int型) -> 出力(int型) のように見ることが出来ます。 関数を適用する際は、2番目の例のようにせずとも 3番目のように単純に並べるだけでOKです。 3つ以上の場合も同様です。
型変数
> fun x -> x;;
val it : 'a -> 'a = <fun:clo@30-16>
今度は、入力されたものをそのまま返す関数なのですが 結果の型に、見慣れない'aというものが出てきました。 これは型変数(type variable)といい、(1.1) 様々な型を抽象的に表したものです。 関数を呼び出す段階になると 入力がint型の値ならば'aはintに置き換わり 入力がstring型の値ならば'aはstring型に置き換わります。 これまでの例で型変数が出てこなかったのは、 + 1などと書いてある部分から 整数(int型)を対象にした関数だとF#が自動的に判断したためです。 (1はint型であり、+は片方がint型であれば、もう片方もint型だと判断出来るため) 実際に値を適用してみると
多相的な関数
> (fun x -> x) 10;;
val it : int = 10
> (fun x -> x) "hello";;
val it : string = "hello"
> (fun x -> x) true;;
val it : bool = true
このように、入力に応じた結果が帰ってきます。 'aのような型変数を持つ関数の事を 多相的(polymorphic)な関数と呼ぶこともあります。 上のケースでは入力にも出力にも同じ'aが使われているので 「入力と出力の型は常に同じ」という事を意味します。 複数の型変数が現れる場合は 必要に応じて'b、'c、...といった別の名前が使われます。 型変数を持つ場合でも、 適切に型注釈を付ければ、型変数を持たない定義にすることが出来ます。 (型注釈は、このように個別の引数ごとのパターンにも使えます)
型注釈
> (fun (a:int) -> a);;
val it : int -> int = <fun:clo@66-20>
今度は、引数に関数を取るようなものを作ってみます。 (これには制限があるのですが、letでの関数定義で説明します)
引数に関数を取る関数
> (fun x -> x 10);;
val it : (int -> 'a) -> 'a = <fun:clo@37-18>
> (fun x -> x 10) (fun y -> y + 1);;
val it : int = 11
> (fun x -> x 10) (fun x -> x + 1);;
val it : int = 11
> (fun (x:int -> int) -> x 10);;
val it : (int -> int) -> int = <fun:clo@40-19>
この関数の型は (int -> 'a) -> 'aという風に括弧がついています。 わかりやすくするため、 注釈を付けて全てint型にしたのが最後の例です。 (int -> int) -> int というのは 「(int -> int型の関数を入力)として(int型の値を返す)」関数を表す型 という意味になります。 上のほうで出てきたint -> int -> intは引数が2つだったのに対し 今回は括弧がついているので引数は1つだけです。 また、二番目と三番目の例からもわかるように 左側のfunと右側のfunの引数の名前の間には 特に関連性はないので、同じにしても問題ありません。 funの引数は、その本体の中でのみ参照出来るスコープを持ちます。 ただし、fun a -> (fun b -> body)のように入れ子になっている場合は bのbody部で外側のaを参照することが出来ます。

引数の無い関数のエミュレート

C言語などの多くの言語では、引数の無い関数も定義出来ますが F#ではそういうものは定義できません。 しかし、慣例的にunit型の値である()を使うことで 同様のものを表現します。
引数の無い関数のエミュレート
> fun () -> 10;;
val it : unit -> int = <fun:clo@34-17>
> (fun () -> 10) ();;
val it : int = 10
この関数は()の入力に対して常に10を返す関数です。

型の見方

(修正しました) F#では、関数が2つ以上の引数を持っていても 全ての引数を使わずに一部だけ適用することも出来ます。 その場合
A
int -> int -> int -> int
このような型は、
A'
int -> (int -> (int -> int))
このような型であるとみなすことが出来ます。 (->を演算子とみなすと、右結合のような動作) (右結合という用語については、演算子の優先順位のページを また、一部の引数の適用については 関数型のカリー化の章も参考にしてみてください) AとA'の例の2つの型が同じ型かどうかは 情報が見つけられないのでちょっとわからないのですが 単純に引数をfunで置き換えただけでない場合は 表示上は別の種類の型として表示されるようです。 意図的な例ですが
括弧付きの型の表示
> let f x y = x + y + 1;;
val f : int -> int -> int
> let g x = let a = 1 in fun y -> x + y + a;;
val g : int -> (int -> int)
実際の所、fとgは、使う側から見れば同じように使えます。

function expressionの正確な構文

最後にラムダ式の正確な構文(6,7)の抜粋を載せます。
function expression
fun argument-pats -> expr argument-pats:= atomic-pat ... atomic-pat atomic-pat := pat ただし、patは const,long-ident,list-pat,record-pat,array-pat,(pat), :? atomic-type,null,_の形であるものに限る