以下の新機能紹介の部分には、
http://www.scala-lang.org/node/27499
こんな感じにしか紹介されていませんが、
def identity(x: AnyRef): x.type = x // the return type says we return exactly what we got
Dependent method typesについてちょっと試してみました。
とりあえず、こんなコードを用意。
DependentMethodType.scala
object DependentMethodType { def main(args: Array[String]): Unit = { val s = "Hello World" val xs = List(1, 2, 3) str(identity(s)) list(identity(xs)) } def identity(x: AnyRef): x.type = x def str(s: String): Unit = println(s) def list[T](xs: List[T]): Unit = println(xs) }
$ /usr/local/scala/scala-2.9.2/bin/scalac DependentMethodType.scala DependentMethodType.scala:10: error: illegal dependent method type def identity(x: AnyRef): x.type = x ^ DependentMethodType.scala:6: error: type mismatch; found : x.type (with underlying type AnyRef) required: String str(identity(s)) ^ DependentMethodType.scala:7: error: type mismatch; found : x.type (with underlying type AnyRef) required: List[?] list(identity(xs)) ^ three errors found
そもそも、
def identity(x: AnyRef): x.type = x
の部分のコンパイルが通らない。
では、Scala 2.10.0で。
$ /usr/local/scala/scala-2.10.0/bin/scalac DependentMethodType.scala $ /usr/local/scala/scala-2.10.0/bin/scala DependentMethodType.scala Hello World List(1, 2, 3)
コンパイルが通りました、動きました。
というわけで、
def identity(x: AnyRef): x.type = x
のように引数の型がAnyRefであっても、その引数に渡した型でメソッドの戻り値が返ってくるという話のようです。
でなければ、
def str(s: String): Unit = println(s) def list[T](xs: List[T]): Unit = println(xs)
これらのメソッドの呼び出しを行っている以下の部分で、コンパイルエラーになるでしょうから…。
val s = "Hello World" val xs = List(1, 2, 3) str(identity(s)) list(identity(xs))
ちなみに、こういう表記にしてみると
list(identity(List(1, 2, 3)))
コンパイルエラーになりました…。
error: type mismatch; found : x.type (with underlying type AnyRef) required: List[?] list(identity(List(1, 2, 3))) ^
1度変数に落とさないとダメ?
この他、少し調べてみると日本語でも1年以上前の日付のブログがヒットします。もしかして、だいぶ遅れてるのかなぁ…?
Stack Overflowに詳しく載っていたらしいです。
http://stackoverflow.com/questions/7860163/what-are-some-compelling-use-cases-for-dependent-method-types
出回ってた情報を、適当に変えて遊んでみました。
object Main { def main(args: Array[String]): Unit = { val i: Int = Foo.withFoo(Foo1) val s: String = Foo.withFoo(Foo2) println(i == 10) // => true println(s == "Hello World") // => true val i2: Foo1.Bar = Foo.withFoo(Foo1) val s2: Foo2.Bar = Foo.withFoo(Foo2) println(classOf[Foo1.Bar]) // => int println(classOf[Foo2.Bar]) // => class java.lang.String println(Out.intFoo(Foo1)(Foo1.v)) // => true println(Out.stringFoo(Foo2)(Foo2.v)) // => true // val i3: String = Foo.withFoo(Foo1) // <= コンパイルエラー // val s3: Int = Foo.withFoo(Foo2) // <= コンパイルエラー // println(Out.intFoo(Foo1)(Foo2.v)) // <= コンパイルエラー // println(Out.stringFoo(Foo2)(Foo1.v)) // <= コンパイルエラー } } object Out { def intFoo(f: Foo)(b: f.Bar) = b == 10 def stringFoo(f: Foo)(b: f.Bar): Boolean = b == "Hello World" } object Foo { def withFoo(f: Foo): f.Bar = f.v } trait Foo { type Bar def v: Bar } object Foo1 extends Foo { type Bar = Int def v: Bar = 10 } object Foo2 extends Foo { type Bar = String def v: Bar = "Hello World" }
なるほどねー、引数の型とそれに関連するtypeキーワードの参照先(の型)が分かってるってことなのね。
とはいえ、コンパイラが分かってる型から解決してるっぽいので、最初の例で
val any: AnyRef = "Hello World" str(identity(any))
みたいに意図的に型宣言を操作してしまうと、辻褄が合わなければコンパイルエラーですね。
found : any.type (with underlying type AnyRef) required: String str(identity(any)) ^
最初の例で、Listを生成していきなりメソッドに渡した時にコンパイルエラーになったのは、この辺りの理由と同じなのかな?
いろんな機能があるんだなぁと…。