From 59bbfe8e3984617884cf8288af49de72116aea20 Mon Sep 17 00:00:00 2001 From: Jun Furuse Date: Fri, 1 Dec 2017 13:33:34 +0000 Subject: [PATCH] added leopard_syntax.md --- leopard_syntax.md | 175 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 175 insertions(+) create mode 100644 leopard_syntax.md diff --git a/leopard_syntax.md b/leopard_syntax.md new file mode 100644 index 0000000..2b4ad38 --- /dev/null +++ b/leopard_syntax.md @@ -0,0 +1,175 @@ +# OCamlの文法とか + +# Reasonが流行っているようです + +私、"OCaml"のTwitter検索タブを作って話題を監視しておるんですが、 +今年2017年のOCamlタブではReasonがわりと流行ってました。 +といっても、OCaml界隈本流ではほとんど誰も話題にしないという +変な流行りかた。 +(Reason流行ってないとか言わないでください。OCamlの流行り方と比べると +無視できないくらい流行ってます) + +Reasonって何やろ? https://reasonml.github.io/ によると、 + +> Reasonを使うとJavaScriptとOCamlエコシステムのいいとこどりをして +> 簡単に早く型安全なコードを書くことができます + +だそうです。作っているのはFacebook、大手ですね安心感がありますね。 +なるほどGoogleの言語がGoならFacebookのはReasonなのかもしれません。 + +本当のところReasonって何んなのかというと、 + +* 中身は完全にOCaml +* BuckleScriptというBloombergで開発されているOCamlのJavaScriptバックエンドを使っている +* `npm`でインストールできる事にはじまって、JavaScript周りのツーリングを用意してある +* OCamlの元の文法は捨てて、JavaScriptの文法に寄せてある + +というものです。それ以上でも以下でもないです。まあAltJSの一つですよね多分。 +上記ホームページにはReasonは言語であるとは一言も書いてありません。 +中身は本当にOCamlと何も違わないので言語と名乗るのは流石に気がひけるのでしょうか。 + +OCamlの文法をJavaScriptに寄せてそのままAltJSにしてしまう、というのは +わりとアイデア賞だと思います。これは多分、非純粋で、またあまり型を書かないでよい +OCamlだからできた事なのかなあと思います。 + +Reasonがわりと流行っているのを見ていると、機能や理論をよく知らないから新しいプログラミング言語を触らないのではなくて、単に文法が自分の慣れ親しんでいる物から離れているからという理由で忌避する人がわりといるんだなあと思いました。私はわりと多言語を触っていて、機能が魅力的であれば文法に好き嫌いはあっても拒否感までは持たないようになってしまっていたので、興味深く思いました。 + +この分だと、OCamlの文法をRubyに似せたら案外使う人が出るかもしれませんね。 +ほんまかいな。 + +# 醜い?OCamlの文法 + +さて、このReasonの事が本題ではなくて、ですね。 + +今年のOCamlタブではこのReasonのユーザー様達が、 + +> ReasonはOCamlの文法の腐った文法を良くしたものである + +とか + +> OCamlの文法は見ると目が潰れる + +とか自由に呟いておられたのが観測されました。 +Facebookの中の人は流石にOCamlの文法に慣れ親しんでいる人達でしょうから +ここまで言わないと思いますが、その一方で、どういう宣伝をしたのかな?と思います。 + +そんなにOCamlの文法って醜いですかね。 +OCamlは現役のML実装の中で最もよくオリジナルのLCF MLの文法を守っているんですよ。 +残しているからって綺麗だと言う事ではないんですけど、 +`;;`でREPL入力評価の区切りにしたり、`2,3`みたいにタプルにカッコがいらなかったり、 +`letrec`があったりするのは1973年頃からの伝統なんですよ。 + +とくに`;;`はすぐバカにされるのですが、トップレベルの宣言の終了が明示できない言語で、REPLでは複数行のソースコードをコピペして入力してもそのまま動くものってあまり無いですよね。改行のところに`\`を入れなきゃいけなかったり。MLの授業ではエディタに書いた複数行のコードをREPLに入れて挙動を確かめさせたりするので、複数行のコードを改変なしにそのまま入力できるというのは重要な機能なんです。そうするとMLのような宣言終了を明示しない文法の言語ではどうしても宣言終了の記号が必要になります。それに`;;`を使っているんですね。 +なぜ`;;`かというと、英語の句読点`:`, `;`, `,`, `.` を使いたいが、 +全て出払っているからです。例えば `;` は逐次評価 `e;e` に使われているから +宣言終了には使えません。`.` だと英語だと文の最後に使うものですからちょうど +いいのですけれど、浮動小数点数他に使ってしまっていて使えません。 +(その「点」PrologやErlangは頑張ってますね。No **pun** intended.)。 +となると句読点は全て使い切っていますので一文字では無理ってことになります。 +そうなると式を逐次実行するための区切り記号`;`なのですから +`;;`を逐次実行される宣言の区切りにするのはわりと理にかなっていると思うのですが。 + +# 対岸のReason + +じゃあ逆に私にとってReasonの文法はどうなんだ、と言うと、これが全く響かない。 +OCamlネイティブの人にとっては、慣れ親しんでいる文法をよく知らないJavaScript風に +変えただけだし、言語機能も全く同じなので何の価値もありません。`npm`なんか使わないし +知らんガナ…というのが正直なところです。 +実際OCamlコミュニティ本流でReasonが話題になることってないし、 +みなさんそうなんじゃないでしょうか。 + +文法では特にOCamlの構造比較`=`がReasonでは`==`、 +OCamlのポインタ比較`==`がReasonでは`===`になっているのを見たところで +私にはReasonは読めない書けないなと判断しました。ひっかけ問題かよ。 +JavaScriptには`==`と`===`があるから、みたいですけど、 +Reasonの`==`と`===`はOCamlの`=`と`==`の意味なんだから、どっちにしろ +やばくないですかね。 + +私はStackOverflowでOCamlタグがついた未解答質問には出来るだけ解答をつけようと +思っているのですが、このところ、質問がReasonで書いてあることが増えてきました。 +多分こういうOCamlプログラムなんだろうなとは思っても確信が持てないので +助けてあげられず、せっかく新しいユーザ入ってきてもコミュニティ的に +初めから分断されてるんじゃないかと思います。 + +# OCamlプログラマのための新文法は? + +もしReasonがOCamlの文法の醜さを解決しているとしても、もともとOCaml使っている人に +魅力なかったら、その人たちには意味がないですね。 +Reasonは関数型言語の文法がどうもという人に受けたのですから、 +じゃあ関数型言語の文法に慣れている人のための +新しいOCamlの文法があってもいいんじゃないか、 +OCamlの文法に慣れ親しんでいる人のための文法改良ってできないものかしらん、 +とは昔から思っていて、今年は暇な時にちょっとそういう事をやっていました。 + +## 既存研究: CamlP4 + +実は既にOCamlの文法を変えたものはあってCamlP4のRevised syntaxと言うものが +ありました、が、これは実に使いにくい。たとえば、この見慣れたOCamlプログラム + +```ocaml +let rec fold_left f accu l = + match l with + [] -> accu + | a::l -> fold_left f (f accu a) l +``` + +がRevised syntaxでは + +``` +value rec fold_left f accu l = + match l with + [ [] -> accu + | [ a :: l ] -> fold_left f (f accu a) l + ] +; +``` + +こうなります。何だこりゃ。余分についた`[`と`]`ですが、Haskellの省略可能な`{`と`}`の記号が変わって省略不可になったものと思えば大体あっています。さらに各宣言の最後は`;`が必須です。Revised syntaxは普段使いとして使いやすいOCamlの文法を提供するのが目的ではなく、OCamlのASTを操作するためのDSLとして作られたもので、文法要素の開始と終了を明示していたるところ「閉じた」文法にすることでquasiquoteを使いやすくなる効果があります。OCamlの普通の文法ではうまく書けないクォートがRevisedだと書けたはずです。でもだからってなんでこんな文法を覚えなきゃいけないの、ということで、CamlP4関連以外では全く使われずに終わりました。 + +で、Revised syntaxが面倒だったことをみんな覚えているので、誰もOCamlの新しい文法を作ろうという人はあまりいなかったと記憶しています。 + +## 既存研究: F# + +あ、でもOCamlの外に、OCaml文法の改変として、もう一つありました。 +F# の "light syntax" です。 + + + +昔からOCaml周りの人のジョークに、「文法はHaskellで機能はOCamlのOCamlが欲しい」と言うものがあり度々言われていることですから、OCamlの文法が古びていることは否めません。 + + + +OCamlがHaskellの文法に求めるものというと、トップレベルのletを無くすだとか、type application の順序を後置にする(int list じゃなくて list intにする)とか、なんとかして;;を無くすとか、色々あると思うのですが + + +Imperativeな機能に関する構文はdo done などの明示的に閉じる文法 +Functionalな機能に関する構文はfun ... -> e や let ...などの開いた文法 +複数の宣言をまとめるモジュールやクラスに関する文法は閉じる + +と、見る人が見ると法則があるのですが、どうも受けがよくありません。 + +特に開いた文法は閉じる文法から入ってきた初心者が必ず引っかかってバグを出します + +match + +これはfunctionやwithのケースの中にケースを入れ子にする場合のカッコの付け忘れに関する問題 + +これはinのないトップレベルのletとinがつくローカルのletの違いがわかってないことによる問題です + +これらの問題はちゃんとしたインデント支援があれば簡単に見つかるのですが、初心者の人はそういうツールをインストールするのも一苦労なのでSOに今日もこういう相談が来るわけです。 + +わかっている人でもケース構文が入れ子になったことに気づいてから中のケース公文をbegin..endで囲ってやるのは無駄にカーソルを移動させなければいけないので面倒です。じゃあelispかなんか書いて一発で囲めるようにしろよといつも言っているんですがどうも誰もelispが書けないのかVim使ってるようなので誰も書きません。 + +逆に、インデントから読み取れるプログラマの意図が実際の構文木と違う場合、警告を出すlintを作ると捗ると思って昔一度書いたことがあります。たとえば + +というコードの場合、最後のケースはインデントからするとプログラマは外側のmatchのケースだと思って書いているようだが実際のパース結果ではそうではない。ケースのインデントが一貫してない場合は警告を出すのですね。でこれわりといいかなと思ったのですが書いた本人がそもそもそんな間違いをしないし、lintなんて基本的に誰も使わないし、わりとfalse positiveがあって放置しています。 + +今まで見てきたようにOCamlの文法として + +インデント支援がしょぼい + +といくつか問題があると思うのですが、実はこれらはすべてインデントルール入れたら解決されるんじゃね?と思いつきました。 + +とはいえOCamlの文法をそのままHaskellのようなインデントルールを入れるとそのままF#になってlight構文となんとか構文と2つの互換性のない文法になってしまいます。インデントルールありで書いたコードが旧文法で別のプログラムにパースできてしまう場合があるので、それは避けたいところてます。(これは新文法!みたいなpragmaを書くのはダサい + +