http://partake.in/events/feac18f7-0b78-4129-92c5-ad0cce8feafb 行きました。とりあえずDに興味がありそうな人を一カ所に集めるから後は好きにしろ、 的な repeatedly さんらしい豪快な集会でしたが6時間ずっと部屋中盛り上がっていて面白かったです。
僕は toHash/getHash 周りの話(後述)を、誰か捕まえて議論しに行こうと思っていたはずなのですが、 問題であると僕が思うコードを人に説明するだけしてネタを振った辺りで突然飽きて、 他のことをずっとやっていました。ザ・無責任。
他のことというのは、半年ちかく前に見つけた Issue 5184 - throw ClassName.templatedStaticMethod(...) cannot be parsed このバグです。なんでこんな挙動になってしまい得るのか興味深いな、と思ったことを思い出したので、 dmd のソースコード読みたい人は題材としてどうですかー! と叫んだらちょうどソース読んでたらしい lyrical_logical さんが捕まりました。やった。 あと ku6_jp さん(でよろしかったでしたっけ、人の名前記憶力に自信がない…(汗) を加えて3人でああだこうだ言いながら追っかけていた結果、 無事追い切れたのでパッチ投げてみました。 詳細は http://d.hatena.ne.jp/lyrical_logical/20110409/1302370303 りりかるろじかるさんのまとめ記事をどうぞ。
その後はいろんな島をフラフラ渡り歩いておしまい。
例に使ったコードは http://ideone.com/m1UK2 これです。 どう解決すべきか自分の中でまとまったらバグレポかパッチ投げたいと思っているのですが、 考える時間がとれていないという。以下メモ書き。誰か整理して。
(1) 動的配列のハッシュ関数
TypeInfo_Array.getHash
の
return hashOf(a.ptr, a.length);
は明確なバグで、
return hashOf(a.ptr, a.length * value.tsize());
であるべき。lengthは要素数なので要素サイズを掛けないと全体のバイト数にならない。
(2) しかし、それ以前にここはそもそも hashOf
でバイトイメージとしてハッシュ値を計算する実装をするの自体が誤りではないか。
数行下の TypeInfo_Array.equals
が配列の要素by要素の equals
を判定しているので、
ハッシュ計算も同様に要素毎に getHash
を再帰的に呼び出すべき。
ここでDの言語仕様とかで明言されているわけではない
「TypeInfo.getHash
と TypeInfo.equals
は常に一貫性がある(equalsならばgetHashが等しい)べき」
という条件を持ち出しているわけですが、これは認められるのではないかなー。
(3) TypeInfo_Array.equals
の実装が"間違って"いるにも関わらず、
組み込みの連想配列のキーとして動的配列を使ったときは、何故か"正しい"動作をする。
何故かというと、dmd はTypeInfoのインスタンスを減らすためなのかなんなのか、
ユーザーに見えない (typeid式などで取得できない) 部分ではいろんな型の TypeInfo
を共通化した内部専用 TypeInfo を使うようになっていて
(typinf.c)
オブジェクトの配列の場合
TypeInfo_AC
が、それ。そして、こちらの getHash
の実装は、"正しい"。
(3.1) なので連想配列のキーとして動的配列を使うときにはハッシュは "正しく" 使われるので、
ならそのまま現状維持でも困らない? と思いきや、 この TypeInfo_AC
はユーザーコードから使えないので、結局ユーザーから実行時型情報で "正しい"
ハッシュ関数を呼べないことを意味していて、問題だろう、というのと、あと、
「配列の配列」とかをキーにすると結局"間違ってる"方の TypeInfo_Array
が使われるので、やっぱりダメ。
(4) 等値性とハッシュ値に一貫性持たせる気ないだろ、
っていう問題はランタイムライブラリではこの配列の部分だけだと思うんですが、
標準ライブラリの Phobos でもこれが問題で、
opEquals だけ定義していて toHash がない(無いのでデフォルトのバイトイメージからのハッシュ計算がされる)
クラス/構造体がほとんど。std.bigint.BigInt
、std.bitmanip.BitArray
、
std.container
のコンテナ各種、std.datetime.SysTime
、std.typecons.Tuple
などが該当する。これらには toHash
メンバ関数があるべき。
現状ないので連想配列のキーにできなくて困る。
Variant
と std.xml
はちゃんと toHash
を実装している。えらい。
ここでは
「.opEquals
と .toHash
は常に一貫性があるべきだ」
という条件を持ち出しているわけですが、これもequals/getHash同様妥当だと信じている…。
(4.1) 他はともかく、Tuple.toHash
の実装には TypeInfo
経由の汎用ハッシュ関数呼び出しが使いたくなるので、配列の getHash
が変だとなんか困るので、
まず(2)が直ってて欲しい、という気分的な依存関係が。
(5) (4) に関連して、クラスや構造体に opCmp/opEquals/toHash
を定義するためのヘルパーライブラリが標準にあっても良いんじゃないかと思っている。
メンバのリストをaliasで渡すと、その辞書順比較に相当する opCmp/opEquals/toHash
を生成してくれるmixinテンプレート。
あとついでに toString
もあったら便利かなーという思いが。
こういう奴。
こちらではご報告していませんでしたが、そういえば、ポスドク → ソフトウェアエンジニアに転職しました。 先週から Google (の東京オフィス)で働いています。 仕事でメインに使うプログラミング言語でいうと OCaml → C++ という変化です、多分。 まだ始まったばかりで、おっかなびっくりしながらコード読んだり書いたりしてますが、 楽しく面白いものを作れるといいなあと思います。
学問の世界から産業の世界へ転職というと 「どういう心境の変化が?」 的な質問をされることが多いのですが、 ええと、正直、特に何も考えてませんでした、すみません…。
なんだろう、 (1) 面白そうなネタが転がっていそうで、しかも、(2) 自分の能力的に壊滅的に不得意ではない、 ならば、自分の中ではそれ以上区別してませんでした。 研究するのもコード書くのもどちらも好きですし、 どちらも得意とは言わないけれど自分の他の酷いスキルと比べればマシな方でしょうし。 今のところ、自分のやりたいことは健康で文化的な最低限度の生活さえ営むことさえできれば個人でできると思っていて、 なので 「この分野にいなければ」 というような考え方はしていなかった/していない感じです。
というわけで、やけに無気力な新年度エントリになってしまいましたが、 本人これでも気力に満ちあふれているつもりなので、これからも新しいところで頑張りたいと思います。