意識の高いエントリを書いてみる。
難解なコードを書く上でもっとも重視しなければならないのは、可読性である。カーネルのように、品質を重視される分野では、特に重要となる。
可読性について基本的なことは「リーダブルコード」という 本に書いてある。変数のローカリティは小さく!ガードを使って早く関数を終了させろ!など、プログラマにとっては当たり前のことがよくまとまっている点に価値がある。
以下では、可読性の高いコードを書く上で気をつけた方が良い二点を紹介する。
抽象度を一定に保つ
可読性の高いコードは、物語を読むように頭に入ってくるコードである。物語を構築する上で大切なのは、一定の抽象度で書くということである。物語の中に、極端に抽象度の高い話が紛れ込むべきではないし、あまりに細かい話が見えるべきでもない。
例えば、パンを焼いて、バターを塗って、食べるという動作の中に、わけのわからない化学反応を書くべきではないし、一部が数学的な抽象で語られるべきでもない。パンを食うという作業に合った抽象度ですべて語られるべきである。コードは必要十分でなければならない。コードを理解させる上で余計なものは含むべきではないが、当然あるべきものは含まなければならない。
ロックを含むコードは、プログラムを切り分けるのが難しくなる。しかし、ロックはプログラムのおまけではない。ロックも含めて可読性の高いコードを書かなければならない。一般的には、シングルスレッドのコードよりはプログラムを平らにして、ロックの設計が分かりやすくなるようにしなければならない。
正しい抽象度で書けば、コードはどんどん分解されていくし、変更箇所は自然に最小化されるはずである。正しく分解されたプログラムは、さらに分解することも簡単であるし、他のものと合成することも簡単だ。コードは始めから最高品質に向かう必要はない。最高品質に向かうことが出来るコードであれば良い。
プログラムの基本構造を明示する
プログラムの構造というのは、mapとaccumulateで語られる。このような構造で書くことを明に求められる関数型言語に限らず、C言語で書かれるカーネルコードにおいても、これは全く同じである。もちろん、C言語などでは、これら本来は分離出来る処理が(パフォーマンスや明瞭さの理由により)明に分離出来ず、混ざってしまうことはあるが、プログラマはこれら基本的な構造を頭に構築しながらコードを理解するのだ。従って、読み手の理解のためには、これら構造が理解しやすくなるようにコードを書けばいい。不必要な抽象化によってコードが余計にわかりにくくなることを避けなければならない。基本的な構造の中で、どういう処理をしており、それによってどういう副作用が発生するのか。この点を意識すればコードは自然と良くなる。
まとめ
以上を守れば、一つの関数が1000行などということは起きなくなる。しかし、いきなり抽象度だ構造だと言われても良く分かりませんという人は、関数を物語と見たらどうか。
物語というのは起承転結で語られる。「転」なるものはプログラムの中には必要ないので、関数は、大体の場合は以下の三要素で構成される。
- [起 (入力)] 引数を受け取る。
- [承] 処理をするための準備をする(情報をかき集める、何か構造を作る)。
- [結 (出力)] 何かの処理をする(何か返すこともある。副作用が発生することもある)。
パンの例でいえば、
- パンを作ることの出来る環境を入力とする
- 食べられるパンを作る(パンの材料をかき集めて、構造を作る)
- 対象がパンを食べたという状態に遷移する(副作用)
というのがものが物語の一例となるだろうが、これに限らず、他の部分にも依存する(何を入力と見るか、何を出力と見るか、その幅を広げるためには色々なプログラミングパラダイムを勉強した方がいいのだと思う)。
この基準に照らし合わせてプログラムを分解していけば、自然と抽象度が一定になると思う。もちろん例外はあるが、常に頭の中で物語を書きながらコードを書くことが基本だ。プログラムは決して、計算機の状態を変化させる命令列ではない。