2

I have a class

case class MyClass[T](values: List[T])

and I'm trying to create a function which will return a value based on the type of T

def myFunc: T = values match {
  case v: List[Boolean] => false
  case v: List[MyType] => MyType.defaultVal
  case _ => throw new Exception("unsupported type")
}

However, I get compilation errors:

Expression of type Boolean doesn't conform to expected type T

Expression of type MyType.defaultVal.type doesn't conform to expected type T

I guess I can solve this by creating an abstract class ans subclasses, but I'd rather not do it this way. Is there any other way to solve this?

Community
  • 1
  • 1
Blibli
  • 63
  • 4

2 Answers2

0

I think you can solve it with scala's type tag but I'm not an expert in that field, always found this too complicated.

A simpler solution I like to use is to pattern match on the head's element:

def myFunc: T = values.headOption.map {
  case v: Boolean => false
  case v: MyType => MyType.defaultVal
  case _ => throw new Exception("unsupported type")
}.getOrElse(throw new Exception("unsupported type"))
LiorH
  • 18,524
  • 17
  • 70
  • 98
0

The thing is that pattern matching is performed mostly at runtime and types are resolved at compile time. Because of type erasure case v: List[Boolean] => ??? is not better than case v: List[_] => ???. In the line case v: List[Boolean] => ... compiler doesn't know that T =:= Boolean. Pattern matching can't return different types from different cases.

So you can either pattern match with type tags and casting

case class MyClass[T](values: List[T]) {   
  import reflect.runtime.universe.{TypeTag, typeOf}
  def myFunc(implicit typeTag: TypeTag[T]): T = values match {
    case v: List[Boolean] if typeOf[T] <:< typeOf[Boolean] => false.asInstanceOf[T]
    case v: List[MyType]  if typeOf[T] <:< typeOf[MyType]  => MyType.defaultVal.asInstanceOf[T]
    case _ => throw new Exception("unsupported type")
  } 
}

or do this more type-safely with a type class (or polymorphic function)

case class MyClass[T](values: List[T]) {
  def myFunc(implicit myFuncInstance: MyFunc[T]): T = myFuncInstance(values)
}

trait MyFunc[T] {
  def apply(values: List[T]): T
}
object MyFunc {
  implicit val booleanMyFunc: MyFunc[Boolean] = new MyFunc[Boolean] {
    override def apply(values: List[Boolean]): Boolean = false
  }

  implicit val myTypeMyFunc: MyFunc[MyType] = new MyFunc[MyType] {
    override def apply(values: List[MyType]): MyType = MyType.defaultVal
  }
}
Dmytro Mitin
  • 48,194
  • 3
  • 28
  • 66