I'm trying to abstract case classes in a module using dependent method types and a nightly build of the compiler (2.10.0.r26005-b20111114020239). I found some inspiration from Miles Sabin' example.
I don't really understand what's wrong in the (self-contained) code below. The output depends on the order of the patterns in foo
.
// afaik, the compiler doesn't not expose the unapply method
// for a companion object
trait Isomorphic[A, B] {
def apply(x: A): B
def unapply(x: B): Option[A]
}
// abstract module
trait Module {
// 3 types with some contraints
type X
type Y <: X
type Z <: X
// and their "companion" objects
def X: Isomorphic[Int, X]
def Y: Isomorphic[X, Y]
def Z: Isomorphic[Y, Z]
}
// an implementation relying on case classes
object ConcreteModule extends Module {
sealed trait X { val i: Int = 42 }
object X extends Isomorphic[Int, X] {
def apply(_s: Int): X = new X { }
def unapply(x: X): Option[Int] = Some(x.i)
}
case class Y(x: X) extends X
// I guess the compiler could do that for me
object Y extends Isomorphic[X, Y]
case class Z(y: Y) extends X
object Z extends Isomorphic[Y, Z]
}
object Main {
def foo(t: Module)(x: t.X): Unit = {
import t._
// the output depends on the order of the first 3 lines
// I'm not sure what's happening here...
x match {
// unchecked since it is eliminated by erasure
case Y(_y) => println("y "+_y)
// unchecked since it is eliminated by erasure
case Z(_z) => println("z "+_z)
// this one is fine
case X(_x) => println("x "+_x)
case xyz => println("xyz "+xyz)
}
}
def bar(t: Module): Unit = {
import t._
val x: X = X(42)
val y: Y = Y(x)
val z: Z = Z(y)
foo(t)(x)
foo(t)(y)
foo(t)(z)
}
def main(args: Array[String]) = {
// call bar with the concrete module
bar(ConcreteModule)
}
}
Any idea?
The warnings are correct and to be expected because, as viewed from within
foo
,Y
andZ
will both have been erased to their bounds, ie.X
.What's more surprising is that the presence of either the match against
Y
or the match againstZ
frustrate the match againstX
, ie. in this case,the result is,
which seems reasonable, whereas with one of the earlier matches restored,
the result is,
which doesn't: I can't see any good reason why the additional case would cause
xyz
to be chosen overX
, so I think you've run into a bug in the pattern matcher. I suggest you search the Scala JIRA for similar issues, and if you can't find one, open a ticket with a minimized reproducing example extracted from the above.To be honest in the second example above, I would have expected the
Y
case to have been chosen in all three instances thanks toY
being erased toX
and theY
case preceeding theX
case in the match expression. But we're in unchecked territory here and I'm not 100% confident of my intuitions.