「Stop Writing Classes」とか「頼むからプログラミングを学ばないでくれ」とか

後者なんか最初いわゆる釣りかと思ったが、読んでみたら至極まっとうだった。

過激なことを言えば人が集まる。炎上商法ちうヤツですな。

後者はこれ。前者は 2012 年の pycon なので相当季節外れですまん。これ:


「読む前」「観る前」にタイトルから双方に共通で感じたのは「プログラミングを目的化すべからず」てなことを言いたいのかなぁ、と思ったが、双方別にそういうことを中心に言ってるわけではない。特に後者なんかは「プログラミングを学ぶことを経済的な救済として人々に売り込むのは誠意のないことだ」も大事なファクターなんだろう。

とはいえやっぱり後者の「私が人々に測量技術を奨励する度合いと、プログラミングを奨励する度合いは、なんら変わらない」、そうそう、これこれ。

「プログラミングという行為」がそれ自身で「面白い」ことは否定しません。ので、「プログラミングを楽しむ」という意味において目的化するのは別に否定はしない。けど(ワタシも若い頃ほどそうだったけど)それが過度に行き過ぎるきらいがあるのね、多くの場合。それだけ病み付きになる要素があるんでしょう、プログラミングという行為そのものには。

没頭すればするほど周囲が見渡せなくなるのはこれは人間として当たり前のことで、だからこそ「で、これでワタシは何をしたかったんだっけか?」と自問する癖をつけないと、本質を見失いやすいわけですな。プログラミングという行為そのものはいつだってただの手段なはずなの。「それ自体が面白い」のだからそれを目的にすること全てを否定はしないにせよ、ただねぇそれ、「もったいない」し、人生損してるんでは、と個人的には思う。

実際問題後者の「頼むから~」は逆の側面を強調してもいるんだろう。私は「強迫観念で考えるなよなぁ」みたいなことをどっかで書いた通りでよく感じるんだけれど、要するに「やらなければならないことだから頑張って学ぶ、という指向そのものが NG」て話でしょう。面白がれるなら言われなくなって学ぶってば。そうやって自発的にやれるぶんには誰にも否定される筋合いなんかないし、大事なのはそれだけでしょう。(ちなみにワタシは学校で正規の「プログラミング」を学んだことはほとんどない。数値計算のために Fortran を少し習っただけ。)

前者の「Stop Writing Classes」は動画の説明文にある通り「Classes are great but they are also overused.」だけにスコープしたプレゼンテーション。ワタシが「観る前に」感じた「目的化してるからでしょ」な観点はたぶん入ってないけどまぁそういうことなんじゃないのかな、と個人的には思っている。いや、「強迫観念」の方かな、むしろ。

「大企業」ではよくこんなことが起こる:

  • 末端開発者は失敗を頻繁にしでかすほど「バカ」である
  • だから「コーディング規約」が必要だ
  • けれど「Python、なにそれ、食べれんの」程度の浸透度
  • 「コーディング規約作成担当」に割当たった哀れな「末端開発者」が自信満々で「間違いだらけの」コーディング規約を作る

「間違いだらけの」の半分は「過度のオブジェクト指向祭り」「過度のデザインパターン祭り」に関係する(残りはくだらない「誤った PEP8 引用」だったりする)。Python の場合、「目に見えるものはインターフェイスである」、つまり「引数の名前」はインターフェイスである。だからこんなのは原則ダメである:

ダメな「コーディング規約」の例
1 # 引数名は衝突を回避するためにアンダースコアで終わらねばならぬ!
2 def fun(hiksu1_, hikisu2_, hikisu3_):
3     pass
4 
5 # 引数名は衝突を回避するためにアンダースコアで始まらねばならぬ!
6 def fun(_hiksu1, _hikisu2, _hikisu3):
7     pass

本物の Python キーワードとの衝突を避けるためにアンダースコアを付けることはあるけれど、「利用者目線に立って」考えれば、

1 fun(hikisu1_="aaa", hikisu2_="bbb", hikisu3_="ccc")

が「不条理だ」ということはわかるはずなのだが、「オブジェクト指向」や「隠蔽化」「カプセル化」という観念にとらわれすぎているとなぜだか「呼ばれる側目線」でしか考えられなくなる傾向が強くなるようである。もともとオブジェクト指向だって「クライアントコード至上主義」から来ているものである。だから「クライアントコードを汚す」規約が「オブジェクト指向的な思考」に反することくらい、本当はちょっと考えればわかるはずなんだ。

で、大元の「Classes are great but they are also overused.」についてもやっぱり結局は「オブジェクト指向ねばならぬ」という強迫観念、が大半なんだろうなぁ、と思う。私は実際に「全てを class で書くべし」という、私から言わせれば「間違った」規約を渡されたことがあるが、当然これこそが「強迫観念」の結果である。

そもそも間違いだ、という以前に、「自分で class を書くことこそがオブジェクト指向の本質だ」と考えるまでもなく、Python や Ruby のようなスクリプト言語は「素でオブジェクト指向の恩恵を受けている」ことが忘れられすぎである。たとえば C++ では以下は「オブジェクト指向であるともないとも言える」例である:

1 template<It_, Fn_>
2 Fn_ for_each(It_ begin, It_ end, Fn_ callback)
3 {
4     for (It_ it = begin; it != end; ++it) {
5         callback(*it);
6     }
7     return callback;
8 }

今の場合 callback が class でないならば、「オブジェクト指向とは言えない」となるが、class ならば「オブジェクト指向の恩恵は受けている」ことになる。けれどもこのような template は「ジェネリック指向」の方を強調したものなのであって、「マルチパラダイムデザイン」であるところの C++ では別段「オブジェクト指向だけがネ申」なんてことは考えない。

けれども Python (など) ではこれとはほんの少し違う。同じように「関数型プログラミング/ジェネリックプログラミング」的な書き方:

1 def fun(iterable, callback):
2     for it in iterable:
3         callback(it)
4     # callback を「返却」するのは C++ template なイディオムなので python で
5     # これをすることはまずないけど対比のために返してみる
6     return callback

これは「あなたがどう思おうと」、iterable も PyObject、callback も PyObject なのだから、「正真正銘オブジェクト指向」である。class だからオブジェクト指向なのとちゃう

誤解の中心はつまり「class = オブジェクト指向」から来てる、てことなんだと常日頃思っている。

「過度のカプセル化祭り」は「@property」の過度の使用になって現れる。規約にこれを書かれたら最悪だが現実にそういう規約を渡されたことがある。当然「JavaBeans と同じようなことがしたい」のにはこれでいいのだ:

1 class Aaa(object):
2     val1 = 1
3     val2 = 2

けれども「カプセル化せねばならぬ」を強迫観念として規約化すればこれは「違反」となり、「こうせねばならぬ!」と主張するわけだ:

 1 class Aaa(object):
 2     def __init__(self):
 3         self._val1 = 1
 4         self._val2 = 2
 5 
 6     @property
 7     def val1(self):
 8         return self._val1
 9 
10     @val1.setter
11     def val1(self, value):
12         self._val1 = value
13 
14     @property
15     def val2(self):
16         return self._val2

これが強迫観念となるのも、「呼び出し側目線に立たないから」である。上の二つの例で val1 についてはそもそもクライアントコードは破壊されない。すなわち「同じ」である:

1 a = Aaa()
2 a.val1 = 1
3 print(a.val1)

java や C++ で「あとでどうせ必要になるから常に setter/getter 形式にしとけ」が説得力があるのは「呼び出し側が破壊されるから」である。かつ、「コンパイル型言語なのでオーバヘッドはさして問題にならない」からである。けれども Python などの言語ではそうではない。「@property」を経由したアクセスのオーバヘッドは決して無視できるほど小さくはないし、「setter/deleter を隠すつもりがないなら」呼び出し側にとっては「どっちでも同じ」である。だから…これは PEP8 にはっきり書かれているんだけれど、「そうしなければならないことはそうしなければならなくなってからやりなさい」ということである。

そういうことなんじゃないのかなぁ、と思うのだが、ただ 後者 の方はもっと人目を引くような過激な例な気がする。いくらなんでもこれは:

1 class MuffinHash(dict):
2     pass

と思う。思うけど java では本当に見たことがある。「HashMap をカスタマイズしたい」というニーズから直接 HashMap から派生する例は後を絶たない。私はそのために派生を使うのには強硬に反対したいところだが、ただ一般にはそう見做されない、つまり「是である」と考えられており、くだんの Python コードはそういった文化に直接影響を受けているんだと思う。つまり「いずれ dict をカスタマイズしたくなる」ことに備えてこうするわけだ。経験則から言えば、十中八九そんなことにはならない。なぜなら「素の dict」にはあなたの欲しいものが大抵全て揃っているからだ。(たまにないとはいわないけど。)

なんてなことをつらつらと考えた。目にはいっちゃうと反応したくなっちゃうてことか。なんかこういうの、ノイズになるから出来るだけ読みたくないんだけどねぇ。なぜノイズになるかは、大抵「異論反論祭り」が長大になるからね。皆結局のところこの手の議論に熱中しがちなのね。それこそが「あぁなんて無駄な時間だ」とはたと我にかえるようにしような。面白いからこそ、ね。