なっとく!関数型プログラミング
翔泳社
著者:Michał Płachta
訳者:株式会社クイープ
監修:株式会社クイープ
まえがき
謝辞
本書について
著者紹介
Part 1 関数型ツールキット
第1章 関数型プログラミングを学ぶ
1.1 あなたが本書を手に取ったのはきっと
1.2 どの様な知識が必要か
1.3 関数とはどのようなものか
1.4 関数に取り組む
1.5 コードが嘘をつくとき
1.6 命令型と宣言型
1.7 コーヒーブレイク:命令型と宣言型
1.8 コーヒーブレイクの解説:命令型と宣言型
1.9 関数型プログラミングを学ぶとどんなメリットがある?
1.10 Scalaに飛び込む
1.11 実習:Scalaで関数を書く
1.12 ツールを準備する
1.13 REPLの仕組みを理解する
1.14 最初の関数を書く
1.15 本書の使い方
1.16 本章のまとめ
第2章 純粋関数
2.1 純粋関数はなぜ必要か
2.2 命令型のコーディング
2.3 コードが失敗する
2.4 データのコピーを渡す
2.5 コードがまたしても失敗する
2.6 格納せずに再計算する
2.7 状態を渡すことでロジックに焦点を合わせる
2.8 状態はどこへいったのか
2.9 非純関数と純粋関数の違い
2.10 コーヒーブレイク:純粋関数のリファクタリング
2.11 コーヒーブレイクの解説:純粋関数のリファクタリング
2.12 信頼される純粋関数
2.13 プログラミング言語での純粋関数
2.14 純粋であり続けるのは難しい
2.15 純粋関数とクリーンなコード
2.16 コーヒーブレイク:純粋か非純粋か
2.17 コーヒーブレイクの解説:純粋か非純粋か
2.18 Scalaを使って純粋関数を作成する
2.19 実習:Scalaで純粋関数を書く
2.20 純粋関数をテストする
2.21 コーヒーブレイク:純粋関数のテスト
2.22 コーヒーブレイクの解説:純粋関数のテスト
2.23 本章のまとめ
第3章 イミュータブルな値
3.1 エンジンの燃料
3.2 不変性のもう1つのケース
3.3 この関数は信頼できるか
3.4 可変性は危険である
3.5 嘘をつく関数について考える
3.6 コピーを使って可変性に対抗する
3.7 コーヒーブレイク:可変性で痛い目を見る
3.8 コーヒーブレイクの解説:可変性で痛い目を見る
3.9 共有ミュータブル状態
3.10 状態はプログラミングの能力に影響を与える
3.11 可動部分に対処する
3.12 関数型プログラミングを使って可動部分に対処する
3.13 Scalaのイミュータブル値
3.14 不変性に対する直観を養う
3.15 コーヒーブレイク:イミュータブルなString API
3.16 コーヒーブレイクの解説:イミュータブルなString API
3.17 ちょっと待った…これはまずいのでは?
3.18 共有ミュータブル状態に対する純粋関数型アプローチ
3.19 実習:イミュータブルなスライスとアペンド
3.20 本章のまとめ
第4章 値としての関数
4.1 要件を関数として実装する
4.2 非純粋関数とミュータブルな値の反撃
4.3 JavaのStreamを使ってリストを並び替える
4.4 関数のシグネチャは事実をありのままに伝えるべきである
4.5 要件を変更する
4.6 それならコードに渡してしまえ
4.7 JavaのFunction型の値を使う
4.8 Function構文を使ってコードの重複に対処する
4.9 ユーザー定義の関数を引数として渡す
4.10 コーヒーブレイク:パラメータとしての関数
4.11 コーヒーブレイクの解説:パラメータとしての関数
4.12 関数型のJavaを読むときの問題
4.13 Scalaで関数を渡す
4.14 sortByを詳しく調べる
4.15 Scalaの関数パラメータのシグネチャ
4.16 Scalaで関数を引数として渡す
4.17 実習:関数を渡す
4.18 宣言型プログラミングを取り入れる
4.19 カスタム関数に関数を渡す
4.20 小さな関数とそれらの役割
4.21 関数をインラインで渡す
4.22 コーヒーブレイク:Scalaで関数を渡す
4.23 コーヒーブレイクの解説:Scalaで関数を渡す
4.24 関数を渡すだけで他に何ができるか
4.25 リストの各要素に関数を適用する
4.26 mapを使ってリストの各要素に関数を適用する
4.27 mapの仕組みを理解する
4.28 実習:map
4.29 一度覚えたら、どこでも使う
4.30 条件に基づいてリストの一部を返す
4.31 filterを使ってリストの一部を返す
4.32 filterの仕組みを理解する
4.33 実習:filter
4.34 ここまでやってきたこと
4.35 同じことを繰り返さない
4.36 このAPIは使いやすいか
4.37 新しいパラメータを追加するだけでは不十分
4.38 関数は関数を返すことができる
4.39 関数を返すことができる関数を使う
4.40 関数は値
4.41 コーヒーブレイク:関数を返す
4.42 コーヒーブレイクの解説:関数を返す
4.43 関数型APIの設計
4.44 関数型APIの反復的な設計
4.45 返された関数から関数を返す
4.46 返された関数から関数を返す方法
4.47 返される関数に基づいて構築された柔軟なAPIを使う
4.48 関数で複数のパラメータリストを使う
4.49 カリー化
4.50 実習:カリー化
4.51 関数値を渡すことによるプログラミング
4.52 多くの値を1つに減らす
4.53 foldLeftを使って多くの値を1つに減らす
4.54 foldLeftの仕組みを理解する
4.55 foldLeftについてこれだけは知っておこう
4.56 実習:foldLeft
4.57 イミュータブルデータをモデル化する
4.58 高階関数で直積型を使う
4.59 インライン関数のより簡潔な構文
4.60 本章のまとめ
Part2 関数型プログラム
第5章 逐次プログラム
5.1 パイプラインに基づくアルゴリズムを作成する
5.2 小さなコードから大きなプログラミングを組み立てる
5.3 命令型のアプローチ
5.4 flattenとflatMap
5.5 flatMapの実践的な例
5.6 flatMapとリストサイズ変更
5.7 コーヒーブレイク:リストのリストを処理する
5.8 コーヒーブレイクの解説:リストのリストを処理する
5.9 連結されたflatMapとmap
5.10 入れ子のflatMap
5.11 他の値に依存する道
5.12 実習:入れ子のflatMap
5.13 入れ子のflatMapにはもっとよい構文がある
5.14 for内包表記で解決
5.15 コーヒーブレイク:flatMapとfor内包表記
5.16 コーヒーブレイクの解説:flatMapとfor内包表記
5.17 for内包表記の仕組みを理解する
5.18 あなたが探しているforじゃない
5.19 for内包表記の内部
5.20 更に高度なfor内包表記
5.21 for内包表記を使ってすべての組み合わせを確認する
5.22 フィルタリングの方法
5.23 コーヒーブレイク:フィルタリングの方法
5.24 コーヒーブレイクの解説:フィルタリングの方法
5.25 さらなる抽象化を求めて
5.26 map、foldLeft、flatMapを比較する
5.27 for内包表記でSetを使う
5.28 for内包表記で複数の型を使う
5.29 実習:for内包表記
5.30 for内包表記を改めて定義する
5.31 for内包表記で非コレクション型を使う
5.32 nullを回避する:Option型
5.33 パイプラインとして解析する
5.34 コーヒーブレイク:Optionによる解析
5.35 コーヒーブレイクの解説:Optionによる解析
5.36 本章のまとめ
第6章 エラー処理
6.1 さまざまなエラーをそつなく処理する
6.2 エラーをすべてを処理することはそもそも可能なのか
6.3 テレビ番組のリストを放送期間順に並べる
6.4 並び替えの要件を実装する
6.5 外部から取得したデータを処理する
6.6 関数型設計:小さなブロックから組み立てる
6.7 Stringを解析してイミュータブルオブジェクトにする
6.8 Listの解析とは、1つの要素の解析である
6.9 StringをTvShowとして解析する
6.10 潜在的なエラーをどうするか
6.11 nullを返すのってあり?
6.12 潜在的なエラーをより適切に処理するには
6.13 Optionを返す関数を実装する
6.14 Optionは起こり得るエラーを強制的に処理させる
6.15 小さなブロックから構築する
6.16 関数型設計は小さなブロックから構築される
6.17 Optionを返す安全で小さな関数を書く
6.18 関数、値、式
6.19 実習:Optionを返す安全な関数
6.20 エラーはどのように伝播されるか
6.21 値はエラーを表す
6.22 Option、for内包表記、検査例外
6.23 検査例外はどうか
6.24 条件付きリカバリー
6.25 命令型スタイルの条件付きリカバリー
6.26 関数型スタイルの条件付きリカバリー
6.27 合成できない検査例外と合成できるOption
6.28 orElseはどのような仕組みで動作するか
6.29 実習:関数型のエラー処理
6.30 関数の合成はエラーが発生してもうまくいく
6.31 エラー処理が必要であることを示すコンパイルエラー
6.32 コンパイルエラーは私たちの味方
6.33 Optionのリストを平坦なリストに変換する
6.34 コンパイラにガイドしてもらう
6.35 ただし、コンパイラを当てにしすぎてはいけない
6.36 コーヒーブレイク:エラー処理の戦略
6.37 コーヒーブレイクの解説:エラー処理の戦略
6.38 2種類のエラー処理戦略
6.39 オールオアナッシング型のエラー処理戦略
6.40 OptionのListをListのOptionに畳み込む
6.41 これで、複数のエラーを処理する方法がわかった
6.42 何が失敗したのかはどうすればわかるか
6.43 エラーの詳細は戻り値で伝える必要がある
6.44 Eitherを使ってエラーの詳細を伝達する
6.45 Eitherに対するリファクタリング
6.46 Optionの代わりにEitherを返す
6.47 実習:Eitherを返す安全な関数
6.48 Optionで学んだことはEitherでもうまくいく
6.49 コーヒーブレイク:Eitherを使ったエラー処理
6.50 コーヒーブレイクの解説:Eitherを使ったエラー処理
6.51 OptionとEitherの使い方
6.52 本章のまとめ
第7章 型としての要件
7.1 プログラマのミスを最小限に抑えるためにデータをモデル化する
7.2 うまくモデル化されたデータは嘘をつけない
7.3 すでに知っているもの(プリミティブ型)を使って設計する
7.4 プリミティブ型としてモデル化されたデータを使う
7.5 コーヒーブレイク:プリミティブ型の問題
7.6 コーヒーブレイクの解説:プリミティブ型の問題
7.7 プリミティブ型によるモデル化の問題点
7.8 プリミティブ型を使うと作業が難しくなる
7.9 パラメータの配置ミスを防ぐnewtype
7.10 データモデルでnewtypeを使う
7.11 実習:newtype
7.12 有効なデータの組み合わせだけを可能にする
7.13 値が存在しない可能性をモデル化する
7.14 モデルを変更したらロジックも変更する
7.15 Optionとしてモデル化されたデータをロジックで使う
7.16 高階関数で決まり!
7.17 そのための高階関数はきっとある
7.18 コーヒーブレイク:forall、exists、contains
7.19 コーヒーブレイクの解説:forall、exists、contains
7.20 1つの直積型の中で概念を結合する
7.21 有限の可能性をモデル化する
7.22 直和型を使う
7.23 直和型を使ってモデルをもっとよいものに
7.24 直和型+直積型の組み合わせを使う
7.25 直積型+直和型=代数的データ型(ADT)
7.26 振る舞い(関数)でADTベースのモデルを使う
7.27 パターンマッチングを使ってADTを分解する
7.28 重複とDRY
7.29 実習:パターンマッチング
7.30 newtype、ADT、パターンマッチングを使う
7.31 継承はどうなる?
7.32 コーヒーブレイク:関数型データ設計
7.33 コーヒーブレイクの解説:関数型データ設計
7.34 振る舞いをモデル化する
7.35 振る舞いをデータとしてモデル化する
7.36 ADTベースのパラメータを使って関数を実装する
7.37 コーヒーブレイク:設計と保守性
7.38 コーヒーブレイクの解説:設計と保守性
7.39 本章のまとめ
第8章 値としてのIO
8.1 外部とやり取りする
8.2 外部のAPIと結合する
8.3 副作用のあるIOアクションの特性
8.4 副作用のあるIOコードに対する命令型ソリューション
8.5 IOコードに対する命令型アプローチの問題点
8.6 関数型プログラミングで本当に改善できるのか
8.7 IOを実行することとIOの結果を使うこと
8.8 IOを命令型で処理する
8.9 IO型の値としての計算
8.10 IO型の値
8.11 IO型の値を使う
8.12 非純粋性を取り除く
8.13 2つのIOアクションから取得した値を使う
8.14 IO型の2つの値をIO型の1つの値にまとめる
8.15 実習:IO型の値の作成と結合
8.16 値だけを使って関心事のもつれを解く
8.17 IO型はバイラル
8.18 コーヒーブレイク:値を操作する
8.19 コーヒーブレイクの解説:値を操作する
8.20 関数型IOに向かって
8.21 IOの失敗はどうなるか
8.22 IOによって表されるプログラムの実行は失敗するかもしれない
8.23 orElseを覚えている?
8.24 遅延評価と先行評価
8.25 IO.orElseを使ってリカバリー戦略を実装する
8.26 orElseとpureを使ってフォールバックを実装する
8.27 実習:IO型の値によるリカバリー
8.28 潜在的な失敗にはどこで対処する?
8.29 失敗に対処する関数型IO
8.30 純粋関数は安全ではない世界でも嘘をつかない
8.31 関数型アーキテクチャ
8.32 IOを使ってデータを保存する
8.33 コーヒーブレイク:IOを使ってデータを保存する
8.34 コーヒーブレイクの解説:IOを使ってデータを保存する
8.35 あらゆるものを値として扱う
8.36 リトライを値として扱う
8.37 未知数のAPI呼び出しを値として扱う
8.38 実習:関数型シグネチャで直観を働かせる
8.39 本章のまとめ
第9章 値としてのストリーム
9.1 無限の彼方へ
9.2 未知数の値に対処する
9.3 外部の非純粋API呼び出しに対処する
9.4 関数型の設計アプローチ
9.5 イミュータブルマップ
9.6 実習:イミュータブルマップ
9.7 IO呼び出しは何回必要か
9.8 ボトムアップ設計
9.9 リストの高度な演算
9.10 タプルの登場
9.11 zipとdrop
9.12 タプルでパターンマッチング
9.13 コーヒーブレイク:マップとタプルを操作する
9.14 コーヒーブレイクの解説:マップとタプルを操作する
9.15 関数型ジグソーパズル
9.16 ボトムアップ設計で型を追跡する
9.17 プロタイプトデッドエンド
9.18 再帰関数
9.19 無限と遅延
9.20 再帰関数の構造
9.21 将来の欠損に再帰で対処する
9.22 無限の再帰呼び出しの有用性
9.23 コーヒーブレイク:再帰と無限
9.24 コーヒーブレイクの解説:再帰と無限
9.25 再帰を使ってさまざまなIOプログラムを作成する
9.26 再帰を使って任意の回数だけ呼び出す
9.27 再帰バージョンの問題点
9.28 データストリームの導入
9.29 命令型言語でのストリーム
9.30 オンデマンドの値
9.31 ストリームの処理、プロデューサ、コンシューマ
9.32 ストリームとIO
9.33 関数型のStream
9.34 関数型のプログラミングのストリームは値
9.35 ストリームは再帰的な値
9.36 プリミティブ演算とコンビネータ
9.37 IOベースの値からなるストリーム
9.38 IOベースの値からなる無限のストリーム
9.39 副作用のための実行
9.40 実習:ストリーム処理
9.41 ストリームを活用する
9.42 API呼び出しからなる無限のストリーム
9.43 ストリームでのIOエラーを処理する
9.44 分解された関心事
9.45 スライディングウィンドウ
9.46 IO呼び出しの間で待機する
9.47 ストリームをzipする
9.48 ストリームベースのアプローチを使うことの利点
9.49 本章のまとめ
第10章 並行プログラム
10.1 どこもかしこもスレッドだらけ
10.2 宣言型の並行処理
10.3 逐次と並行
10.4 コーヒーブレイク:逐次的に考える
10.5 コーヒーブレイクの解説:逐次的に考える
10.6 バッチ処理の必要性
10.7 バッチ処理を実装する
10.8 並行処理の世界
10.9 並行状態
10.10 命令型の並行処理
10.11 アトミック参照
10.12 Refの登場
10.13 Ref型の値を更新する
10.14 Ref型の値を使う
10.15 すべてを同時に行う
10.16 parSequenceを使う
10.17 実習:同時IO
10.18 並行性をモデル化する
10.19 Refとファイバを使ったコーディング
10.20 決して終わらないIO
10.21 コーヒーブレイク:並行的に考える
10.22 コーヒーブレイクの解説:並行的に考える
10.23 非同期性のニーズ
10.24 非同期アクセスの準備
10.25 関数型非同期プログラムを設計する
10.26 ファイバを手動で管理する
10.27 関数型の非同期プログたむのコーディング
10.28 本章のまとめ
Part3 関数型プログラミングの応用
第11章 関数型プログラムを設計する
11.1 まず動かす、正しく動かす、高速に動かす
11.2 イミュータブルな値を使ってモデル化する
11.3 ビジネスドメインのモデル化と関数型プログラミング
11.4 データアクセスをモデル化する
11.5 BoF
11.6 純粋関数としてのビジネスロジック
11.7 現実のデータアクセスでの関心の分離
11.8 命令型ライブラリとIOを使ってAPIを統合する
11.9 設計に従う
11.10 入力アクションをIO型の値として実装する
11.11 ライブラリのIOを他の関心事から分離する
11.12 カリー化と制御の反転
11.13 値としての関数
11.14 点と点を結ぶ
11.15 まず動かしてみた
11.16 正しく動かす
11.17 リソースのリーク
11.18 リソースを処理する
11.19 Resource型の値を使う
11.20 正しく動かしてみた
11.21 コーヒーブレイク:高速に動かく
11.22 コーヒーブレイクの解説:高速に動かす
11.23 本章のまとめ
第12章 関数型プログラムをテストする
12.1 そのためのテストはある?
12.2 テストは単なる関数
12.3 テストする関数を選択する
12.4 サンプルを提供することによるテスト
12.5 実習:サンプルによるテスト
12.6 よいサンプルを生成する
12.7 プロパティを生成する
12.8 プロパティベースのテスト
12.9 プロパティを定義することによるテスト
12.10 関数を渡すことで作業を委譲する
12.11 プロパティベースのテストの失敗を理解する
12.12 テストが間違っている?それともバグ?
12.13 カスタムジェネレータ
12.14 カスタムジェネレータを使う
12.15 さらに複雑なシナリオをわかりやすい方法でテストする
12.16 実装からバグを見つけて修正する
12.17 コーヒーブレイク:プロパティベースのテスト
12.18 コーヒーブレイクの解説:プロパティベースのテスト
12.19 プロパティとサンプル
12.20 要件のカバレッジ
12.21 副作用のある要件をテストする
12.22 作業に適したテストを突き止める
12.23 データ使用法テスト
12.24 実習:IOを使って外部サービスをスタブ化する
12.25 テストと設計
12.26 サービス統合テスト
12.27 統合テストでのResourceとしてのローカルサーバ
12.28 分離された統合テストを作成する
12.29 サービスとの統合は単一責任
12.30 コーヒーブレイク:統合テストを作成する
12.31 コーヒーブレイクの解説:統合テストを作成する
12.32 統合テストには時間がかかる
12.33 プロパティベースの統合テスト
12.34 正しいテストアプローチを選択する
12.35 テスト駆動開発(TDD)
12.36 存在しない機能のテストを作成する
12.37 Red-Green-Refactor
12.38 テストを緑にする
12.39 赤のテストをさらに追加する
12.40 TDDの最後のステップ
12.41 本章のまとめ
12.42 最後の実習
付録A Scala早見表
付録B 関数型の重要なポイント
索引