Go の型は First-class ではない、ということにゴールデンウィーク最終日に気づき、悶々としています。(何も Go が悪いわけではない)
ことの発端は、以下の様な関数を定義したところから始まります。
func getName(x interface{}) string {
return reflect.TypeOf(x).Elem().Name()
}
この関数に、任意の型のポインタをわたすと、型の名前を取得できます。けど、ポインタを渡さなければならないのですね。
var foo *Foo
name := getName(foo)
foo は使わないのに! 使わないというのはウソですね。けど、ちょっと違うんですよ。まあこれは私がPython 脳だからであって、別にGoが悪いわけじゃない。
何をしたいかと言うとですね、Google App Engine datastore のクエリの生成をラップしたいのです。標準ライブラリを使うと、以下のように書きます。
c := appengine.NewContext(r)
q := datastore.NewQuery("Foo") // ☜(◉ɷ◉ )
q.Filter("Y =", "xaxtsuxo")
foos := make([]Foo, 0, 10)
keys, err := q.GetAll(c, &foos)
Foo が型が定義されているのに、文字列で "Foo" を渡すのが悔しいわけです。そこはコンパイル時にスペルミスをひっかけて欲しいわけですよ。ストアされているデータと、Foo の定義が違うと、それはそれで実行時エラーになるんだけど。なので、
func createQuery(AnyType) *datastore.Query
のような関数を定義したいな、と思ったのですね。ところが、型は渡せないのです。以下のような方法に落ち着きます。
func createQuery(x interface{}) *datastore.Query {
kind := reflect.TypeOf(x).Elem().Name()
q := datastore.NewQuery(kind)
return q
}
…
var foo *Foo
q := createQuery(foo).Filter("Y =", "xaxtsuxo")
var foos := make([]Foo, 0, 10)
keys, err := q.GetAll(c, &foos)
あぁ foo 使わないのに。いや、 配列の foos があるんで、そっち使えよって話なんだけど。なんなら GetAll せずに、foo を使ってイテレートするのが筋なのかも知れません。
ああ、そんなこと言い出したら、Filter にだって文字列を渡してるぞ。そう考えると GAE/Python の NDB はよくできてるなぁ、あのライブラリ作った人は Python のことよく分かってるなぁ。薄いラッパにしたかったんだけど、やっぱり厚くなってしまうのかなぁ。PropertyList とか PropertyLoadSaver をうまく使ったらよいのか? ぎゃぁ。というわけで連休が終わってしまったので、ペースダウンしていきます。