日別アーカイブ: 2016年7月25日

プログラミング小噺三題

Python で OOP

Python のクラスオブジェクトの初期化処理は__init__メソッドに書くと思っていた。いや、実際に__init__がその役割を持っているのは事実だが、なんと、クラスを継承した場合、継承されたクラスの__init__は何もしなけりゃ呼ばれない。えぇぇぇえ。なんでこんな仕様なのか、色々すったもんだがあって今更変えられないとかパラメタ付きはどうするとか面倒があるんだろうけど、ともかくきっぱり呼ばれない。一番元のクラスを定義するときにobjectを継承するとsuper()関数が使えたりするが、super(ClassName, self).__init__()なんて記述は呼ばれないからこそ書かなくちゃいけないわけで、たいした解決にはならない。しかも、多重継承した場合、super().__init__()では最も左に(最優先の位置に)記述されたクラスの__init__しか呼ばれない。いったいどうしろというのだ。クラスの意義の一つには実装の隠蔽があるはずなんだが、objectを継承しているのかとか、__init__が定義されているのかとか使う側がソースレベルで知らないといけないってのはどう考えても間違っているだろ、これ。

他人の書いた Python プログラムのメンテで困ったことになる。継承されちゃってるクラスにインスタンス変数を用意したかったのだが、__init__メソッドがない。ってことは継承してる先では__init__を呼んだりはしてないわけで、__init__を加えるとしたら継承してる先(当然複数箇所)全部に__init__を呼ぶ処理を加えなくちゃいけない。いや、そもそも継承してる先にも__init__なんてないので、ってところで気付く。汚染じゃないか。__init__汚染とでも名付けようか。

しかたがないので、インスタンス変数アクセスに getattr() 関数を使う。格好悪いし馬鹿馬鹿しいが、そういう言語でそういうシステムなのだと思うことにする。迷惑ついでに迷惑なのが__call__メソッド。インスタンスを関数として呼んだ時に来るエントリー。困ったねぇ、検索性ゼロじゃん。どこで呼んでるかのキーワードがなくなってしまう。機能の一意性とかメンテナンス性とか捨てて「ヘイ、こんな風に書けちゃうんだぜ、格好良い」って思いたけりゃ使えばいいけど、仕事で使うのは勘弁してくれ。

加えて最悪なのは__getitem__メソッド。インスタンスをマップとして呼んだ時に来るエントリー。これも検索性ゼロだよ。どうやってメンテすんだよ。しかも__getitem__メソッドだけでも使えるのだが、その状態でインスタンスを参照すると {}(Pythonでの空のマップ)になる。これ、if 文で False 扱いだぞ(インスタンスの存在を単純な if 文で判断できない)。

教訓。Python で OOP は真似事程度にしておけ。あと、3000 行超えるとか複数ファイルになるなら別の、できたら静的型付けの言語にしておけ。

必殺技

C で開発していた頃、ある程度の規模のプログラムに発展すると構造体に関数ポインタを配置して OOP 的なアプローチになったりしていた。デバッグとかメンテナンス性は悪くなる。その後 C++ を使うのが一般的になり、メンテナンス性はまだしも C++ に対応したデバッガでのデバッグは容易になった。
メンテナンス性を下げる最大要因は同じ名前のメソッドがひたすらあちこちにあること。ポリモーフィズムの暗黒面がメンテナンス性を下げる。標準的すぎるメソッド名に重要なロジックを隠しちゃダメってことだ。名が体を現してるうちはまだいいが、そんなものはすぐ破綻して、なんでこんな名前のメソッドでこんなことをってなる。

名前があればまだ良い方で、C++ にはオペレータという必殺技がある。もちろん殺されるのはメンテナ。記号に独自の意味を付加するのは勘弁して下さい、マジ迷惑。Scala は謎記号の宝庫で、そこが微妙に好きになれない理由の一つ。Dispatch とかできたら使いたくない。 無駄に継承が深いのも考えもの。ロジックとして1ファイルに収まってて欲しいようなことまであちこち見ないといけない。書いた奴はメンテナンスのことなんて考えてない。

さて、ある程度の規模のプログラムがさらに発展するとデータのやり取りに柔軟性を持たせるため、特定処理向けインタプリタになってしまうことがある。もちろんデバッグとかメンテナンス性は死ぬほど悪くなる。普通、独自インタプリタの独自スクリプトにはドキュメントは無く、加えて文法エラーすら教えて貰えない。 独自インタプリタって、作るのはきっと楽しいんだろうなぁ。でも俺にメンテナンスさせないで下さい。

JavaScript さんこんにちは

某掲示板を読んでいたら『未だに HTML の飾り付け程度に思ってる人は別だけど、経験上、その手の人は他の知識も時代遅れだったりするからめんどくさい』なんて書き込みがあり、JavaScript なんざ HTML の飾り付け程度、と思っているのでもうちょっと調べてみようかと思った次第。

JavaScript のブラウザ以外での環境を一変させたのが Node.js。Node.js ってjQuery のような何かを実現する JavaScript のライブラリかと思っていたら、JavaScript 実行環境も含んでいた。.js って付けるなよ紛らわしい。C# における.NET Framework。それなら納得。Node.js をインストールする。

Node.js に標準に備わってる ZLIB の説明を見て(あぁ、文書化されてるって素晴らしい)主に gzip や zlib の話なので所謂 ZIP ファイルは標準では扱えないということのようだ。これでモチベーションは半分以下になる。npm で各種ライブラリをインストールするそうな。調べるとなんとも乱立状態のカオス。OCaml のOPAM やHaskell の Cabal や Python の PyPI でわかるように、碌な文書化もされてない寄せ集めで何をしろと、「こんなもの」をどうしろと。モチベーション駄々下がり。もう俺「他の知識も時代遅れだったりするからめんどくさい」でいい。そもそも Node.js はWeb サーバを構築するためのツールなんだし。いや、それでも ZIP ファイルは扱うだろ。仕事でメンテしてる Scala + Lift の Web サーバも ZIP ファイルは扱ってるぞ(たまたま Java のライブラリに標準で入ってるだけとも言えるが)。

Electron も Node.js を使ってるんで、標準で用意されてない処理でカオスなのは必至。ちゃんとした ZIP ライブラリを探すところからってのは泣きそう。とはいえ、OCaml の状況に比べれば利用者が多いというだけでも Node.js の方が断然有利。多分。

ともかく、JavaScript をいわゆる LL 的にサクっと書いてサクっと動かすには Node.js が適切なのだと判断。やっぱり名前がおかしいよなぁ。.js は余計だろ。で、しかも Windows と Linux で、多分 Mac OS X でも同じ JavaScript で同じように動くプログラムを書けそうだ。ココ重要です。はっきり言ってこれ(マルチプラットフォーム)がなけりゃ手を出してない。

Node.js のライブラリ文書をざっと眺めると、それなりに、特にネットワーク関連が充実してるようだ。でも多分 Python の方が充実してる。前述したように ZIP ファイルは標準では扱えない。何故か XML も標準では扱えない。なんだってぇええ。モチベーションが負の数になっちゃうよ。なんでこんなのが流行ってるんだろう。
Unix システムコール程度のライブラリ環境でも書ける程度のサンプルを書いてみる。OCaml では色々と標準的にあってもよかろうにと思う程度に足りない部分があったが、Node.js + JavaScript ではそういう部分はなかった。Array と Object と String が機能豊富だからだろう。Scala や Python と同程度の記述で書ける。
どんな素晴らしい言語でもへっぽこな環境やライブラリしかなかったら見向きもされない(OCamlがまさにそう)。どんなしょぼい言語でも適切な環境やライブラリが他に無いなら使われる(かつてのUnix/DOS/WindowsとC)。JavaScript は言語仕様は面白いんだが、開発実行環境はまだ微妙ですな。