kenokabeさん
kenokabeさんは、自説の組み立て方、参照する先行研究や書籍が普通ではないので突っ込まれやすいにも拘わらず自説が正しいという態度を崩さない、知人だったらわりと面倒なお方だなという印象なんだけど、ここに書いてたことにはそんなに違和感ないな。
http://kenokabe-techwriting.blogspot.jp/2015/04/amazon102-93.html
モナド構文糖
モナドが便利で、モナド構文糖が便利だってことには特に異論がないんだけど、 それならそれでHaskellのdo-returnみたいに逐次実行に似せるほうが絶対にいいと思う。 そうでなければ、もっとこう、bindとかunitとかそういう語彙を前面に出してほしい。
つまり、Scalaのように、for-yieldとかflatMapというリスト中心の語彙でモナドを扱うのは、どう考えてもキモイ。 (LINQのfrom-selectとかSelectManyとかのあれでモナドを扱うのは、さらにキモイ)
という感覚は、Scalaユーザーと共有することはできないだろうなあ……
(2016/7追記)
scala 界隈でも for はちょっと…という意見はたまに見るし、F# の方が筋いいよねみたいな話 rpscala の懇親会でやった記憶ある。
— りりかるろじかる (@lyrical_logical) 2016年7月28日
いやまあ、Scalaのforは最後がmapじゃないといけない縛りとかあって不便なので当然Haskellのdoの方がいいに決まってますよ。 https://t.co/NAt2oVzi35
— 水島宏太(証明力強化月間) (@kmizu) 2016年7月28日
"モナド構文糖 - 猫とC#について書く代わりにHaskellとF#について書くmatarilloの日記 (id:matarillo(https://profile.hatena.ne.jp/matarillo/) / @matarillo) https://t.co/zmh9pxz7cr" については、Scalaユーザとは十分共有できる感覚だと思いますよ
— 水島宏太(証明力強化月間) (@kmizu) 2016年7月28日
lyrical_logicalさんのツイートを見て、お、追記しておこう、と思ったら、kmizuさんもツイートしてた。
Scalaのfor式が不便(Haskellのdoとreturnみたいに独立してない)というのはそう思うんだけど、言いたかったのはそういうとこじゃないんですよね。
どっちかというと
@kmizu Haskellとか知ってる人には、flatMapはbindだしfor式はdo式の不便なものという感じで説明できるし、そういうの知らない人向けには、コレクションをfor式でまず扱えて、それに加えてflatMap, mapを実装した任意の型に適用できるので
— 水島宏太(証明力強化月間) (@kmizu) 2016年7月28日
@kmizu implicit parameter≒型クラスということ、研究関係以外では最初は全然言ってなかったの(今はもう知られてるからそうでもないかもだけど)もそうだけど、小難しい(そうな)概念をそのままの語彙で導入していない点はOdersky先生のバランス感覚だと思ってる。
— 水島宏太(証明力強化月間) (@kmizu) 2016年7月28日
このあたりが感覚を共有できないと思うあたりで。「コレクションをfor式でまず扱えて」まではいいんだけど、そこから「それに加えてflatMap, mapを実装した任意の型に適用できる」には飛躍があるよね、とか、implicit parameterって結局型クラス以外で使わないんだったら他に名前なかったのか、とかなんですよね。Scala(および設計者のOdersky先生)の選択なんで間違いだとかダメだとかイケてないとかDisりたいわけじゃなくて感覚の問題。
遅延評価とfoldr/foldl
Haskellの遅延評価のイメージは「まず式を立てる(サンクを作る)けど、値の評価はまた別の話」。
foldr
は
foldr f acc [x0, x1, x2, x3, ... , xn] = x0 `f` (foldr f acc [x1, x2, x3, ... , xn]) = x0 `f` (x1 `f` (x2 `f` (x3 `f` ... `f` (xn `f` acc) ... )))
こんな感じで、カッコの内側である右から計算するように見える式を立てるというイメージ。
が、評価は遅延評価なのでカッコの外側つまり左から評価するので、そのたびごとにリスト [x0, x1, ... , xn] を1つづつバラしていくことが可能。よって途中で打ち切る場合なら無限リストでも問題にならない。
foldl
は
foldl f acc [x0, x1, x2, x3, ... , xn] = foldl f (acc `f` x0) [x1, x2, x3, ... , xn] = ... ((((acc `f` x0) `f` x1) `f` x2) `f` x3) `f` ... `f` xn
こんな感じで、カッコの内側である左から計算するように見える式を立てるいうイメージ。
が、評価は遅延評価なのでカッコの外側つまり右から評価するので、リスト [x0, x1, ... , xn] は全部バラさないといけないから、無限リストだと計算が終わらなくなるし、有限リストでもメモリをたくさん必要とする(メモリについてはfoldl'
を使うという手があるが)。
貧者の型クラス
去年、こんなのをメモしてた。
- http://haskell.g.hatena.ne.jp/matarillo/20130524/1369369774
- http://haskell.g.hatena.ne.jp/matarillo/20130527/1369636675
これってオダスキー先生のこれ↓がベースなのか!
http://lampwww.epfl.ch/~odersky/talks/wg2.8-boston06.pdf
(追記)というわけで、C#で書いてみた。
OKWaveの質問
Graham Hutton著「プログラミングHaskell」の第8章関数型パーサーを勉強しています。 本の中にあるソースコードではdo記法などが使えないため、動きません。 サポートページにプログラムがあります。そのプログラムの意味がよくわかりません。 教えていただけないでしょうか。
プログラミングHaskellでHaskellを学んだ私が来ましたよ。 OKWaveにアカウントを作るつもりはないですが。
まず、newtypeで新しい型を定義していると思うのですが、右辺のPというのがなにかわかりません。
また、instance Monad Parser whereやinstance MonadPlus Parser whereの意味もわかりません。 その下で、return, >=, mzero, mplusといった関数を定義する意味もわかりません。 よろしくおねがいします。
この本は、コードを写経して動かしながら学ぼうという向きにはつらくて、「第8章 関数型パーサー」の中に >>= とか return とか do記法とかが出てくるのだけど、そこには嘘があって、きちんとモナドとして定義された型でしかdo記法は使えないはずなのに、モナドとして型を定義する話は第8章にはぜんぜん出てこない。
第8章の頭には
type Parser a = String -> [(a, String)]
って書いてあるけど、これではdo記法は使えないのだ。
じゃあどう定義すればParser型でdo記法が使えるのか、というと、それがダウンロードできるサンプルコードでの定義のやり方なのだけど、この方法は第8章まで読んだだけでは理解できない。なぜなら、型クラスの話は「10.6 クラスとインスタンスの宣言」まで出てこないからだ。
その代わり、第10章まで読めば、
instance Monad Parser whereやinstance MonadPlus Parser whereの意味
とか、
その下で、return, >=, mzero, mplusといった関数を定義する意味
については理解できると思う。
とはいえ、
この本にはMonadPlusの説明なんか1行もでてこないけどな!
しかもしかも、サンプルコードには
newtype Parser a = P (String -> [(a,String)])
って書いてあるけど、
この本にはnewtypeキーワードの説明なんか1行もでてこないけどな!
というわけで、質問者のあなたがわからないのも当然ですわ。この本以外を使って自学するしかないです。
それではあんまりなので、少しだけ説明しましょうか。
- MonadPlus
- モノイドの性質をあわせ持つモナドを表す型クラス。ざっくりいうと、足し算ができるモナド。この本のParser型では、パーサー同士を連結できるという意味。
- newtypeキーワード
- dataキーワードに似た、新しい型を定義する文法。Pは値コンストラクタ。dataキーワードとの違いは、newtypeで定義された値コンストラクタは正格なこと。パターンマッチの動きも違ってくる。
とはいえ、ぶっちゃけこのあたりのことは、この本を読み進める上ではわからなくてもいいです。
F#の立ち位置
いげ太さんがときどきぼやいてたかと思いますが(追記: 不正確でした。いげ太さんからのコメントは下に)、「F#はなーんかMSに冷遇されてねえ?そのせいか、C#からF#に乗り換える人も少なくね?」みたいな話があります。
それは正しいといえば正しいのですが、コインの裏表のようにも思われて。
C#は、いろんな機能を貪欲にぶっこんできているわけじゃないですか。LINQみたいな関数型プログラミング由来の機能とか。asyncとか。
で、そのぶっこみかたというのが、割とコンパイラががんばるタイプの、キーワードが増えていくやつじゃないですか。
そんで、その様を「専用の構文をどんどん追加していくと、その言語にはどんどんゴミの山が積みあがっていきます。」と厳しく指摘されたりするわけじゃないですか。
で、なんでC#はそういう(言わば、ダサい)方針なのかというと、言語仕様についてもユーザービリティテストを実施して、その結果がおれらみたいなボンクラにとって難しすぎるとわかると却下していたりするからなんだけど、その中に含まれる1要因として、IDE、というかVisual Studioで開発したりデバッグ時にステップ実行したりするときにわかりやすいかどうか、というのもあるんじゃないかなと。
イメージしてるのはC++のテンプレート機能なんだけど、あれ、エラーメッセージが宇宙レベルでヤバイって話があるじゃないですか。そんな機能、C#には絶対入らないですよ。まああれはコンパイル時のエラーですが……
C#に新機能が入るときは、VSの方も更新しないといけないことが多くて。逆に、VSの方で対応できない(つまり、「わかりやすく」見せることができない)ような機能は却下されたり、違う形に変更されたりするわけで。
F#の言語仕様はそこまでVSにべったりにならないのが、自由で良いところでもあり、C#ユーザーが移行してこないという意味で悪いところでもあるという。
あとはあれだ、VBな。そういうIDEべったりの言語の面倒は2つで十分ですよ!という。(それがしんどくてかどうかはわからないが、Roslynはどちらの言語もカバーするようになっている)
(追記)いげ太さんのツイート。