orangain flavor

じっくりコトコト煮込んだみかん2。知らないことを知りたい。

「プログラミングHaskell」を読んだ

ちょっと前の記事で宣言したように「プログラミングHaskell」を読んだ。

A5判で本文は185ページと読みやすい分量なのに、小さな関数を1つずつ作りながらしっかりと理解できて良かった。これもHaskellの記述の簡潔さのおかげと言えるだろう。若干説明が足りないところには、すかさず訳注が挿入されており、素晴らしかった。

プログラミングHaskell

プログラミングHaskell

それぞれの章の感想

一通り読んで練習問題もだいたいやった。それぞれの章について思ったところを残しておく。

1章〜6章

これらの章はPythonLispの経験があったおかげで、あまり引っかかるところはなかった。1章に出てくるHaskellでのクイックソートのエレガントな実装によって引き込まれた。

3章での「1つ以上のクラス制約(例: Num a)を持つ型を多重定義型と呼ぶ」という定義は未だにしっくりこない。

4章で出てくるパターンマッチは便利。

7章 高階関数

畳み込みを行うfoldrfoldlは苦手だったけど、練習問題などで使ったことで少し慣れてきた。

関数合成は便利。数学で出てきた時はf(g(x))と連続して適用することに比べたメリットがよくわからなかったが、Unixのパイプみたいに関数を順に適用していく書き方ができて便利さを実感した。

以下は86ページに掲載されているencode関数。文字列に含まれる文字を1文字ずつ数値にして、2進数に変換して、8bitになるよう揃えた後に結合する。

encode :: String -> [Bit]
encode = concat . map (make8 . int2bin . ord)

関数合成によって括弧が減るのも重要。

8章 関数型パーサー

本章のプログラムは動かないと書かれていてちょっと辛かった。

結果的には、原著者のサポートページからコードをダウンロードし、Parsing.hsからParser*1, parse, itemの定義だけ抜き出してきて、failure(+++)Pで囲う形に置き換えたら動くようになった。

failure :: Parser a
failure = P (\inp -> [])

(+++) :: Parser a -> Parser a -> Parser a
p +++ q = P (\inp -> case parse p inp of
                      [] -> parse q inp
                      [(v,out)] -> [(v,out)])

9章 対話プログラム

パーサーが「残りの文字列」という状態を変化させながらパースしていくのと同様に、対話プログラムはIOによって表示や入力などの世界の状態を変化させながら動作する。この意味でパーサーと対話プログラムには共通項があり、10章のモナドの話につながっていくという流れ。

エスケープシーケンスを出力することでコンソールの表示を制御するというのは、これまでほとんどやったことなかったので新鮮だった(積極的に使いたいものではない)。

10章 型とクラスの定義

新しい型を宣言するdataはシンプルに書ける割に強力で便利。

最終的にモナドが出てきた。Haskellと言えばモナドというイメージあるけど、あっさりとした説明だった。

今のところの雑な認識としては、モナドは副作用のあるアクションをdo記法で繋げて手続きっぽく書けるようにするためのクラス。モナドによって何かスゴイことができるようになるわけではなく、(手続き型言語では普通に行われている)副作用を扱うことが純粋関数型のHaskellでもできるようになるのがスゴイという理解。

11章 切符番号遊び

ずっとインタラクティブシェル(ghci)で動かしてきたが、総当りで解くにはちょっと性能が足りなかった。ghcコンパイルする方法をググって実行ファイルを作った。

12章 遅延評価

遅延評価ってなんだか難しいけどいい感じにやってくれてるという雰囲気で使っていたが、式を簡約する時に外側から簡約していき、複数回評価しなくても良いよう同じ式にはポインタを張っておくことで実現できることを学んだ。ちなみに遅延評価のない普通の言語は内側から簡約していく。簡約の順番が得られる結果に影響しないのは、副作用のない純粋関数のみで構成されているおかげ。

無限リストを使ったエラトステネスのふるいの実装も美しかった。

13章 プログラムの論証

Haskellの関数は=で定義されているので、数式と同様に証明できるというのは面白かった。人間が手で証明するのは面倒なので、自動化したいというモチベーションもわかる。

全体的な感想と今後

関数型言語をちゃんと学ぶのは初めてで、いろいろと新しい気付きがあった。Pythonなどの関数型言語の影響を受けた言語をこれまで使ってきたことで、さほど苦しまずに学べたのはラッキーだった。

カリー化やモナドなどのキーワードを聞いても何が嬉しいのかわかっていなかったが、Haskellの世界を構成する上で必要なものだと実感できた。

しかし、Haskellが向く用途がまだよくわかっていない。Haskellのメリットは理解したが、他の言語で書くのと比べてどの程度良くなるのかは、実際にいくつかプログラムを書いてみないとわからないと思う。

そして実際に何か書いてみようと考えると、まだ学ぶべきことはいろいろある。そもそもmain関数の書き方とかモジュールの定義方法の説明とかなかった。例えばWebアプリケーション作るにはどうしたらいいんだろうとか、サードパーティライブラリの使い方とか。モナドについてももっと知りたい。

というわけで、次はとりあえず「関数プログラミング実践入門」を読んでみることにする。手元にあるのは改定される前の版の方だが。

*1:Parserの3つのinstanceの宣言も含む