Google Go

Go はなかなか面白そうな言語ですね。

コンパイルあり、静的型システムあり、ガベコレあり、並列実行サポートあり、Ken Thompson と Rob Pike のビッグネームありということで、しばらくは話題になりそうです。

実行速度は C よりやや遅い(10 〜 20% 程度)ということですが、コンパイルは驚異的な速さ。Tech Talk の Go 全ライブラリビルド実演では、プレゼンに使ってる MacBookPro(と思われる)でわずか 9 秒。いやはや凄まじいことになってます。今後、Generics のような機能拡張によって言語が肥大化していってもこのスピードを維持できるのかどうか。興味深いところです。

ガベコレや並列サポートも魅力的ですが、「軽い」型システムも気になります。例えば以下のケースで、C は A と B のどちらを実装したことになるのか。

type (
    A interface { func Foo() int; };
    B interface { func Foo() int; };
    C struct { x, y int; };
)
func (c *C) Foo() int { return c.x * c.y; }

ここでは A と B は同一のメソッドセットで構成されているのでどちらを実装しようが実質的な違いはありません。インタフェースが同じなら区別は不要、別名がついてるとでも思えばいいのでしょうか。

あるいは、C が A や B の存在を知らずに実装された場合、エクスポートされた C.Foo が意図せず A や B の実装として利用されてしまうケースがあるのではないか。これも A や B のように振る舞えるなら、もともとは意図していなくても、そう振る舞ってもいいよね、ということでしょうか。

もしそうだとすると、継承が前提の世界では考えられないことだけど、過去に作成したオブジェクトが、新たに作成したインタフェースに勝手に適合する、ということもありえてしまう。自由ではあるけれど、あまりにも風通しが良すぎるような。

あと、Go のインタフェースには従来の OOP で言うところの「契約」のような強制力もないので、コンパイル時に不完全な実装を検出することもできないのではないか(たぶん)。これはどう解決したらいいんだろう。ランタイムで落ちるんじゃないだろうか。

というわけで、現時点では Go 流の OOP というものがどう効果的なのか、良くわからない状態です。あーだこーだ言ってる暇があったら実験したほうがいいですね。

ふむ。ちょっと試してみますか。

追記

試してみたところ、心配は杞憂に終わりました。

package main

import "fmt"

type (
     A interface { Foo() string; };
     B interface { Foo() string; };
     C struct { };
     D interface { Foo2() string; };
     E interface { Foo3() string; };
)

func (c *C) Foo() string { return "Foo"; }
func (c *C) Foo3() string { return "Foo3"; }

func doit1(a A) string { return a.Foo(); }
func doit2(b B) string { return b.Foo(); }
func doit3(d D) string { return d.Foo2(); }
func doit4(e E) string { return e.Foo3(); }

func main() {
     c := new(C);

     fmt.Printf("doit1=%s\n", doit1(c)); // ok
     fmt.Printf("doit2=%s\n", doit2(c)); // ok
     //fmt.Printf("doit3=%s\n", doit3(c)); // build error
     fmt.Printf("doit4=%s\n", doit4(c)); // ok
}

コンパイル時に必要なチェックがかかり、Go のインタフェースが契約として機能しています。

馴染むにはもう少し使い込んでみる必要がありそうです。