2010-05

2010-05-01 

久しぶりの更新がこれというのもかなりあれだと自分でも思うのだが……。

悪事千里を走るというが、こういう記事ばかりが目につくせいで俺にとっての「@IT 自分研究所」の印象は「極めてたちの悪い釣り堀」である。

それはともかく久しぶりに凄いものを読んでしまったので、ちょっとだけ突っ込んでみる。

もっとちゃんとした突っ込みにはえらく労力がかかり、俺はそんな労力を出す気力がないのでやめとこう。しかしコメントを見ると他人の意見を全然聞いてないというか、自分のやり方を疑うという発想がないというか。オブジェクト指向プログラミングについて知らない事自体は別に致命的に悪い事じゃないが(だって勉強すりゃいいだけでしょ)、こういう裸の王様的なメンタリティは周囲にとっては迷惑千万。というわけで、こういうアレがプロジェクトにいるとどれだけ周囲が悪影響を受けるかについてちょっと書く。


えーと、仮に A 社の I さんとします。 I さんは A 社でもトップクラスのプログラマで、 I さん擁する A 社のプログラマはみんな自信・やる気共に満々でプロジェクト(仮に Z とします)に参画してきました。そのプロジェクトが終わりの見えない鉄火場だとも知らずに。

I さんたちはそれまで比較的規律の緩いプロジェクトでしか働いたことがなかったようで、プロジェクト Z の開発規約・プロジェクト標準・検証ステップの厳しさに面食らっていました。実際には I さんたちの驚きとその後に抱いた疑問にはもっともなところも多々あったのですが、郷に入りてはなんとやらという奴で、政治力を発揮しない限りはそのルールに従わなければ全体に大変な迷惑がかかります。

異変が起きたのはプロジェクト Z のとあるミーティングの場でした。意欲旺盛な I さんは「プロジェクトの助けになれば」との思いから、開発を容易にするためのモジュール群をミーティングの場で提案しました。それ自体は大変喜ばしいことで、そういった意欲のある人たちと働きたいものだなあと思えたものですが、実際にはそんないい話で終わるはずもありませんでした。

まずプロジェクト Z は大変成果物の管理が厳しく、事前に作成することを申請していないモジュールを納品するのは不可能に近いぐらい困難でした。また納品物には開発したパートナー企業によるチーム内検証、元請けによる検証、クライアントによる検証と検証のステップが何段階にも及び、さらに単体テストの実施と検証も必要なので、それぞれにかかる時間を計算すると、明らかに当初のスケジュールを大幅にオーバーしそうな勢いでした。当然スケジュール管理も大変に厳しいプロジェクトであり、一人の開発者の思いつきでスケジュールにインパクトを与えるような提案を各社の揃うミーティングでいきなり投げるのは些か問題があると言わざるをえないでしょう。

というわけで、「自分の考え・やり方は正しい」で止まってしまって周りの見えてないガキがプロジェクトにいると、そいつを諫めたりスケジュールなどの帳尻合わせをしたりといった極めてストレスフルな作業が発生し、他のメンバーにとっては迷惑なだけというお話でした。実際には「正しいか否か」「必要か否か」というのは提案を行う上では単に前提条件でしかないので、その提案が通った後で発生する作業などをすべて込みで計算しておき、「このモジュールの作成作業は別の関連モジュールの開発スケジュールに入れ込むので、リスケジュールの必要はありません」「このモジュールを作るのにかかる工数は開発効率の向上で十分ペイできます」などといった形できちんと周囲を説得しなければ、通るべきものが通らないという事態に陥ってしまうわけです。

もっともその A 社の書いたコードを検証したところ、ちょっとこれは人間としてどうよな突っ込みどころが多々見つかったので、彼らが本当に良いものを提案しようとしていたかについては疑問が残ったようです。次はその A 社のコードを検証しようとしたところ、「お前が読んだら血の雨が開発室に降るからやめろ」と先輩に止められた、歩く核弾頭エンジニア J さんのお話です。


J さんはプロジェクト Z から離任したあと、いろいろあって B 社が受託したプロジェクト V の技術調査の仕事に配属されました。そのプロジェクト V は C 社の開発したパッケージを可能な限りそのまま D 社の手がけたフレームワーク上に移植し、システムの柔軟性の向上を図るというものでした。実は事前に D 社のお偉いさんから「できるわけねーだろ。本当に移植するなら全部書き直せ」という大変に有難いアドバイスを頂いており、そのお偉いさんの実力を知っている J さんは十中八九その通りだろうと思いつつも、仕事なので仕方がないと現地入りしました。

そこで J さんに渡されたパッケージはもう酷いの一言で、どのリポジトリからとってきたのか、どういう手順でパッケージングされたのか定かではなかったそうですが、まずそもそもコンパイルが通らないソースコードを手渡されて本気で泣きそうになっていました。コンパイルの通るソースを手にしたら手にしたで根本から設計がトチ狂っており、システム全体で Struts でいうところの ActionForm が一個しか定義されていない事が発覚した時には J さんを初め調査チーム全員が腰を抜かしました。他にも実に愉快な設計が随所に見られ、数日も経った頃に J さんは「本気でこのシステムの権利が欲しい。このソースを世に出してシステム設計べからず集として活用したい」と言い出す始末でした。

その後も J さんの元に届けられた設計書に明らかに嘘が書いてあったり、ソースコードのバージョンと整合性が取れていないなどの問題が山のように出現、いい加減 J さんもうんざりしていた頃に、そんな愉快なシステムを構築した C 社のエンジニアは自分が凄くいい仕事をしたつもりになっているらしいという怪情報が入り、その時ばかりは久しぶりに盛大にキレそうになったそうです。とはいえ J さんも大人、調査結果の報告書に「このシステムを作った奴を縛り首にしろ」と書きたい衝動を必死で抑え、普通の報告書を作成して提出したそうです。

というわけで、最初に開発・運用・販売した人たちはそれでいいと思っていても、その後のある時点でとてつもないカタストロフィが発生するというケースは実際にあり、またそこで割を食う者とそのシステムで儲けている者は必ずしもイコールではないので、クソな技術力に無自覚なまま開発を進め、万が一それが形になってしまうといろいろ後味の悪い事になってしまうというお話でした。技術力と収益は必ずしもイコールで結ばれないとはいえ、儲かってんだからクソなコードを量産してもお咎めなしというのは、その後割を食う人の事を考えると倫理的には相当な問題があるわけです。


えーと、何が言いたいかというとだな、仮に実力があっても周囲を顧みない野郎はチーム開発じゃ足手まといになりうるし、実力がなくて「俺が正義」な野郎は本当に足手まといでしかない。件のコラムを書いた野郎はどう考えても不勉強で、そういう奴でも上手く立ち回れば世渡りできるのだろうが、その結果として割を食う人たちがそれなりにいるであろう事が予測される。

2010-05-04 

twitter に書き飛ばそうと思ったけど、収まるはずもなかったんでこっちに。

現時点での Caty スキーマは再帰的な型をサポートしていないため、次のようなスキーマが書けない。

type Tree<T> = {
    "left": T | Tree<T>,
    "right": T | Tree<T>
};

実際のところ再帰的な型が扱えないのは致命的な問題なので、これはなるべく早くサポートする。最初は型検証の無限走行の可能性が気になったが、型検証は有限ステップで終了するのはまず間違いないのでそこは問題にならない。無限長データを Caty はサポートしていないため、上記の Tree の型検証の場合、 Tree<T> のプロパティの値は T 型か、あるいは Tree<T> であるため、いつかは末端ノードが T となって成功するか、 T でない末端ノードが出現して失敗する。

問題になるのは型推論および静的型チェックで、これはちょっといろいろ資料に当たらないとまともな実装がちと難しそうだ。話を簡潔にするために、型変数を含まない次のような型を考えてみる。

type Tree = {
    "left": integr | Tree,
    "right": integer | Tree
};

ここで別のモジュールで定義された Tree とまったく同じ構造を持った AnotherTree という型があったとする。両者は別名の型なので、 Tree 型の出力を AnotherTree 型の入力に接続する場合、型推論と静的型チェックを走らせて型の整合性をチェックしなければならない。そして現在の推論ルールでは、どうもこれは無限走行するケースがありそうだ。ちょっと型チェックの過程を大雑把に書いていくと、

Tree
AnotherTree
    -> 両者とも object なので各プロパティについて検証
    Tree.left = integer | Tree
    Another.left = integer | AnotherTree
        -> Tree.left = integer かつ AnotherTree.left = integer => 成功
        -> Tree.left = integer かつ AnotherTree.left = AnotherTree => 失敗
        -> Tree.left = Tree かつ AnotherTree.left = integer => 失敗
        -> Tree.left = Tree かつ AnotherTree.left = AnotherTree => 各プロパティについて検証
        Tree
        AnotherTree
            Tree.left = integer | Tree
            Another.left = integer | AnotherTree
                -> Tree.left = integer かつ AnotherTree.left = integer => 成功
                -> Tree.left = integer かつ AnotherTree.left = AnotherTree => 失敗
                -> Tree.left = Tree かつ AnotherTree.left = integer => 失敗
                -> Tree.left = Tree かつ AnotherTree.left = AnotherTree => 各プロパティについて検証……って止まらねえよこれ!

こういう事が起こるわけだ。ユニオンの性質上、出力の取りうる型すべてが入力の取りうる型に対してチェック成功とならなければ、完全に成功とはならない。ところがそんな事をやってると、上記の通り無限ループしてしまう。ここに型変数が絡むと頭が痛くなってくるが、この問題を解決しないとなあ。

2010-05-13 

Wiki Creole は乱立しすぎてワケワカメな Wiki 記法の統合プロジェクトということで、「絶対に誰も従わねえだろ、これ。オレオレ Wiki 記法が簡単に作れる以上、みんなろくに調べずに作り出すはずだもの」と思いつつも、記法自体は趣味から外れるものが散見されつつも妥当ではあると思うので、一応 Caty プロジェクトでは全面的に採用しているのだが、どうにも仕様書と公式のテストケースを読めば読むほど微妙な部分が出てくる。

例えば Wiki Creole では strong や em は以下のような記法で記述することになっている。

You can make things **bold** or //italic// or **//both//** or //**both**//.

ところがこれ、実は閉じるための記号は省略可能と公式のテストケースに書いてあるんだよな。

Character formatting extends across line breaks: **bold,
this is still bold. This line deliberately does not end in star-star.

Not bold. Character formatting does not cross paragraph boundaries.

これだけでも十分酷いが、リンクというか URL 生書きの記法は輪をかけてひどい。

Creole1.0 specifies that http://bar and ftp://bar should not render italic,
something like foo://bar should render as italic.

つまり、 http(s) や ftp などは URL として扱い、不明なスキームは無視しろとある。そしてここからが酷いのだが、有効なスキームの一覧はどうも存在しないようだ。そら常識的に考えれば、ウェブブラウザで扱えて、なおかつリンクとして書き下せるスキームなんてたかが知れてんだから、それに合わせろ空気読めってことなんだろうが、それじゃ仕様にならねえんだよ。

ちなみに檜山さんより Wiki Creole の ANTLR 文法ファイルを教えてもらったのだが、この文法ファイルでは上記の URL の扱いをしていないようだ。

2010-05-31 

本日の誰得 Python Tips は、標準ライブラリの optparse のカスタマイズだ。なんか前にも書いた気がしなくもないが、まあいいや。今回はそれのアップデート版みたいなもんだし。

まず標準の optparse.OptionParser は -h/help といったヘルプオプションに行き当たるか、オプションの解析に失敗するかすると sys.exit を呼ぶというトンデモ仕様になっている。それがウザい場合は、 OptionParser のサブクラスで exit 及び error メソッドをオーバーライドすればよい。

また、 OptionParser はデフォルトでは引数が空のときに sys.argv を勝手に解析し出すという「小さな親切、大きなお世話」な仕様になっている。これをどうにかするには _get_args をオーバーライドすればよいのだが、これを次のようにしてはいけない

class MyOptionParser(OptionParser):
    def _get_args(self, args):
        if args:
            return args
        else:
            return []

何故なら OptionParser は内部で _get_args の戻り値に対して破壊的な操作を行っている邪神が降臨しっぱなしなクラスなため、 args の値が書き換わってしまうからだ。大抵はそれでも問題ないかもしれないが、思わぬバグの温床になりかねない。そのため、必ず args のコピーを返すこと。

ちなみにこれらの仕様は Caty の開発で実際にバグの原因となった代物だ。