Matthew Griffithさんのブログ記事 MECHANICAL ELEPHANT - Becoming Productive in Haskell comming from Pythonの翻訳です。そういえばProductive Programmerって本にもHaskellを使って実証実験する話が出てました。Haskellは何度も勉強しようとして途中で止まっては忘れを繰り返しているので見習いたいと思います。
最近になってようやく私は生産性を高められるぐらいHaskellに熟達してきました。そこでHaskellを学習してきた経験について、それらを忘れてしまう前に私の考えを書き留めておこうと思います。今や私はWebプロトタイピングのほとんどをHaskellで行っています。まだ普段はPythonを使い、また楽しんでいるにも関わらず、です。
Data First(データが第一)
これは動的言語から静的言語に移行したときに思っていたより大事なことでしたが、しかしHaskellの場合はデータ構造はデータの宣言と型シグネチャでほとんど決まります。Pythonの場合はコード本体内に埋め込まれてしまうのですが。
私がHaskellの関数について最初に考えることは「このデータはどう見えるか?この関数は___を引数にして___を返すよな?」でした。一方Pythonの場合は「このコードは一体何を言っているんだ?」でしたね。
この「データが第一」という考え方は私のコーディングをよりよいものにしました。Pythonに戻った時においてもそうです。データの構造が「簡単だったから」以外の理由で変わるとき、よりしばしばそれを認識しましたし、そういうときは私は問題に対してすごく「ズームイン」できていました。
データ構造の変更に制約をつけることはコードの複雑さを減らし理解しやすくします。
The Readability(読みやすさ)
私がPythonを使う主な動機はコードの読みやすさです。Haskellは注意深く手作りされた例題として見えるもの以外は最初すごく汚く見えました。部分部分は非常にすっきりしていましたがなんだか無意味なガラクタに囲まれていたのです。しかしそれは明らかに強力でした。
私は明白に、強力だけど混乱のもとになる「お利口」なコードは避けたかったのです。
ですが、私の「読みやすさ」の査定能力は他の命令形言語を査定してきて培われたものです。北京語の読みやすさについて英語圏の人間が批判するのとちょっと似ていますね。
Haskellは「お利口だけど人を誤解させるような」ものではありませんでした。もちろん「お利口」なコードは他の言語と同じようにHaskellでも書けるのですがそれは一般的なケースではありません。
実際、Haskellでは「お利口なコード」は単に本当に型システムで制約されたお利口なことをあれこれ実行するだけです。もし「Intを返す」と書いてあったらその関数はちゃんとIntを返すかコンパイルに失敗するだけです。
Haskellが提供する、より強力で精密な抽象化機構はときどき魔法の香りがします。私はPythonではそういう魔法は避けるようにしているのですが。
No, Really, the Readability(いや、ほんとに読みやすさについて)
最初に、やはり、他の人はHaskellをある基礎に則って問題なく読みこなしているということをある程度自分に信じこませる必要があるでしょう。一度乗り越えてしまえばHaskellは私にとって非常に読みやすくなりました。
1. 型シグネチャ。それはちょうど本の章の先頭に小さな「まとめ」がついているようなものです。おまけの特典としてそれは「正しさ」を保証してくれます。もしあなたが他の言語を習得しようとするときにそういうものがあったらすごくいいんじゃないでしょうか?
これはTommyが市場にアヒルを買いにいくという章です
chapter :: Tommy -> Market -> Duck
2. 他の関数から合成された関数やより小さな関数は複雑さを大いに軽減します。それらがうまく名前付けされていれば読みやすい関数を書くことができます。
3. 簡潔であること。強力なアイデアを表現するのに山盛りのコードを書く必要はありません。
Infix Symbols and Noise(中置記号とノイズ)
Haskellでは一般的に使われている中置関数についても一言言いたいです。中置関数/演算子は2つの引数の「前」ではなくて「中」に置かれる関数です。古典的な例としては + が足し算を表すなどですね。
Haskellではいくつかの中置記号はよく使われます。例えば$, <$>, <-, ->, など。
そしてそれらを使って新参者の絶望/怒りを呼び起こす記号の数々を生成することもできます。
しかし絶望しないでください。誤解を招くようなお利口さに繋がるきらいはあるものの中置記号で一般的に使われるものの数は限られています。一度わかってしまえば使いやすくて簡単なことが納得できるでしょう。
私は通常の場合、だいたい5個ぐらいの中置記号を使っていると思います。
とはいうものの、初期の段階ではlens libraryは避けるのが賢明だと言っておきましょう。なぜなら中置記号がてんこ盛りなんですよね。
lensは非常にクールなライブラリですが使わなくても問題ありません。Haskellでそこそこ大きいプログラムが書けるようになってから暇な時に試してみるのがいいでしょう。
A Whole New Vocabulary.(新しき語彙の世界)
Haskellを勉強していると全く新しい単語を覚える必要にたくさん出くわすでしょう。例えばファンクタやモナドです。
それらの単語はいくつかの理由から勉強するときには「重たく」感じられるでしょう。命令形言語を学び始めたときにもものすごく多くの新しい語彙があったが多少なりとも馴染みやすさがありました。ループという単語で想起されるもの…そう、ループ。レース場のトラックやローラーコースター…あとうーん、例のシリアルとか。
我々が何か記憶するときは以前に記憶したものに結びつけて記憶しようとします。もしあまりにも多くの重たい単語が文章中に出てくると脳が拒絶してしまいがちになります。私は_ファンクタ_なんて単語から何も連想できなかったので記憶するのが大変でした。
私は次のようにしてそれらの単語を覚えました。つまりその重たい単語が出てくるたびに私にとってわかりやすい、心理的に置き換え可能な私独自の呼び方をするようにしたのです。しばらく経つとその独自同義語がしっかり心に刻まれて「重たい単語」は何の問題もなくなりました。
例えばファンクタ。
Haskellにおいてはmapが可能な何か、です。例えばリストはファンクタです。このことは何かの関数を引数にとってリスト中の全てにそれを適用し新しい結果のリストをつくるマップ関数があるということを意味します。
map (+1) [1,2,3,4]
-- results in [2,3,4,5]
そこで私は最初ファンクタを「マッパブル(マップ可能なもの)」と読み替えることにしました。マッパブル、は私にとって覚えやすかったですし何をするものかより明確に記述されているように思えました。リストはファンクタ。リストはマッパブル。
My Trusty Print Statement(頼みの綱のPrint文)
Pythonでは私の主な開発ツールはprint文/関数でした。
Haskellでは私の主な開発ツールは型システムです。それで今までprint文で普段チェックしていたもの~ある関数がどういうデータを受け取るか、また返すか~はチェックできます。
しかし!Debug.Traceを使えばHaskellのIO型に悩まされることなくPythonのprint関数のようなことができます。Haskellを始めるときにはこれにはすごく重宝します。しかし一度Haskellへの移行が済んでしまえば思いの外、この機能は使わなくなるでしょう。
もしデバッグ完了後もtrace文をあなたがコードに残しているようなら…Pythonで同じことをやるよりもHaskellでやるほうがより汚く思えるようになるでしょう。
The Best Monad Tutorial(ベストなモナドの教則)
はParsec Tutorialでした。
もしあなたが、誰かがHaskellで生産性が高くなったという話を聞いたら大抵はその人がどうやってついにモナドを理解したかというお話がついてきます。よし、じゃあ話しましょう。
私にはパーザを書く必要がありました。既にPythonで書いた何がしかのコードはあったのですが私のパーザプログラミングの経験不足から複雑さがどんどん膨れ上がっていき、開発速度は見る見る落ちていったのです。
そこで延長戦に持ち込みました。ここはHaskellでやるべきなんじゃないかなあと考えたわけです。
私はYouTubeのビデオ、Parsing Stuff in Haskellを見つけました。そこではParsecライブラリを使ってHaskellでどうやってJSONパーザを書くかを説明しています。
だがそのビデオはそれとなくモナドやApplicativeの使い方を私が必要としているものを作るためのツールとして紹介していたのです。どのようにそれらが機能するか、またお互いにどう関連しているかを。
モナドなどを使ってパーザを書いた後になって、私はそれらを使った他のコードを理解でき始めました。それから私はモナド等の抽象的な性質について勉強し始めたのですが…抽象化についてのレッスンはまた別モノです。最初にやる必要はありません。
また、Parsecは私のパーザープログラミングの経験不足が実は大した問題ではないほど十分なしくみを提供しています。実際、Haskellを勉強し始めたところだった私が、どの尺度からみても、長年Pythonを使ってきたがパーザのノウハウを持っていないプログラマであった私が書いたものよりずっとよい(低い複雑さ、より高速、より読みやすい、拡張も簡単)パーザを書けたわけですからね。
The learning process was incredibly rewarding(学習の過程は信じられないほど実りのあるもの)
Haskellはいくつかの理由から私にとって主要なWebプロトタイピング用言語です。
まず、理由その0は私は自分で使うテクノロジーを自分で選べたということ。これは非常な贅沢であることは理解しています。
- 私はプロトタイプをより素早く書くことができました。そして大抵の場合プロトタイプはそのまま製品版になります。
- つまらないバグに時間を取られなくなりました。
- 私が遭遇したバグは概してより有意義で他の問題を理解できるように導いてくれました。註:有意義というのがいつも大変である、ということを意味するわけではありません。
- Pythonは私に実行速度についてあまり心配してもしょうがない、と教えてくれました。Haskellもそれは同じですが速い実行速度が勝手についてきます。
- リファクタリングは息をするのと同じ。Pythonでは常にいちいちうるさいという感じがしていくつかのコードの小さい部分を変更し忘れ、それがあとから効いてくるものです。
- すばらしいライブラリ群がある。Haskellが持つ基本的な保証機構はライブラリ群の標準的な品質を並外れて高くしているように思えます。いくつか私にとって「ゲームのルールを変える」ライブラリ群がありました(ParsecとQuickCheckがまず思い浮かぶが他にもいろいろある)。
- 助けになるコミュニティ
- マルチコアを使った場合に容易にコードをスケールアップできること。
- Haskellのインフラは常に改良され続けています。昨年、GHC(Haskellコンパイラ)7.8がリリースされましたが、それによってWarp(元々相当高速だった素晴らしいwebサーバー)の性能が倍になりました。
最後に、Haskellのコードを書くということは深いレベルの満足感を与えてくれると言わせてください。これは私の今までの他のどのコーディング経験よりも実りのあることでした。
Where to start?(どこから始める?)
どこから始めればよいか、の見つけ方を書くのはなかなか大変ですが…。
ここではもし私がHaskellをもう一度はじめから学習するとしたらどうするだろうか、ということを書きます。
最初に少なくともLearn you a Haskell for Great Good.(すごいHaskell楽しく学ぼう!)の第1章から第8章までは読んでおきましょう。
で!
- **IOモナドについて気にしなくていい小さなモジュールを書いてみましょう。**例えば数独の問題を自動生成するようなモジュールです。乱数をseedに使うことについては気にしない。何が起きているかDebug.Traceをprint文代わりにして見てみること。数独の問題を生成してDebug.Traceで画面に出してみましょう。何か独自のデータ型を作って関数だけ使ってみる(例えば型クラスなんかは使わない)。
- ScottyかSpockを使って**それをWebサイトとして展開します。**単純に、あるURLをアクセスすると生成された数独の問題を表示し、あるURLをアクセスすると数独問題のJSONを生成するようにしましょう。
- 本当のIOでグダグダになりましょう。数独問題をDebug.Traceなしでターミナルに表示してみる。
- それに段階的にあれこれ追加していく方法を見つけます。数独問題のファイルフォーマットを設計し、それ用のParsecパーザを書きましょう!ファイルフォーマットはJSONのままにしておかないで…何かもっと凝ったものに。
幸運を祈ります!