技術とかの雑なToday I Learnedメモ

プログラムの複雑さ・表面積・グラフの構造の記事を読んだ

プログラムの複雑さ・表面積・グラフの構造の記事を読んだ

プログラムの複雑さ・表面積・グラフの構造 - Object.create(null)

読んだ。

プログラムの複雑さを、「表面積」「グラフ」で表現するというもの。

表面積

引数と戻り値だけでモデル化できるが、実際に動くプログラムはそれだけでは足りず、色々な副作用(記事内での表現)がある。

ユーザーの入力や環境変数、画面やファイルへの出力など。

こういった入出力の多さを、この記事では表面積と表現し、表面積が大きければ大きいほど複雑なプログラムになる。

表面積が大きいほど複雑になる理由として、まず以下の理由が挙げられている。

一つは大抵のプログラミング言語において, 関数 (またはそれと同等のもの) のシグネチャには入出力チャンネルのうち引数と戻り値しか明示されないことが多く, 逆に言えばそれ以外の入出力チャンネルについては暗黙的に扱われることが多いためです.

引数と戻り値以外の入出力が暗黙的に扱われるというのは、たとえばだけどReactの関数コンポーネントのreturnで返されるReact.Elementは画面への出力だが、それは「画面への出力ですよ」という明示的なものは(そのコンポーネントが書いてあるファイル上には)なく、結局Reactというものの仕組みを理解したりコードを読んだりしなくてはいけない、ということだろうか。

二つ目は, 引数と戻り値以外の入出力チャンネルには, プログラム外部とのやりとりを含むものが多くあることです.

プログラム外部とのやり取りが多いと複雑になるのは想像がつきやすい。

外部のAPIを叩いたりするなどすれば、通信回数が増え、APIが返すエラー以外にもネットワークのエラーなどのエラーを処理する必要があるし、テストもモック化しなくてはいけない。

三つ目は認知負荷の問題です. 表面積が大きいほどプログラムが一度に関係するものが多くなり, そして関係するものが多ければ多いほど人間が認知することが困難になります.

これはまさにそのとおり。

どうやってこの複雑さに立ち向かうかは、

ではどのように表面積を減らすようにリファクタリングするかというと, 理想的には引数と戻り値以外の入出力チャンネルを, プログラムのエントリポイント (main 関数や HTTP リクエストのハンドラなど) に局所化します. こうすることでプログラムの大部分の入出力を引数と戻り値のみとすることができ, 大幅な単純化をすることができます.

こう書いてある。

ユーザーの入力や画面への出力など、これらを可能な限りエントリポイントに局所化する。言いたいことは分かるし想像もつくが、実際に何をどうするかはコードを見て書き換えたりしないと手応えがわかないかもしれない。

出力チャンネル自体を引数を使って注入することで, 入出力の有無を明示してプログラムの動作を把握しやすく

ここが想像つかなかったけど、全体的にも局所的にも表面積を減らすのが大事。

グラフ

モジュール間の依存関係やデータフローなどをグラフで表すことが可能で、そのグラフの構造にはいくつか種類がある。

記事内では、木/非巡回/一般のグラフ という区別をしていて、一般のグラフに行くほど一般性は上がるがプログラムが複雑になると紹介している。

一般のグラフは、

一般のグラフには閉路 (巡回路) があります. 閉路はモジュールの依存関係であれば相互依存している状態であり, 強く関連するもの同士が不当に複数のモジュールに分かれてしまっている可能性があります. またデータフローであれば双方向のデータフローであり, 例えば React コンポーネントであれば状態の同期というアンチパターンに陥ってしまっています.

という説明で、双方向のデータフローやコンポーネント間での状態の同期のような、見るだけで複雑度が高い状態というのが分かるようになっている。

非巡回グラフは, モジュールの依存関係であれば相互依存がない状態, データフローであれば単方向のデータフローに対応します. それぞれ一般の閉路のあるグラフと比べると, より健全かつ扱いが簡単になっています.

これは分かりやすくて、単方向データフローや相互依存がない状態というのは素晴らしいと思う。が、木のほうは更にこれより分かりやすい。

木構造はさらに枝ごとに分割統治を考えることができるため, プログラム的にも人間の認知的にもより簡単に扱うことができます.

木は枝ごとの分割統治が可能で、Reactでいうところのpropsバケツリレーがこれに当たりそう。

複雑度を減らして記述量を増やすみたいな。

リファクタリングは, 過剰に一般的で複雑な構造になっているグラフを, より制約の強い簡単な構造に変形するようにして行います.

リファクタリングでは一般的なグラフを木にすることを意識する。

まとめ

こうして整理してもらえるとすごく理解しやすかった。

閉路のあるグラフとないグラフの差があまりにも痛感しやすく、もしかしてReactとVueか……?みたいなことを考えた。違ったらアレですが。