この記事はF# Advent Calendar 2011の20日め、のはずでした。クリスマスに間に合はせるはずが…あれ……?
いろいろねたは考へてたんですが、どれも綺麗な落ちがつかないので初心者らしいねたでお茶を濁すことにしました。
いままで漠然と考へてたことをがんばって整理してみたのですが、基本的には初心者が初心者向けに書いた感じのドヤ顔記事です><
letは何かを束縛する。スマートに!
let a = 10 ;; let b = 20 ;; printfn "%d" (a + b);;
F#のlet
は、他の言語(たとへば、C, Java, Ruby, Python...)の「変数宣言」や「代入」に相当するものだと説明されることがあり、実際に同じようにも使用されます。
F#などの函数型プログラミング言語では、これを「束縛」と呼びます。
代入の場合は「aに10
を代入する」と言ひますが、束縛では「10
にaを束縛する」と言ひます。また、F#では束縛とは別に代入もあり、別物です。
let
は函数に束縛することもできます。
let f a b = a + b ;; printfn "%d" (f 1 2);;
これはみんなが大好きなJavaScriptで書くと以下のコードとほぼ同じです。
var f = function(a, b){ return a + b; }; console.log(f(1,2));
「F#のlet
は変数も函数も定義することができる」と説明されることがありますが、これは正確ではないと思ひます。なぜなら、F#での函数は値でもあるからです。
let f a b = a + b ;; let g = fun a -> fun b -> a + b ;; printfn "%d" (f 1 2);;
このf
とg
は、意味としては同じものです。また、f
とg
に束縛されてゐる「値」のことを函数オブジェクトまたはラムダ式などと呼ぶみたいです。F#のfun
式、つまり函数オブジェクトは、必ず一つの引数をとって、一つの値を返します。
これを統合して考へてみると…
let
で二引数函数を定義できてたように見えてたのは、実は一引数函数二段重ねの糖衣構文に過ぎなかったんだよ! ΩΩΩ<ナ、ナンダッテー!?
引数を二つ以上とる函数定義が、実は二段重ねになった函数への束縛に過ぎない、といふことはfsi
で函数を定義してみて、その函数に引数を一つだけ渡してみると確認することができます。
let
による函数定義は函数オブジェクトへの束縛ですが、実はJavaScriptでのvar foo = function() { ... };
形式の函数定義は、正確には函数定義ではなく函数オブジェクトを生成して代入してゐるので、F#の函数の束縛とよく似てゐます。
まとめると、
let
は値を変数名に束縛する- F#の函数とは函数オブジェクトといふ「値」で、引数を一つとって一つ返す
let
と=
の間に空白区切りで仮引数の変数名を書くと、引数に仮引数の変数名が束縛された函数が返されるlet
で複数の値をとるように見える函数は、一引数の函数の入れ子の糖衣構文(syntax sugar)である
となります。
実はF#を知ったばかりのときはF#の変数とは引数を取らない函数のことではないかと思ってゐました。ひとつの見方としては綺麗に見えてアリなのですが、実際の動きを理窟で考へてみると、事実はどうでせう…?
蛇足ですがついでに、let
は函数への束縛に過ぎないといふことは、函数オブジェクトはそのままで値に適用することができます。
(fun x -> fun y -> x + y) 33 66
letはスコープを作る。エレガントに!
let f = let a = 1 let b = 2 a + b printfn "%d" f
ローカルな変数a
とb
を足し合はせる函数f
を定義します。
これと見た目上(だいたい)等価なコードを、みんなも大好きなJavaScriptで書くとこんな感じになりますね。
var f = function(){ var a = 1; var b = 2; return a + b; }; console.log(f());
JavaScriptの方がちょっと文字数は多いですが、だいたい似たやうな雰囲気があります。
ところでF#はOCamlといふ言語の血を色濃く受け継いで居り、ソースコードに#light "off"
と書くことで軽量構文(lightweight syntax)を無効にし、ML互換モードである冗語構文(verbose syntax)のみで書くことができるようになります。では上のF#のコードを冗語構文で書き換へてみませう。
#light "off" let f = let a = 1 in let b = 2 in a + b ;; printf "%d\n" x
すると、今度はin
といふコードがくっつきました。
これもやっぱり、みんな大好きJavaScriptで、意味的にだいたい等価にしてみると、こんなのになります。
var f = function(x){ return function(){ var a = 1; return function(){ var b = 2; return function(){ return a + b; }(); }(); }(); } console.log(f());
このコードを見てキャー、クロージャカッコイー!
なんて反応をしてくださるのは一部の方だけですね。長ったらしすぎてふつうに気持ち悪いです。自分で書いてて思ひましたが、気持ち悪いです*1。
JavaScriptのfunction
リテラルはスコープを保持してゐて、var
はローカル変数の定義です。
これと上ふたつのF#(≒OCaml)のソースコードは簡潔で、言語としての洗練されっぷりの半端なさがわかります><
let f = let a = 1 in let b = 2 in a + b ;; printf "%d\n" f
と、インデントを深くせずに書くらしい*2ので、F#の軽量構文のインデントとほとんど同様になるようです。
さいごに
F#の軽量構文は書きやすさを重視して設計されてゐますが、函数型プログラミングの学習には「省略」といふ形で概念のわかりにくさに繋がってゐる一面もあります。let
の柔軟な糖衣構文はとてもエレガントでスマートなものですが、その「隠された」意味を探ってみることで函数型言語の理解への一助になるのではないでせうか><
この記事を書き終ってッターンしたあとで、Advent Calendarの27日めにF#初心者による in キーワドの考察:Gushwell's C# Dev Notesといふ記事があることに気がついて心臓が止まるかと思ったのですが、さらに進んだin
キーワードの作用について紹介されてゐて、たいへん参考になります。
参考文献
- //www.amazon.co.jp/gp/product/4774145165/ref=as_li_ss_tl?ie=UTF8&tag=wx310kflashca-22&linkCode=as2&camp=247&creative=7399&creativeASIN=4774145165">実践 F# 関数型プログラミング入門:F#の入門書で、とてもわかりやすくまとまってゐます。私もこれで学びました!
- //d.hatena.ne.jp/hamatsu1974/20090616/1245142607">少年オッカムル - 序 - - 暗号、数学、時々プログラミング:さっき軽量構文について調べてるときに見つけました。まだほとんど読んでませんが、F#/OCamlの勉強にすごく良ささうです!
誰のために書いたの?
ドヤ顔して書いた割にはエキスパートには当たり前すぎる内容で、本当の初学者向けには情報が不足してゐます。初心者向け記述の不足は手間の省略と同時に意図したもので、F#に興味を持った初心者が複数の疑問点について自分で調べ進めて、納得する楽しみを味はってもらへると良いなじゃないかなあと思ふのです。
そんなちょっとした楽しみを覚えていただけた方は、せっかくなのでRuby vs Python! 〜def vs def〜もどうぞ。def
とlet
ってちょっとだけ似てますけど、たまたまでした。