I am using scala 2.11. I have question on the scala generic types and pattern matching.
In the class MyImpl.example()
, compiler is unhappy saying type mismatch, Required: _$1, Found: X[_ <: A]
trait A
class B extends A
class C extends A
trait Foo[T <: A] {
def foo(arg: T): Unit
}
class Bar extends Foo[B] {
override def foo(arg: B): Unit = print("Bar")
}
class Car extends Foo[C] {
override def foo(arg: C): Unit = print("Car")
}
class MyImpl {
def getA(): A = {
new B()
}
def example(): Unit = {
getFoo("1").foo(getA())
}
def getFoo(something: String): Foo[_ <: A] = {
//return either
something match {
case "1" => new Bar()
case "2" => new Car()
}
}
}
object Test {
def main(args: Array[String]) = {
new MyImpl().example()
}
}
Note: getFoo("1") is more dynaminc in my real case, here is just an example.
I kind of understand this, compiler is unable to predict on which pair of the 2 implementation the method is invoked.
I am able to work around this, if I change the implementation of MyImpl.example()
to
def example(): Unit = {
(getFoo("1"), getA()) match {
case (i: Bar, j: B) => i.foo(j)
case (i: Car, j: C) => i.foo(j)
}
}
I am not really happy with this as I am just repeating i.foo(j)
Any scala functional style writing much cleaner code?
Basically you want something like
(getFoo("1"), getA()) match {
case (i: Foo[t], j: t) => i.foo(j) // doesn't compile, not found: type t
}
or
(getFoo("1"), getA()) match {
case (i: Foo[t], j: Id[t]) => i.foo(j) // doesn't compile, t is already defined as type t
}
type Id[T] = T
You can remove code duplication in
def example(): Unit = { // (*)
(getFoo("1"), getA()) match {
case (i: Bar, j: B) => i.foo(j)
case (i: Car, j: C) => i.foo(j)
}
}
(or fix compilation of getFoo("1").foo(getA())
)
with nested pattern matching
getFoo("1") match {
case i => getA() match {
case j: i._T => i.foo(j)
}
}
if you add type member _T
trait Foo[T <: A] {
type _T = T
def foo(arg: T): Unit
}
For getFoo("1")
and getA
producing new B
this prints Bar
, vice versa for getFoo("2")
and new C
this prints Car
, for other combinations this throws ClassCastException
similarly to (*).
Please notice that this can't be rewritten as just a variable declaration and single pattern matching
val i = getFoo("1")
getA() match {
case j: i._T => i.foo(j)
}
//type mismatch;
// found : j.type (with underlying type i._T)
// required: _$1
because for nested pattern matching the types are inferred correctly (i: Foo[t]
, j: t
) but for val i
the type is existential (i: Foo[_]
aka i: Foo[_$1]
after skolemization, j: _$1
).