構造的部分型とJavaのボックス型

Scalaの勉強してたら嵌った。挙動が良く分からない。
Scala使いには常識あるいは仕様読めwな話かもしれないけど。
実用性の話ではなくてパズル的な話です。
Scalaのバージョンは2.9.1

文字列をくっつける

引数a1とa2を取って文字列として連結したい。a1の型はなるべく広く取りたい。
その1。引数1に+(x:String):Stringというメソッドがあれば呼べる。

   def concat1(a1:{def +(x:String):String}, a2:String) = { a1 + a2 }

   println(concat1(1, " inch"));      // 1 inch
   println(concat1("1", "2"));        // 12

その2。Anyで受けてtoStringして連結

   def concat2(a1:Any, a2:String) = { a1.toString + a2 }

   println(concat2(1, " inch"));    // 1 inch
   println(concat2("1", "2"));      // 12

その3。引数1にtoString:Stringというメソッドがあれば呼べる。

   def concat3(a1:{def toString:String}, a2:String) = { a1.toString + a2 }

   println(concat3(1:java.lang.Integer, " inch")); // 1 inch
   println(concat3("1", "2"));                     // 12
//   println(concat3(1, " inch"));                 // これはコンパイルエラーになる

最後のはみなし型を指定しないとエラーになる。こういう理由らしい:

[error] Note: an implicit exists from scala.Int => java.lang.Integer, but
[error] methods inherited from Object are rendered ambiguous.  This is to avoid
[error] a blanket implicit which would convert any scala.Int to any AnyRef.
[error] You may wish to use a type ascription: `x: java.lang.Integer`.

多重継承的な問題というか。

整数を掛ける

引数a1とa2を取ってかけ算したい。a1の型はなるべく広く取りたい。
その1。引数1に*(n:Int):Intというメソッドがあれば呼べる。と思ったら呼べない。

   def mul1(a1:{def *(n:Int):Int}, a2:Int) = { a1 * a2 }

   println(mul1(2, 3));

下のようにコンパイルエラーになる

[error] ...(略).../Hoge.scala:25: type mismatch;
[error]  found   : Int(2)
[error]  required: AnyRef{def *(n: Int): Int}
[error]    println(mul1(2, 3));
[error]                  ^

???*1
その2。引数1にtoInt:Intというメソッドがあれば呼べる。

   def mul2(a1:{def toInt:Int}, a2:Int) = { a1.toInt * a2 }

   println(mul2(2, 3));

でも実行するとエラーになる

[error] (run-main) java.lang.NoSuchMethodException
java.lang.NoSuchMethodException
	at scala.runtime.BoxesRunTime.toInteger(Unknown Source)
	at Hoge$.mul2$1(Hoge.scala:28)
	at Hoge$.main(Hoge.scala:30)
	at Hoge.main(Hoge.scala)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
	at java.lang.reflect.Method.invoke(Method.java:597)
java.lang.RuntimeException: Nonzero exit code: 1
	at scala.sys.package$.error(package.scala:27)
	at scala.Predef$.error(Predef.scala:66)

これの一番下のところでエラーになっているっぽい。
自分でIntに変換してやれば問題ない。

   def mul3[A](a1:A, a2:Int)(implicit f:A=>Int) = { f(a1) * a2 }
   implicit def doubleToInt(d:Double):Int = d.toInt
   implicit def stringToInt(s:String):Int = Integer.parseInt(s)

   println(mul3(2, 3));   // 6
   println(mul3(2.2, 3)); // 6
   println(mul3("2", 3)); // 6

まあ普段こんなことすることないので困ることも無いと思うけど。

追記

上のエラー

[error] ...(略).../Hoge.scala:25: type mismatch;
[error]  found   : Int(2)
[error]  required: AnyRef{def *(n: Int): Int}
[error]    println(mul1(2, 3));
[error]                  ^

見た通り、構造的部分型はAnyRefベースなのでIntからは変換出来ないということらしい。
Anyベースで定義するか

   def mul1(a1:Any{def *(n:Int):Int}, a2:Int) = { a1 * a2 }

   println(mul1(2, 3))   // 6

implicitで変換してやれば

   def mul1(a1:{def *(n:Int):Int}, a2:Int) = { a1 * a2 }
   implicit def toAny(n:Int):AnyRef { def *(n:Int):Int } = new AnyRef {
      def *(m:Int):Int = n * m
   }

   println(mul1(2, 3))   // 6

AnyVal系でも構造的部分型が使えるっぽい。

追記2

単にいろいろな型を取って*がしたいだけなら、implicitでNumericを取って利用すれば良いみたい(Listのproductの真似)

   def mul4[A](a1:A, a2:A)(implicit nm:Numeric[A]) = {
      nm.times(a1, a2)
   }

   println(mul4(2, 3));   // 6   
   println(mul4(2.1, 3)); // 6.300000000000001

*1:追記参照