let が再帰でない理由というかメリット
そうすると本当に rec の存在意義を疑うしかなくなる。もし何らかの理由で付いてると嬉しいのだとしても、現代的な言語だったらプログラマが怠けられるようにそういうのは排除するべきじゃないでしょうか。納得できそうな理由があったら教えてください。
OCaml の再帰関数 -
はいお教えします。再帰で書きたくない時もあるので、デフォルト再帰だと困るからです。ある identifier を、その値を使いつつ再定義する際に必要です:
let _ = let verbose = ref false in Arg.parse [ "--verbose"; Arg.Unit (fun () -> verbose := true); "verbose mode"; ... ] (fun s -> ...) "command [options]"; let verbose = !verbose in ... if verbose then prerr_endline "debug message";
上の例では Arg.parse を使って、コマンドライン引数をパースしてます。Arg.parse を知らなくても大体判るでしょう。"--verbose" ならば bool ref の verbose を true にします。
このリファレンスはコマンドのパースが終わればそれ以上上書きされることは無い事がわかっているので、reference を参照しちゃって定数にしたい。そうすれば、その後間違って誰かが上書きすることもありませんし、いちいち参照するときに !verbose を書かなくて良い。なので、
let verbose = !verbose in
と、リファレンス参照した上で、結果を同じ identifier に束縛しています。こうすればより簡単な上に、 (リファレンスセルがエスケープして無い限り) 間違って元のリファレンスセルを参照できなくなりますからより安全。let が再帰がデフォルトだとするとこういう定義は書けません。どうしても別の identifier を使わなければならないので、元のリファレンスセルも参照できたままになってしまう。名前が面倒だし、リファレンスを隠せないので、不便かつ不安全:
let _ = let verbose_ref = ref false in Arg.parse [ "--verbose"; Arg.Unit (fun () -> verbose := true); "verbose mode"; ... ] (fun s -> ...) "command [options]"; let verbose = !verbose_ref in (* do not touch verbose_ref any more! *) ... if verbose then prerr_endline "debug message";
こんな感じになっちゃいます。まあ、
let verbose = let verbose = ref false in Arg.parse [ ... ] ...; !verbose_ref in
でもいいんだけど、値が複数あるとあまりスマートではないかな。
コマンドオプションや ref に限らず、同名 identifier を使って元の値を隠し、新しい値で identifier を再定義したい事は良く起こりますよ。
let がデフォルト再帰じゃないのでできるテクニックというか、テクニックがあるから再帰デフォルトじゃないのか、どちらが先か良く判りませんが、OCaml の文化の一つとでも思ってください。ある日突然、これから let はデフォルト再帰ですよ、と言われたら少なくとも私はフザケンナと突っ込みますね。便利だモン。
追記 2011/05/11
この例は判りやすいと思って提出したのだが、良くなかった。Shadowing は ref への間違った代入、つまり意図せぬ副作用を防ぐために*しか*使う意義がないと勘違いされる可能性がある。そうではない。下の kwakita さんのコメントにもあるように pure な状況でも一時変数束縛を shadowing によって更新していくと、古い束縛に誤ってアクセスすることを禁止する事が出来、っ便利である。つまり、Haskell の様な pure な言語でも let/let rec の違いがあれば便利かもしれない局面はあるのだ。Shadowing を使わないとウザイ例は実際散見される。 ghc-6.12.3/libraries/Cabal/Distribution/Simple/NHC.hs 。これは bind の例なので名前を変える必要も無いのだが、 conf, conf', conf'', conf''', conf'''' などと書かれている。これは、 OCaml に慣れている人が見ると、噴飯物である。