拙書「Haskell 教養としての関数型プログラミング」の紹介
2017年4月に出版された拙書「Haskell 教養としての関数型プログラミング」を、紹介する。
この記事について
2017年アドベントカレンダー「Haskell(その4)」の12月24日の記事です。ノリで登録してしまい、おおいそぎで書いた記事です。できれば、また、修正等できたらと考えています(とくに、「他の入門書」の項目は、もう一度、それぞれの入門書をよく読んで、まとめなおしたい)。
これは何の本?
Haskellの入門書だ。ハンズオン形式でHaskellを身につけられるよう、構成してある。「プログラミング自体、はじめて」という人でも読めることを、目指した。僕自身にとって「理想的な入門書」となるよう腐心した。
その他の入門書(日本語で読めるもの、一部)
拙書は、他の入門書よりも「手前」のところを説明している。より基本的な内容について、説明している。他の入門書と競合するものではない。拙書は、他の入門書の内容の最大公約数的な内容をあつかっている。外側への広がりを切り捨てる代わりに、内側を塗りつぶすことには徹底している。細かすぎて他では触れていない内容について、ぐりぐりと解説しているのが、他の入門書とくらべたときの拙書の特徴だ。
「Haskell入門」
今年、発売された良書「Haskell入門」は、より実践的な内容となっている。Haskellでばりばりとコードを書きたいかたには、「Haskell入門」のほうを、おすすめする。拙書が「他の入門書にない内容」をできるだけ含まない純粋な入門書をめざしたのに対し、「Haskell入門」では、Haskellで実践的なコードを書くうえで、知っておきたい知識、技法について、意欲的に紹介している。
「関数プログラミング実践入門」
2014年に発売され、去年に改訂された、(翻訳ではなく)日本語で執筆された入門書の定番。他の手続き型言語との比較や、Haskell以外の関数型言語の紹介などもあり、広がりのある一冊になっている。
また、後半は「関数型らしいコードの設計」や「ライブラリ」とのつきあいかたなど、実践的な内容になっている。
「すごいHaskellたのしく学ぼう」
2012年に発売された、Haskellの入門書の定番。入門に必要な内容がコンパクトにまとまっている。それだけではなく、Zipperという魅力的な構造についての解説があり、「Haskellだいたいわかる」という人でも、買って損のない内容。定番中の定番なので、書籍の内容自体が古くなったとしても、ネット上でみんなでサポートしてもらえるので、その点についても、おすすめ。
「Haskell 教養としての関数型プログラミング」の目次
- 第0部 はじめに
- 第i章 ようこそ
- 第ii章 この本の楽しみかた
- 第iii章 教養としての関数型プログラミング
- 第iv章 個人的な話
- 第v章 関数型プログラミングは常識になる
- 第vi章 謝辞
- 第I部 関数型プログラミング
- 第1章 古くて新しい、関数型プログラミング
- 第2章 関数型プログラミングの特徴
- 第3章 Haskellを学ぶ/Haskellで学ぶ
- 第II部 関数と型
- 第4章 Haskellを学ぶための環境を作る
- 第5章 関数
- 第6章 型
- 第7章 多相関数
- 第8章 関数と型のまとめ
- 第III部 タプル、リスト、再帰
- 第9章 タプル
- 第10章 リストによる「くりかえし」
- 第11章 演習: モンテカルロ法
- 第12章 ここまでの補足
- 第13章 再帰関数の基本
- 第14章 リストをとる再帰関数
- 第15章 たたみこみ
- 第16章 リストをかえす再帰関数
- 第17章 リストの再帰的定義
- 第18章 リストをあつかう標準的な関数
- 第19章 リスト内包表記
- 第20章 演習: 構文解析
- 第IV部 代数的データ型と型クラス
- 第21章 代数的データ型
- 第22章 多相的な再帰的な、代数的データ型
- 第23章 レコード構文
- 第24章 代数的データ型とモジュール
- 第25章 演習: NML(Nano Markup Language)
- 第26章 型クラス
- 第27章 基本的な型クラス
- 第V部 高階型
- 第28章 型の種類
- 第29章 モノイド
- 第30章 ファンクタ
- 第31章 関数を変形する
- 第32章 モナドという性質
- 第33章 モナドとファンクタ
- 第34章 アプリカティブファンクタ
- 第35章 モナドをあらわす型クラス
- 第36章 モナドを身につける
- 第37章 モナドとリスト
- 第VI部 入出力
- 第38章 IOモナド
- 第39章 IOモナドを身につける
- 第40章 演習: ライフゲーム
- 第VII部 補足
- 第41章 並べられる
- 第42章 並べるときに、構造を覚えておく
- 第43章 どっちでもいい
- 第44章 演習: 構文解析を、アプリカティブファンクタのわくぐみで
- 第VIII部 おわりに
- 第45章 まとめ
- 第46章 つぎに何をするか
- 第47章 さあ、とびたとう
- 補足資料
- 補足資料A 遅延評価で遅延リストを実現する
- 補足資料B モナド則から、モノイダル則を導く
- 補足資料C 「なかみに関数を適用する関数」はひとつ
- 補足資料D 個人的なコーディングスタイル
- 補足資料E レイアウトルールの、正確な仕様
- 補足資料F 言葉の説明
- 索引
対象・目的
「プログラミングは、はじめて」という人から、他の言語にものたりなさを感じている人まで。Haskellの基本的な考えかた、構文を身につける。
今の時代、プログラミングの情報において、ネットで入手できないものは、ほとんどない。しかし、ネット上の情報は、入門者に必須である基本的な情報から、入門には必要ない応用的な内容とが、まぜこぜになっている。この書籍では、あつかう内容を厳選し、「本当に入門者に必要な内容」にしぼった。ただし、「本当に理解」してもらうために、一部やや高度な内容も含まれている。
この書籍は、「ネット上の記事で学べる」というところまでの、最短のステップを用意することを、目的としている。
Haskellをひととおり学んだという人にも、「基本的な内容」について、網羅できているかのチェックに使うことができるのではないかと自負している。細かいところでも、意外に知らないことはあるものだ。
ねらい
つぎの点に注意して執筆した。
- 前提とする知識をできるだけ減らす
- 実際に手を動かしてもらう
- 「何のために?」に答える
- 楽しめる
- 誤字やコード例のチェックの徹底
Haskellを学ぶときに、手続き型言語の知識はそれほど必要ではない。「はじめの言語がHaskell」という人も書籍のターゲットにできるはずだ。知的好奇心の強い高校生とかでも読めるようにと、考えた。
また、プログラミング言語を学ぼうというときに、「理解してから試してみよう」という人がわりと多いと聞いたことがある。動くコードが書けるのがプログラミングなのに、そういうアプローチは「もったいない」。よって、この書籍では、「実際にコードを書く」「書いたコードを評価する」ということを重視した。書籍のとおりに、コードを打ち込んで試していくだけで「身につく」というあたりを、ねらいとした。
言語を学ぶうえでいきあたる「わからない」について、何がわからないかというと、「何のために」がわからないことが多い。なので、構文や機能を説明するうえで、それらが必要になるコード例を、(ムリにでも)作り紹介することにした。
内容そのものも楽しめるように、コード例も工夫したが、「身につける」ということを考えると、どうしても単調になってしまう箇所が出てきてしまう。そのような部分を乗り越えてもらうために、プチストーリーを作り、学習の進捗と、主人公の成長を重ね合わせた。
誤字について、たとえば、ひとつの誤字があったとして、読者が、そこで1分、立ち止まるとする。1000人の読者が1分、立ち止まったとすると、17時間の損失になる。そこで、10時間かけてでも、ひとつの誤字を見つけることを心がけた。ちなみに、この記事そのものは、しめきりに追われて書いているので、そのかぎりではない。
特徴
内容を厳選
「入門」に必要な内容のみになるように、できるだけしぼった。もっと、もりこみたくなる気持ちをおさえ、できるだけ「Haskell入門に必須」と思える内容だけに、なるようにした。本書の内容を、きちんと理解すれば、より高度な内容をネット上で学んでいくことが可能になる。
手を動かしながら学ぶ
言語の構文や機能を説明するときに、極力、対話環境に実際に打ち込む例を表示するようにした。言葉の説明だけでは、あきてしまう(僕のような)人にとって、そのまま打ち込んで結果を確認できる「入力例」があることは、ありがたい。
意味のある、短いコード例
構文や機能が「何のためにあるのか」という、その意味がわかる例を用意した。なおかつ、手で打ちこめる程度の短いコード例とした。言葉での説明では、よくわからなかったところも、短いコード例を実行し、いろいろといじるなかで、構文や機能の意味について理解できるだろうと考えた。
単に構文や機能を、それだけで説明する部分は、できるかぎりすくなくして、それらの存在意義がわかるようなコード例を、示すことを徹底した。
ひとつひとつ学べるように
新しい内容が、できるだけ、いちどに、ひとつだけになるように、構成した。新しい構文や機能を紹介するときに、これまで学んだ内容に対して、「その新しい内容」だけが追加されるようにした。できるだけ「前の内容のくりかえし + ひとつの新しい内容」のように説明するようにした。これにより、「新しい内容」を「前の内容」と連続して学べる。また、「前の内容」のくりかえしが復習となり、しっかりと身につくという効果も期待できる。
地味にやくだつ内容
コードを読みやすくするためや、コードをきれいに構成するための、地味だけれど、やくにたつ技法やベストプラクティスをちりばめた。細かすぎて意外と、ほかの入門書に書かれていないものもあるかと思われる。
適度に図を配置
書籍を読んでいるなかで、「図」というものは目を休めるうえでも有効だ。適度な割合で図を挿入した。とくに「関数の図」は、この書籍でつたえたい、構文の「見た目」ではない、意味論的な「本当のこと」を理解できないと、読み解けない側面があり、理解度のチェックとしても、やくだつだろう。「どうしてもわからない」ときは、サポート用アドレスにメールをいただければ幸いだ。
また、学習意欲を長続きさせる仕組みとして、書籍を読み進めるうちにアイテムを獲得して、成長していくキャラクターの図も随所に配置した。
漢字の割合
文章を読みやすくするためには、「漢字の使用を3割ていどにおさえるといい」と、いう話がある。Haskell自体が、むずかしいと思われていることもあり、より、とっつきやすくするために漢字を使用する割合を低く保った。
「」や読点を適宜、追加
論理の流れや、単語の切れ目、文の構造をはっきりさせるために、「」や読点を適宜、利用した。たとえば、「Aは、つぎのように、Bである」のように、「つぎのように」の前後に読点をいれることで、「この内容について、このあと具体的な入力例がありますよ」という意味の、標識になるように配慮した。また、漢字の割合を減らすことにより、ひらがなが連続してしまう箇所が多くなってしまったが、そこにも適宜、読点をうつことで読みやすくなるよう腐心した。
読点は、とくに、流し読みで、目をざっとすべらせていくときに、(すくなくとも僕にとっては)やくにたつ。ただし、好きずきがあるようで、「読点が多い」ということで、この書籍を低く評価するかたもいるようだ。
内容
第0部 はじめに
第i章
「プログラミングを楽しもう」というメッセージを導入にした。「『絵が描けない』こどもはいない。大人になるにつれて、『上手でなければいけない』と、思いこんでしまう」。初歩的なコードであっても、コードを書くことを、楽しむ気持ちが大切。
第ii章
この本の読みかたについて、まとめた。とくに、「はじめから順に、読む」ということを強調した。順に読んでいくことで、「新しい内容は、いちどに、ひとつ」となり、かつ、前に学んだ内容を「くりかえし」復習することができる。また、わからないところがあったときに、質問するためのメールアドレスも、ここに、掲載した。以下に示す。
書籍を購入していないかたも、Haskellについてや、この書籍についての質問があれば、できる範囲で答えたいと思う。
第iii章から第vi章
個人的な話や謝辞。
第I部 関数型プログラミング
第I部は、すくなくとも、ほかのひとつの言語に習熟している人のための説明だ。ほかの一般的な言語の多くは手続き型言語とされている。そのような、手続き型言語と関数型言語とを比較して説明している。「プログラミングは、はじめて」という人は第II部(第4章)から、読み進めよう。
「関数型プログラミング」という言葉は、明確な定義がないまま使われている。ここでは、この言葉をいくつかの要素にわけたうえで、それぞれについて説明している。説明にあたって、「手を動かして」読んでいけるように、ここでは、プログラミング言語Rubyを導入する。Rubyは、書きたいことが、そのまま、すぐに書ける、非常に優れた言語だ。
第II部 関数と型
ここからが本題だ。
第4章 Haskellを学ぶための環境を作る
現在、Haskellで開発するための標準的な方法は、つぎのようになる。まず、Stackというツールを導入する。そのうえで、Stackを使って、Haskellの代表的なコンパイラであれGHCを導入する。それぞれのOSにおけるStackの導入のしかたは、つぎのアドレスを参照。
Windows 10とMac OS X、GNU/Linuxについては、この書籍内にも導入方法を掲載している。
第5章 関数
「関数とは何か」という話から、「関数を使う」「関数を作る」練習まで。「かんたんで、すこしは、おもしろい」内容とするために、RSA暗号の例で関数を説明した。立方体の体積でも、円の面積でも、例は何でもいい。ただ、RSA暗号で暗号化した数値が、複号化でもとにもどったというのは、多少の爽快感があるかと考えた。
また、関数リテラルの記法について学ぶ。ほかにも、ここで学ぶ「関数と演算子とのあいだでの相互変換」などは、コードをきれいに書くうえで、地味にやくだつ。
第6章 型
値や関数には型がある。型によって、許される操作と、そうでない操作とを区別することができる。コードの安全性を保証するうえで、重要な意味をもつ「型」を、対話環境のなかで表示しながら、学んでいく。また、「型駆動開発」という造語によって示したが、「型をさきに決めるコーディングスタイル」について、BMI(Body Mass Index)を求める例で説明した。
第7章 多相関数
Haskellでは、定義ずみの関数の多くが多相関数である。ちょっとしたコードを書くにも多相関数を使うことになる。ここで、いくつかの多相関数を紹介する。
第8章 関数と型のまとめ
第5章から7章までの、まとめ。
第III部 タプル、リスト、再帰
第9章 タプル
単純な型を組み合わせて作る、複合的な型として、まずはタプルを紹介する。点をあらわすX-Y座標の、それぞれの値を、まずは別々の値として、つぎにタプルにまとめて示す。
第10章 リストによる「くりかえし」
Haskellで重要な役割を待つ、リストを紹介する。データ構造としてリストを紹介すると、ほかの言語でより一般的な「配列」との混同が予想されるので、あえて「くりかえし」という「操作」を、データ構造としてかためたものとして紹介している。
ここで紹介する「列挙、ろ過、写像、集計」というモデルは、多くのコードに応用可能であり、かつ、あつかいやすい有用なモデルだ。
第11章 演習: モンテカルロ法
第10章までで紹介した、構文や仕組みを利用して、あるていど意味のあるコードを作成する。「モンテカルロ法で円周率を計算する」例とした。
第12章 ここまでの補足
第11章までで紹介できなかった構文や仕組みについて、ここで追加で説明しておく。
第13章 再帰関数の基本
「再帰」は、関数型言語において、中心的な役割をはたす仕組みだ。それなのに、ここまで「再帰」を説明しなかったのには理由がある。「再帰」を使えば何でもできる。「再帰」は組力な概念だ。いきなり「再帰」を使うことを考える前に、リストに対する操作など、より力の弱い仕組みを使うことを考えるべきだ。そのほうが、コードがわかりやすくなることが多い。そのため、再帰の説明を遅らせた。
再帰の利用を説明するうえで、「ライプニッツの公式」と「二分木を、したにたどる」という、ふたつの例を挙げた。
第14章 リストをとる再帰関数
何であれ言語を学ぶうえで、標準的な関数を自分で再定義してみること以上に、勉強になることはない。第14章から18章までは、標準的な関数について、自分で再定義してみる練習をする。
第14章では、リストについて、「総和」「総積」「長さ」を求める関数を定義する。
第15章 たたみこみ
第14章で定義した関数について、それらに共通する「わくぐみ」を抽出する。それは、リストの要素を演算子でつないでいくという、わくぐみであり、「たたみこみ」と呼ばれる。たたみこみのわくぐみを抽出した関数foldrについて学ぶ。
第16章 リストをかえす再帰関数
リストを引数としてとる再帰関数があれば、リストを返り値としてかえす再帰関数もある。たとえば、1から始めて、順に1ずつ増えていき、nで終わるリストをかえす関数などだ。そのような、「リストをかえす関数」についても、そのわくぐみを抽出した関数がある。その代表的なものである関数iterateやunfoldrについて学ぶ。
第17章 リストの再帰的定義
リストを引数にとる関数でもなく、リストをかえす関数でもなく、リストそのものを再帰的に定義することができる。そのような例として、フィボナッチ数列をあらわすリストの定義を挙げる。
第18章 リストをあつかう標準的な関数
リストをあつかう標準的な関数について、その定義を示し、再定義してみる。多くについて3通りの定義のしかたを示した。
第19章 リスト内包表記
関数mapとfilterを組み合わせて、リストをいろいろと変化させていくことができる。そのような操作をより、読みやすく書くことのできる、リスト内包表記について学ぶ。実はこの章は、後々のモナドを学ぶ章の布石ともなっている。
第20章 演習: 構文解析
第19章までで学んだ構文や仕組みを利用することで、構文解析器をきれいに構成することができる。
第IV部 代数的データ型と型クラス
第21章 代数的データ型
値を列挙したり、既存の型を組み合わせたりして、複合的な型を作る方法を学ぶ。
第22章 多相的な再帰的な、代数的データ型
新しい型を作るうえで、その型を多相的にすることができる。また、新しい型を再帰的に定義することもできる。
第23章 レコード構文
複合的な型を作るときに、それぞれの要素について、名前をつけることができる。そのための構文を学ぶ。
第24章 代数的データ型とモジュール
代数的データ型に関する、いくつかの名前をモジュールから公開したり、しなかったりすることで値を保護できる。そのやりかたを学ぶ。
第25章 演習: NML(Nano Markup Language)
第24章までで作れるXMLに類似したデータ構造のパーサを作成する。
第26章 型クラス
型クラスは、複数の型の待つ性質を抽出したもの。型クラスの定義のしかたについて学ぶ。
第27章 基本的な型クラス
標準的に定義されている型クラスを学ぶ。Haskellでは等値演算など、いくつもの基本的な演算が、クラス関数として定義されている。
第V部 高階型
第28章 型の種類
値に型があるように、型にも型がある。その型の型のことを種類と呼ぶ。その種類について、みてみよう。
第29章 モノイド
足し算やかけ算、リストの連結など、ある種の演算に共通する性質を取り出したものがモノイドだ。このような、「共通する性質をもつ演算を、クラスとして取り出す」という考えかたが、モナドを理解するうえで重要になる。モノイドを学ぶことで、その考えかたを導入する。
第30章 ファンクタ
「なかみに変換を適用する」ことができるという、性質をもつものがファンクタだ。モナドを理解するうえで、避けては通れないファンクタについて学ぶ。
第31章 関数を変形する
この書籍では、モナドを、ふたつの「文脈つきの値をかえす関数」の合成、として説明する。この「導入として定義する関数」と、Haskellにおける実際のモナド関数の定義とがおなじことを説明するために、関数を意味を変えずに変形するやりかたを示す。
第32章 モナドという性質
ふたつの「文脈つきの値をかえす関数」の合成、としてモナドを定義した。電卓の例で説明。
第33章 モナドとファンクタ
モナドはすべてファンクタであることを示した。モナドは、ファンクタであり、かつ追加の性質をもつものだ。
第34章 アプリカティブファンクタ
モナドとファンクタのあいだに、アプリカティブファンクタがあることを示した。
第35章 モナドをあらわす型クラス
それをすなおに実装した関数から、Monadクラスのクラス関数の実際の形へと変形する。
第36章 モナドを身につける
モナドの実例をさらに、いくつか挙げて、モナドへの理解をかためる。
第37章 モナドとリスト
リストもモナドである。リストが「複数の可能性の演算」という意味でのモナドになることを示した。
第VI部 入出力
入出力については、入力と出力の口をもつ機械というイメージを使って説明した。このような機械を、つぎつぎにつないでいく仕組みが、モナドという「わくぐみ」であつかうことができるということを説明した。
第VII部 補足
いくつかの追加の型クラスについて説明した。
第VIII部 おわりに
第45章 まとめ
この書籍で説明したことの、全体をざっとおさらいする。
第46章 つぎに何をするか
この本で何をあつかっていないか。
第47章 さあ、とびたとう
最後まで学んでくれたかたがたへのメッセージ。
補足資料
補足資料A, B, C
「A. 遅延評価で遅延リストを実現する」、「B. モナド則から、モノイダル則を導く」、「C. 『なかみに関数を適用する関数』はひとつ」の、みっつは、Haskellに関するいくつかの性質が、本当に成り立つことを示した。
補足資料D 個人的なコーディングスタイル
主流ではないが、僕自身が個人的に採用している「コードをきれいに書く」ための書きかたを紹介。
補足資料E レイアウトルールの、正確な仕様
Haskellでは、レイアウトルールにより、;{}を省略できる。省略された構文要素の挿入は、形式的に定義されている。その形式的な定義を紹介する。
補足資料F 言葉の説明
この書籍で出てきた、いくつかの言葉について説明する。楽しんで読めるように腐心した。
おわりに
この書籍は、手で打ち込めるていどの小さいコード例からできている。書籍のなかの指示どおりに試すことで、Haskellにおいて必要最小限となる構文や機能、技法を身につけることができる。 はじめから順に打ち込みながら読んでいけば、論理の飛躍なく一歩一歩、進んでいけるはずだ。「内容」の紹介を読んでも、ちんぷんかんぷんだったとしても、この書籍を「順に、試しながら」読んでいけば、理解することは困難ではないだろう。
わからないことがあれば、つぎのアドレスに質問してほしい。