Revision 5 as of 2011-04-30 12:35:38

Clear message
Locked History Actions

scala/structuralSubtyping

構造的部分型(structural subtyping)

むつかしそうな名前だが、やりたいことはきわめて簡単。 そもそもプログラミングを簡単にしたいがための仕組みなのだから。

やりたいこと

例えば、Javaでは以下のようには記述できない。

class Greeter {
  public void hello() {
    System.out.println("hello, world");
  }
}
class Executor {
  public void execute(Object someObject) {
    someObject.hello(); // Objectにそんなメソッドはない!コンパイルエラー
  }
}
...
new Executor(new Greeter()).execute();

つまり、Executor.executeメソッドは「任意の」オブジェクトを受け入れ、そのhelloメソッドを呼び出したいわけだが、コンパイラはコンパイル時にパラメータの型を調べて、それにhelloというメソッドがなければエラーにしてしまう。実行時にはhelloというメソッドの定義されたGreeterオブジェクトが渡されているにも関わらずである。

これはJavaなどの静的型付けの言語ではいたし方ないし、これがあるがゆえに実行速度が高いわけである。つまり、実行時に渡されたオブジェクトにそのようなメソッドがあるかどうかを調べなくて済むわけである。コンパイル時にメソッドの有無を判定済みだからである(もちろんリフレクションを用いて調べる場合は別)。

これに対して、ruby等の動的な言語は実行時にメソッドの有無を調べる。それがゆえにどうしても実行速度は上がらないし、完全なテスト(テストカバレッジ100%)が必要になる。例えば、スペルミスして「hell」などという呼び出しを記述してしまえば、そのミスはその行が実際に呼び出されるまではわからない。

※私見だが、これがゆえにスクリプト言語のプログラマは「スペルミスをしないように」長い名前を避ける傾向があるように思われる。しかし、短い簡単な名前ばかりつけていけば、大規模なプログラムでは同じ名前がそこかしこに存在することになり、リファクタリング時には非常な問題になる。「この名前とあの名前は同じだが、同じものを指しているのかわからない」という状況になりうる。静的に決定することができないのだ。スクリプト言語が大規模プログラム作成に向いていないのはこのような理由だ。

やり方

さて、Javaで上述のようなことをしたいならば、どうしても以下のように書く必要がある。

interface IHello {
  public void hello();
}
class Greeter implements IHello {
  public void hello() {
    System.out.println("hello, world");
  }
}
class Executor {
  public void execute(IHello someObject) {
    someObject.hello(); // IHelloにはhelloというメソッドがある。OK!
  }
}
...
new Executor(new Greeter()).execute();

説明不要だろうが、ここで重要なことはGreeterとExecutorが共に参照しなければならないIHelloというインターフェースの存在である。これが二つのクラスの架け橋となっている。

これに対して、Scalaでは以下のように書けてしまうのである。

class Greeter {
  def hello { println("hello, world") }
}
class Executor {
  type HelloType = { def hello }
  def execute(someObject: HelloType) {
    someObject.hello
  }
}
new Executor().execute(new Greeter)

つまり、Greeter側は何らのインターフェース(trait)も実装していないし、Executor側も何らのGreeterに関する知識はない。この二つのあいだには、何の架け橋となるものがない。

その代わり、Executorは単に自分の中で「helloというメソッドを持つ型」を宣言し、それを引数として受け入れるだけなのである。そして、executeメソッドは、この型のオブジェクト、つまりhelloというメソッドを持つ型を受け入れるというわけである。