Functional Core, Imperative Shellというスクリーンキャストを見た。(以後 FCIS と略す) これは、ツイッタークライアントを「純粋な関数/オブジェクト群(Functional Core)」と「それをとりまく IO と状態更新(Imperative Shell)」にわけて実装するという作り方を解説している。 純粋な関数はモックなど使わずにテストしやすいし、振る舞いが理解しやすい。 IO の処理は最小化されており、この部分はテストしていないが条件分岐などがなく問題が起きにくいということだった。
この整理の仕方を見て、自分はしっくり来た。
The Elm Architecture(以後 TEA)を触っていた時も、Update : Msg -> Model -> Model として、つまり Update を入力と状態を受け取って新たなる状態へと変換する純粋関数として書けるのはありがたいと感じていた。
FCIS 的にいうと、Model と Update が Functional Core で、それ以外(Elm のランタイム処理含む)が Imperative Shell であると言える。
Elm はきれいだけど実用がむずかしい箇所があるように思っていて、それは TEA が強制されすぎる所だと感じていた。
緊急ハッチ的に「ここは素の JavaScript が書きたいんですよ」に対応しづらいのである。
(いちおうPortという仕組みはあるが…)
つまり Elm は FCIS 的ではあるが、Imperative Shell が固定され、Functional Core を実装するだけでなんとかしてくれという強制力が強すぎるのではないか。
FCIS であればあくまで言語やフレームワークによる制限でなくセルフ縛り的なものなので、そこの良し悪しはあるものの、より現実の泥臭い問題に対処しやすいのではないか。
以前Clean Architectureという本を読んだが、自分の理解でいうとこれも大雑把にいうと「内と外」の話である。内に大事なビジネスルール、外にやっかいな UI や IO を置き、依存関係を一方向にすることで整理するという。 (よく Clean Architecture の説明でみる円の図はあくまで例であって、「こう分けるのがよい」という話ではないという理解をしている。実際 「円の数を決めるルールではなく、依存の向きがルールだ」 ということが書かれている) FCIS も Clean Architecture もやっていることは節分、つまり守りたい価値がある重要なものを内におき、やっかいなものを外に分ける「鬼は外福は内」といえる。FCIS は Clean Architecture を補強し、「内にあるものは純粋関数のほうがよい」と言えるのではないだろうか。
最後に補足として、なんでもかんでも純粋関数がいいという話ではない。 それこそ UI やデータストレージのようなものは状態を持つのが仕事なわけで、そこを下手に分けようとすると逆に難しさが生まれてしまう。 またパフォーマンス面も気になる。純粋関数型データ構造のようなやり方もあるが、破壊的な操作を許すデータ構造のほうが基本的には計算効率がよい。 ゲームプログラムが純粋関数にならんか…というのを昔から考えているけど、なかなか浸透しないのはこのへんも関係あるんじゃないかと思う。 (一時期超並列計算機がゲームでもあたりまえに使われる世界が来ると純粋関数の作り方に置き換わっていくのでは…という期待があったが、結局いまのところ来ず、相も変わらずメインスレッドに律速されるのだった…)