diff --git a/leopard_syntax.md b/leopard_syntax.md index 76c9c2f..72b1f72 100644 --- a/leopard_syntax.md +++ b/leopard_syntax.md @@ -17,7 +17,7 @@ Reasonって何やろ? https://reasonml.github.io/ によると、 だそうです。作っているのはFacebook、大手ですね安心感がありますね。 なるほどGoogleの言語がGoならFacebookのはReasonなのかもしれません。 -本当のところReasonって何んなのかというと、 +本当のところReasonって何なのかというと、 * 中身は完全にOCaml * BuckleScriptというBloombergで開発されているOCamlのJavaScriptバックエンドを使っている @@ -87,12 +87,15 @@ JavaScriptバックエンドはBuckleScriptもjs_of_ocamlも普通のOCamlから 実際OCamlコミュニティ本流でReasonが話題になることってないし、 みなさんそうなんじゃないでしょうか。 -文法では特にOCamlの構造比較`=`がReasonでは`==`、 -OCamlのポインタ比較`==`がReasonでは`===`になっているのを見たところで -私にはReasonは読めない書けないなと判断しました。ひっかけ問題かよ。 +文法では特に + +* OCamlの構造比較`=`がReasonでは`==` +* OCamlのポインタ比較`==`がReasonでは`===` + +になっているのを見たところで私にはReasonは読めない書けないなと判断しました。ひっかけ問題かよ。 JavaScriptには`==`と`===`があるから、みたいですけど、 Reasonの`==`と`===`はOCamlの`=`と`==`の意味なんだから、 -JavaScriptに寄せたからって意味違うんだしやばくないですかね。 +JavaScriptに寄せたからって全然意味違うんだしやばくないですかね。 私は日頃StackOverflowでOCamlタグがついた未解答質問には出来るだけ解答を つけようと思っているのですが、 @@ -113,32 +116,76 @@ Reasonは関数型言語の文法がどうもという人に受けたのです まあそれだけ紹介してもつまらんですので、読み物として書いていると、 前置きがどんどん長くなりますね。 -## 問題 - -OCamlの文法は醜いとか言われても美的感覚は主観的なものですから、 -特にOCamlの文法に慣れている人には正直どうでもいいです。 -それより、OCamlプログラムをガリガリ書いている時に、あっこれは不便だなー -みたいなところで、文法から来る要素を改善した方がいいのかなと思うんです。 +## 終端が閉じた文法要素、開いた文法要素 -終端を書かなくて良い開いた文法と、 +OCamlには終端を書かなくて良い開いた文法と、 終端を明示しなければいけない閉じた文法要素があるのが わりと美しくないなあと思っています。 -関数型言語的な機能に関する構文は`fun ... -> e`, `let ... in ...`, や -`if .. then .. else ..`など、終端を書かなくて良いLCF MLとかISWIMの -「開いた」文法を採用し、 -他方、命令型機能に関する構文は`for i=0 to 10 do .. done`や +OCamlでは、命令型の制御構文は`for i=0 to 10 do .. done`や `while .. do .. done`などの「閉じた」文法を採用しています。 -さらに、モジュールに関する文法`struct .. end`や`sig .. end`は -今度は閉じています。まあこれはリストなので閉じるのを明示せざるを得ませんが... -開いていたり、閉じていたり、統一感が無い気がします。 +さらに、モジュールに関する文法`struct .. end`や`sig .. end`は閉じています。 -開いた文法には +一方、関数型言語的な機能に関する構文は`fun ... -> e`, `let ... in ...`, や +`if .. then .. else ..`など、終端を書かなくてよいLCF ML由来の「開いた」文法を採用しています。 -Imperativeな機能に関する構文はdo done などの明示的に閉じる文法 -複数の宣言をまとめるモジュールやクラスに関する文法は閉じる +閉じていたり、開いていたり、統一感が無い気がしますね。 +そのあたりに醜さを感じます。 -と、見る人が見ると法則があるのですが、どうも受けがよくありません。 +開いた文法はLCF MLが参考にしたISWIMからのものですが、この文法は、 +できればプログラムを自然言語っぽく書きたい、閉じ記号を書きたくないのですね。 +それに慣れると、閉じ記号必須の文法でプログラムを書いていて +`end end .. end `や`)))))`や`fi; esac`とか書かないといけなくなると +無駄だなあと感じるようになります。 +`for`や`while`とか、OCamlに慣れてくると滅多に書くことはないんですが、 +こいつらの最後に`done`を書かないといけないのは格好が悪い。 +`for i = 0 to n do e` とか`while true do e`でいいと思うんです。 + +ただ、開いた文法には一つ致命的な問題があります。それは閉じる記号が無い分、 +括弧を書かないといけない場合があることです。例えば、 + +``` +map (fun x -> x * 2) xs +``` + +このコードは、`fun p -> e`が`fun p -> e end`のように終端記号が必要な文法だった場合、 + +``` +map fun x -> x *2 end xs +``` + +と括弧なしで書けるのですね。え?余計気持ち悪い?この例ではそうですね。 + +では次の例ではどうでしょう(OCamlのUnixライブラリから引用、自明な整形あり): + +``` +let system cmd = + match fork() with + | 0 -> + try + execv "/bin/sh" [| "/bin/sh"; "-c"; cmd |] + with + | Error _ -> + sys_exit 127 + | id -> snd (waitpid_non_intr id) +``` + +これはよくある`fork`+`exec`の例です。でも間違っていますね。本当は、 + +``` +let system cmd = + match fork() with + | 0 -> + begin try + execv "/bin/sh" [| "/bin/sh"; "-c"; cmd |] + with + | Error _ -> + sys_exit 127 + end + | id -> snd (waitpid_non_intr id) +``` + +と、中の`try .. with ..`を括弧(ここでは`begin`,`end`)で囲まないといけません。 特に開いた文法は閉じる文法から入ってきた初心者が必ず引っかかってバグを出します