Home > Net >  scala typemimatch _$1 found instead of required Type
scala typemimatch _$1 found instead of required Type

Time:10-15

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?

CodePudding user response:

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).

  • Related