ソースコードの心脳問題

 先のコードコメントに書くべきは「意図」 - プログラマーの脳みその関連でソースコードの心脳問題という話をしよう。

 心脳問題というのは脳という分子機械が、いかにして心を持つのかという哲学のような脳科学のようなテーマの話題。*1紀元前にプラトンイデア論にて意識とは何かという基礎命題は与えられていたわけだけども、古代には哲学の範疇に収まっていた。

 ところが現代のコンピュータサイエンスでは、脳みそのシュミレーションがかなりの規模でできるようになってきていて、工学的に脳を作れる時代が近く迫ってきている。2007年の記事になるが

今回、現在世界最速の性能を持つ「BlueGene L」スーパー・コンピューター上に、マウスの脳の半分にあたる800万のニューロンの働きを再現させる事に成功したそうです。

使われたのは、それぞれが256MBのメモリを使用する、4096台のプロセッサを持つ「BlueGene L」スーパー・コンピューターなのですが、800万ほどのニューロンが、最大でひとつあたり6300のシナプスを持つという設定のマウスの脳の半分の活動を、「現実時間の10倍遅いスピード」で10秒間シミュレートしたそうです。つまりマウスの脳の半分の活動を「1秒分」再現したわけです。

ニューロンが自然にグループを作るのが観察されたり、仮想のシナプスが実際のシナプスで観察される連携パターンと同様の方法で、交互に活性化するのも観察されたそうです。実際のマウスの脳で見られる構造は欠けていたそうですが、それでも凄いものだと思います。

http://www.mypress.jp/v2_writers/beep/story/?story_id=1601251

 というわけだ。さて、この1秒分のマウスの脳みそのシミュレーションは意識を持ったのだろうか?

機械と意識の間

 さて、コードコメントに書くべきは「意図」 - プログラマーの脳みそではこう述べた。

 つまるところ、ソースコードというのは機械が読んで実行するためのものなのだから、「どのように動くか」という事実については雄弁に語ってくれるものなのだけど、「どのような意図で書かれたか」ということについては全くもって寡黙だ。

 ならば、コード中にプログラム言語ではない自然言語を敢えて書く理由は、その欠けている「意図」を補うためだ。

コードコメントに書くべきは「意図」 - プログラマーの脳みそ

 え、なになに?話がぶっとんでいて訳が分からない?

b:id:r-west 意図と処理の違いをもっと突っ込んで説明してくれるとありがたい

はてなブックマーク - コードコメントに書くべきは「意図」 - プログラマーの脳みそ

 オーケー、オーケー。このあたり、いくらか解説がいるだろう。そう。僕は突飛なことを書いている。この変態性は僕の誇りだから病院が来いとか病院逃げてとか言わなくても大丈夫だ。

 発端となったソースコードのコメント率は20%を切ることが望ましい : 小野和俊のブログに意図不明ではあるが面白いブクマがついていた。ここから話をたどろう。

b:id:mura-taiken コンピュータが理解できない記述は最小限にするべき。そして、平文との差が小さいコードを書けば良い

はてなブックマーク - 小野和俊のブログ:ソースコードのコメント率は20%を切ることが望ましい

 このコメントの真意はよくわからないが、ことプログラム言語において「コンピュータが理解できない記述」は存在してはならない。そのようなものはシンタックスエラー(構文エラー)になって実行不可能なので意図的に残すことはありえない。なので「最小限」という形容詞はありえない。常に0だ。

 コンパイラ、あるいはインタープリタが実行可能な時点で、プログラム言語というのはコンピュータによって理解可能だ。コンピュータは淡々と命令をたどって実行を続ける。

 プログラマソースコードを見て、コンピュータの真似をして淡々と命令をたどって実行すると何が起こるのかを調べることがある。すっきりとした構造のプログラムであれば、ソースコードからどのような実行結果が得られるか理解しやすい。

 この部分を僕は「処理」と呼んだ。

処理が明確で意図が不明のコード

 さて、ここに、intの引数をふたつ取り、intを返す関数f(m, n)があったとしよう。

1. 入力を m, n (m ≧ n) とする。
2. n = 0 なら、 m を出力してアルゴリズムを終了する。
3. n が m を割り切るなら、 n を出力してアルゴリズムを終了する。
4. m を n で割った余りを新たに n とし、更に 元のnを新たにm とし 3. に戻る。

wikipedia

という仕様が与えられた。ある程度腕に覚えがあるならば、これをすっきりと分かりやすくコードにすることが出来ることだろう。

 さて、このコードを書いた意図はなんだろう?

「m を n で割った余りを新たに n とし、更に 元のnを新たにm とし、m を n で割った余りを新たに n とし、更に 元のnを新たにm とし・・・」ということをやるという意図だろうか。*2

 違う。

 分かる人には分かったと思うが、このコードは最大公約数を取得するという意図で書かれた。ここではアルゴリズムユークリッドの互除法を採用している。しかし、「最大公約数を取得するという意図」が明確ならこのコードを捨てて別のアルゴリズムに差し替えても構わない。これがプログラミング実習の課題で「ユークリッドの互除法を実装する」という課題なのだとしたら別のアルゴリズムに差し替えてはならない。

 そのコードで何をしようとしたか。それを僕は「意図」と呼んだ。

 コンピュータはそのコードを読むにあたって「最大公約数を取得する」なんてことは考えない。
 プログラマはそのコードを読んで「最大公約数を取得する」のだろうと考える。
 「コードのあるべき動作」をイメージしながら僕らはコードを綴る。

ソースコードが持つ二つの側面

 ここでソースコードが持つ2つの側面を指摘した。ひとつは、コンピュータによって淡々と動く「処理」そのもの。もうひとつは、その「処理」によって何をしようとしたのか、何のためにそのコードが書かれたのかという「意図」だ。

 冒頭で心脳問題を取り上げた理由が分かってきただろうか。

 ソースコードを読むとき、僕たちプログラマは処理を追いかけ、意図を探る。だからこそ、「処理」の掴みやすさ、追いやすさといったものと、「意図」の掴みやすさを混同する。「m を n で割った余りを〜」をいくら饒舌に語られても「最大公約数を取得する」という一言の意図を掴むことは容易ではない。

 僕らは動いているプログラムを見て、中身が見えすぎるのだ。あるいは中身を見てプログラムの実行時のふるまいを想像でき過ぎるのだ。心の脳の境目を見失ってしまっているのだ。

 ソースの読みやすさとコメントについての議論ではこうした似て非なるものがそこに潜んでいる。だから、ねじれの位置にあるような意見が、交わることもなく平行線になることもなくすれ違い続けることになる。

コードから意図を読むとはどういうことか

 ここまでで、処理(コード)=脳、意図=心とその両者の違いを述べてきた。

b:id:daisuke-m コメントだけではなく。コードは「処理」だけではなく「意図」を表現することもできる。という話を先日同僚とした。だが、表現しきれない部分はコメントで補足する。

はてなブックマーク - コードコメントに書くべきは「意図」 - プログラマーの脳みそ

 経験を積んだプログラマならば、コードから意図を読み取れる。会話などなしにソースコードの内側に潜って考えが読み取れる不思議な夜を過ごしたことのある人も多かろう。ソースコードから意図を読み取る」というのは経験によって「こういうコードはこういう文脈で使われることが多い」を知っているからなせる技なんだ。


 定番のアルゴリズム
 定番のデータ構造。
 あるいはデザインパターンと呼ばれるもの。


 こうした「定番」に併せて書かれたコードは、経験によって先読みが利くため「読みやすい」

 僕はコードで「意図」を表現するのは、あくまでそうしたプログラマの経験を前提とすると考えている。例えばJavaのようなオブジェクト指向万歳という言語での定番表現と、Scalaのような関数型プログラミングができる言語での定番表現は大きく食い違う。それをコードから「意図」を読め、では多人数での開発なんてやってられない。システム開発はヒューマンエラーとの闘いなんだ。

 だから僕は、「コードによる意図の表現」の限界は相当に低いところにあると思うし、本質的に「意図」はプログラミング言語で表現できるものではないと考えている。あるのは「意図」が推測しやすいプログラミング言語表現じゃないだろうか。

トリッキーなコード

 ソースコードのコメント率は20%を切ることが望ましい : 小野和俊のブログで挙げられていたトリッキーなコードというのはなんだろうか。

 さきほど「ソースコードから意図を読み取る」というのは経験によって「こういうコードはこういう文脈で使われることが多い」を知ることで為されているといった。そこからすれば、トリッキーなコードというのはプログラマの経験則から外れるコードと言える。

 「定番の処理」という常識が、ハードウェアやソフトウェアのアーキテクチャといった時代背景で異なってくることから、「意図を読み取りやすいコード」つまるところ、「こう書いておけば分かるだろ。というか分かれよ、プログラマなら」というコードも当然ながら変わる。

 僕が働き始めたばかりの2000年前後ぐらい。Javaオブジェクト指向を学んで使い方が分かってきた、まさにオブジェクト指向厨まっさかりの頃に、ポリモフィズムを用いたコードを書いた。当時のリーダーに「どうしてこういう読みにくいトリッキーなコードにしたんだ。直せ」と言われたのも今ではいい思い出だ。*3

 例えばGoFデザインパターンVisitorパターンをトリッキーなコードとみるか。あるいは意図の読み取りやすい定番コードとみるかはプログラマの主観によるだろう。

 VisitorパターンはGoFデザインパターンの中でもっとも複雑と悪評高いがハッカーたちを相手にVisitorパターンの採用理由を説明する必要はない。*4GoF本を読めばVisitorパターンの効能も適用するべきシチュエーションも、止めておくべきシチュエーションも書いてある。

 じゃぁ万人が分かるコードだと何の説明もなしにVisitorパターンを採用したAPIを公開すればそれで大丈夫だろうか。「トリッキー」というのがどれだけ主観的なものか容易に想像がつく。

理由ではなく「意図」である理由

 いろんな事情が噛みあって、いたしかたなくトリッキーに書かざるを得ないコードというのはある。そういうとき

b:id:r-west 書くなら「理由」
b:id:llill 私はほとんど書かないけど、書く時はたいてい言い訳を書いてる気がする。別の意味で見苦しいコードに。

はてなブックマーク - コードコメントに書くべきは「意図」 - プログラマーの脳みそ

ということになるのだけど、それは必要悪と意識して書かれるコードに限られる。

 たとえば、プログラムを始めたばかりの初心者の書くコードはトリッキーだ。

本人がトリッキーではないと確信して書かれるコードと、トリッキーなコードとの線引きはどうしたらよいのだろう?

 あるいは、プログラム初心者が書いたようなプログラムというのは、その本人が考え得た精一杯であって、
普通とかトリッキーとか考えてる余裕なんぞこれっぽっちもない。
そして、大抵はそのコードは未熟さゆえのトリッキーなものとなる。
ならば、初心者は常にコメントを書くべきだろうか?書くとしたらどのようなコメントを?

コードコメントに書くべきは「意図」 - プログラマーの脳みそ

書くべきが「意図」ではなく「理由」としてしまうと、こうした部分を取り漏らしてしまう。

 やりたいことがあった。精一杯頭をひねってコードを書いた。洗練されていないからトリッキーになってしまった。そうしたとき、「理由」は「今の自分の精一杯です」になってしまう。そんなコメントはいらない。だからそのソースコードという脳を稼働させてどのような心を作ろうとしているか、つまり「意図」を書けと提案しているのだ。

 自分がこのコードで作ろうとした心は何か。それを書く。常に書く。

ソースから意図が読めるべきというのは

 ここまで「コードの構造」と「コードのあるべき動作」は別だということを述べてきた。「処理」と「意図」だ。「脳」と「心」だ。

 経験を積んだプログラマは「処理」から「意図」を推測する。「脳」を見て「心」を推測する。そしてそれは経験によってもたらされると述べた。だから、ソースから意図が読めるべき論というのは、経験則に則ってコードを書けに他ならない。

 経験則に則って書くことで、処理の見通しはよくなることだろう。
 処理の見通しがよければ意図を読むのに集中できる。

 ん?何を言っているんだ。

 そんなの推測しなくてもコメントされていれば推測処理をする必要がないじゃないか。

 何?このコードには意図がコメントされていない?

 読み取った意図をコメントに残せばいいじゃないか。DRY原則に則って解析処理は1回だけにしておこうぜ。

意図の更新

 コードに書かれた意図は、修正よって方針転換することがある。

masfj コメントに書かれた意図を組んで修正してもコメントは残ったままなので、さらにそこに何かしようとするときにコメントとコードに齟齬があるために困る、という事が最近あった

はてなブックマーク - コードコメントに書くべきは「意図」 - プログラマーの脳みそ

 こうなってしまうと、意図を尊重するべきか、コードを尊重するべきかわけがわからなくなってしまう。だから、本来はこういう用途で作られていたけど拡張して汎用的にしました、という場合に「ここの用途はXXX用に限定しているから云々」みたいなコメントがあれば除去しなければならない。

 除去するといっても単にコメントを削ればいいわけではなく、コードを修正してそのような意図を消し飛ばさなくてはならない。「限定的にXXXに使いたかった」が拡張した時点でその意図は上書き更新されるべきだ。

 そして、自分の手で更新したのではなくとも、乖離を見つけたのであれば常に直すことを努力すべきだ。自分の手で直せないなら直せる人に依頼するでもいい。「自分に言われても」と見て見ぬふりをし続けると、やがて自分に厄災が降ってくる。情けは人のためならず*5という。

 誰も意図がつかめなくなってしまったコードがあったとしても、それはもう使い物にならない。

僕は、コメントはid:Nagise さんのおっしゃる通り、意図を書き記すべきだと思っています。ただ、僕が直面したように、コードとコメントの齟齬が発生する場合もあります。故に僕は、日付と意図を書いて、その後、そこをさらに修正する時は、そのコメントの下に(つまり以前のコメントを消す事なく)新たに日付と意図をコメントとして記述するようにしています。

とはいえ、切羽詰まっている時はコメントを書いている余裕なんてないので、そういう時のコードを直すときにどうしたもんかと……。

コメントに書く内容について - とあるぼっちの生存報告

 あるある。わかるわかる。

 でも、忙しいときほど、そこで手を抜かない方がいい。こういうところで手を抜くと、それが原因で同じコードの意図をつかむために何度も読み直すことになったりするからね!

 呼吸をするぐらい自然にコード中に意図を記せるようになりたいものだけど、どうにも手が回らないならせめてこう書いておこう。

FIXME コメントと実装が乖離しています

 妥協の極みだね orz

*1:心身問題 - Wikipediaの派生

*2:自ら考えることをせず、提示された仕様をコードにするだけの人にとってはそういう「意図」でコードを書いたと言えるかもしれない :-P

*3:GoFのStrategyパターンというやつ。当時の自分はパターンを使うのに必死でいささかやりすぎた反省はある。それは割り引いて考えるべきだろうが、オブジェクト指向への理解度の低い人にとってはやはり読みにくくてトリッキーだろう。僕は「今ではいい思い出」と言いきったけど、現在進行形でこうした意見と闘っている人がいるとしたら、がんばれとエールを送りたい

*4:java-jaとかの懇親会とか行くとかなり普通に通じる

*5:本来の意味で。いや、もうこの断り書きが無粋の極みだが。