F#とかML系言語の話
最近どこでもF#いいよしか言ってない気もするけれど、なかなかいいですよ、F#。 Microsoftエコシステムの外の人間の方が使いみちが多いというのが面白いところに思う。 Windows使ってる人じゃなくてMacとかLinux上でコマンドラインでなにかやりたい人にマッチしているのが盲点になりがち。 自分がまさにその盲点にはまってたんですが。
MacとかUnixでも動いて、ファイルのmoveとかcopyとかのシステム周りが一通り揃っていて、 新しい圧縮だとか通信だとかにもちゃんと大企業が対応してくれて(.NETがだけど)、 VSCodeのextentionも良く出来ていて、なおかつML系言語。 Unix系コンソールでのML系言語の時代来たな!
とか思っていたら、和良さんに DarkもOcamlからF#の時代ですよ! とか教えてもらって、 リンク先を読んでたらReasonML とか Elm を知る。 以前AltJSとしてどっかで見かけた事はあった気がするが、 その時は好きものがやってるだけのマイナープロジェクトくらいに思っていたけれど、 F#もそんな風に思っていたのに使ってみると意外と使える感じだった。 こいつらも結構いいんじゃないか?とか思い始める。
この辺のAltJS系、それなりに流行ってるんですかね? 自分が知らなかっただけで意外とML系言語の時代来ていた?
F#が普通のプログラマにどのくらい受け入れられるか的な話
流行るといえばどの位一般のプログラマに受けいられるのかが課題。という事で言語的な学習のしやすさを。
F#(というかML)は、表面上はPythonとかとそんなに変わらなくて、見様見真似でもちょっとは書ける。 一方で、いい感じにF#の良さを活かそうとすると、これまでのスタイルとは大きく変えてプログラミングをしないといけない。 例えばF#のパイプ演算子をうまく使うためには関数はカリー化したものを基本としてプログラムしてる方が良くて、 そうするとクラスやオブジェクトでは無くValueを基本にプログラミングをしないといけない。
このプログラムの構成の仕方の変更はそれなりに難しさ、関数型言語の素養を必要とす。 入り口は普通のプログラム言語っぽくても、結局は関数型言語として接する必要はある。 この「関数型言語としてのプログラム構成の仕方を学んで従う」のは、どのくらい普通のプログラマに受け入れられるのか?
あと、F#は割と基本的な所でモナドが出てくる。Option型を扱うのにDSLを作るのが推奨されてるっぽい感じ。 専用の構文があればいいはずなのにそういうのは無くて、 computation expressionというbind系の関数を幾つか実装すると使えるDSLを作る枠組みがあって、それを勧られる。 それにしたって最初からOption用のcomputation expressionで使えるビルダを用意しておいてくれれば、理解しなくてもしばらくは使えると思うのだが、 F#には何故かビルドインではOption用のcomputation expressionのビルダが無いので自分で書かないといけない(6行くらいで書けるけど)。
自分で書くためにはbindってなんだよ、というのを学ぶ必要があり、これはお決まりのモナド入門をやらないといけない。 提供してくれているのを使うだけならもうちょっと後回しに出来るのだけど、 提供してくれてないので言語の入門の割と初期でどうしてもbindの話が出てきてしまう。
教育的配慮もあってそうなってるのかもしれないが、 大多数の人には不要な壁になってしまっている。 F#を使う時の期待値として関数型言語の勉強という側面も持っている気がするので、 初期にbindの必要性に当たるのは悪いとも言い切れないのだけれど。
そもそもbindのシンタックスシュガーってどうなのか?
Bindって今どきのプログラマはどこかでは乗り越え済みなんですかね?
C#はLINQが入った時にみんな頑張ったし、F#も.NET勢なので.NET界隈はなぜかこの辺を普通のプログラマもやる、 という良く分からん風習があった。 でもふと冷静になって外の世界を見渡すと、なんだかんだでモバイルの2大主流言語であるKotlinにもSwiftにもない。 機械学習で主流のPythonにも、Webのフロントエンドで使うJSにもない。
パーサーコンビネータはみんな使ってると思うのだけど、別にbindとか知らんでも使える。
こうして考えると、普通にプログラム言語を使っているとbindのシンタックスシュガーのある言語を触る機会は今でも意外と無さそう。
そもそもF#で初期にbindの例で出てくるOption、先程も言ったとおりちゃんと専用のシンタックスシュガーを導入すればbindのシンタックスシュガーなんて要らないんですよね。 実際KotlinはOption(Nullable)周りに専用のシンタックスシュガーがいろいろ入っていて、 bindのシンタックスシュガーなんて無くてもむしろF#より快適に書ける。
Computation expressionの枠組みでいろいろな物を汎用的に美しく書けるとは言っても、良く出てくるケースはだいたい決まってて、 それ独自のシンタックスシュガーを入れれば特に問題は無い。 Asyncとawaitもcomputation expressionで美しく書けます、と言われても、別にsuspend関数で問題が無い。 専用の構文を入れるのはダサいかもしれないけれど、どうせ似たような仕組みに落ち着くのだから使う側的に違いは無い。 理論的に美しくないだけで学習コストを大きく下げられるのだから、そっちの方が良いのでは?という気もする。
Computation expressionは新たにDSLを作りたい時には強力な仕組みとなる訳だけど、 if elseのショートカットとかをちゃんと実現しようとすれば遅延評価みたいな仕組みも必要になってきて(F#ではDelayというのでこれを行う)、 そんなにシンプルで美しいという訳でも無い。 でもそうしたフルな機能が要らないなら別にKotlinのようにインライン周りの工夫が入っているだけで十分だったりする。 むしろreturn周りなんかはKotlinの方がシンプルに書ける事も多い。
いろいろなDSLをしょっちゅう作って、それがかなり汎用な言語機能を要求するならbindのシンタックスシュガーがある方がいい。 でもそういうのって一部の人だけがやればいい気もするんですよねぇ。 一方でF#とかの良さを享受出来る人というのはもっとたくさん居る訳で、 もっとそういう多数派に向けてシンタックスシュガーいろいろ入れてbindに触れずに済む範囲を増やした方が流行るんじゃないか。
Kotlinとかってまさにそういう言語で、algebraic typeの良さとかは受け入れつつ、 汎用だが全てを含めると複雑になるbindのシンタックスシュガーでいろいろ統一せず必要に応じたシンタックスシュガーを入れて便利に書ける。 これでいいというのが現在の答えなのかね。
一方でKotlinみたいな言語だとオブジェクトが多くなりがちで、algebraic typeをガンガン使ったモデリングというF#の教義(というかたぶんML系言語の教義なんでしょうが)に従わない事も多い。 そもそもAndroidはActivityとかViewがオブジェクトで作られていて、 それらとの相互作用がプログラムの多くの部分を占めるので、そうなるもっともな理由もある。
ただそういう日々をすごしているとfunctionalなスタイルを学ぶ事は難しくて、 いろいろ切り離してpureなドメインモデルの世界を構築してML的に書ける時でもそれを行わない事が多く、 関数型言語の教育的側面としてはよろしく無い部分もある気もする。
などととりとめも無く書いてきたけど、以前Idrisの話をしたり Mini OCaml動かしたりしていたjmuk的にはなにかコメントはありませんかね?