Functional Programming in C# まとめ 9章
8章のまとめは、こちら
9. Thinking about data functionally
この章では以下の内容を説明する。
- 状態変更の罠
- 変更をせず変化を表現する
- イミュータブル性を強制する
- 関数型データ構造
9.1. The pitfalls of state mutation
以下のように、ある項目の値を変更すると、さらに別の項目も連動して、変更されるような例を考える。
public class Product
{
public bool IsLowOnInventory { get; private set; }
public int Inentory
{
get { return inventory;}
private set
{
inventory = value;
IsLowOnInventory = inventory <= 5
}
}
}
こういうクラスのインスタンスを同時実行で利用した場合、状態がおかしくなることは稀であり、バグとなる原因を特定するのが難しい状態となってしまう。
状態変更を行うことの問題点は大きく以下
- 同時実行ができない(マルチスレッド、非同期実行など)
- コンポーネント間の結合が強まる
- 純粋さが失われる
2番目の「コンポーネント間の結合が強まる」については、例えば、上のProductクラスのInentoryの値を設定したり、参照したりする別のクラスがどこにどれだけあるかわからないことが問題、という趣旨のようなのですが、状態の変更を行わないことで、それがどう改善するのか?がよくわかりませんでした。
9.2. Understandhing state, identity, and change
本文ではきっちりと説明されているわけではないが、いわゆる、値オブジェクトとエンティティという区別(DDDの用語か?)があり、エンティティの場合には、識別子は同じだが、値が変化する場合がある。例えば、ある個人の情報を保持するようなオブジェクトの場合、名前も住所も身長も体重も変化する可能性があるが、それによって、どの個人かということが変わることはない。
OOPとFPではデータに対する考え方が異なる
- OOPは、対象の状態に応じて、既存のオブジェクトの値を変化させる
- FPは、オブジェクトにしまわれているデータはスナップショット(ある時点を切り取った情報)なので、それは捨てて、新しいスナップショットを作成する
ただし、新しいスナップショットを作る場合、対象となるオブジェクトの値を全部コピーしなければならないので面倒である。
9.3. Enforcing immutability
イミュータブルなクラスを作る。
この節では、イミュータブルなクラスを作るために、様々な議論を行っているが、最新版のC#ではすべて解消されているようです。
- Record型:クラスの代わりにRecord型とすることで、メンバはイミュータブルとなり、さらにWithを使って、コピーも簡単に作れるそうです。
- System.Collection.Immutableの利用:執筆時点ではNuGetで取得する必要があったが、.NETに組み込まれている模様
別の議論として、例えば、Jsonへのデシリアライズを行うツールやORMが空のコンストラクタとセッターを求めるようなケースではこうした対応が行えない、というようなものありました。
9.4. A short introduction to functional date structures
ここでは、リンクドリストや2分木の関数型プログラミングでの簡易的な実装を紹介しています。
- イミュータブルなコレクションがどいういうものか
- イミュータブルなコレクションでも、処理の効率はそれほど悪くない(実際の実装を示して)
ということを示したいのだと思います。おそらく。
まとめとして、イミュータブルるな型を利用することによって、処理の効率がわるいのではと思うかもしれないが、実際にはそれほど悪化するものではない。また、命令型のプログラミングでは、同時実行に際して、ロック制御を行ったりすることになるので、結果としてイミュータブル実装の方が早い場合もある。そういうことを考えると、最初からイミュータブルな実装にしておいた方が良いのではないか。また、同時実行に限らず、変更可能なオブジェクトの参照をやり取りしていると、予期せぬところで値が変更される可能性もあるので、イミュータブルな型を使っておくと安心である。
Discussion