Reputation: 487
Ok so I don't know what's bugging in this code:
import scala.reflect.runtime.universe._
trait Key extends Product
case class SomeKey(a: Int, b: String) extends Key
case class SomeOtherKey(a: Int, b: String, c:Boolean) extends Key
trait MyTrait[T <: Key] {
def someField: Int
def someFunc(implicit tTypeTag: TypeTag[T]): Map[T, Int] = {
typeOf(tTypeTag) match {
case t if t =:= typeOf[SomeKey] => Map(SomeKey(1,"2") -> 1)
case t if t =:= typeOf[SomeOtherKey] => Map(SomeOtherKey(1,"2",true) -> 2)
}
}
}
I want (the example has been oversimplified) to be able to return a Map[SomeKey, Int]
if someFunc
is called from a case class extending MyTrait[SomeKey]
. And return a Map[SomeOtherKey, Int]
from a MyTrait[SomeOtherKey]
case class MyClass(val s: Int) extends MyTrait[SomeKey] {
override def someField = s
}
Here a new instance of MyClass
should return a Map[SomeKey, Int]
when calling someFunc
.
But it does not even compile, compiler complaining for each line of the pattern match:
type mismatch;
found : (Playground.this.SomeKey, Int)
required: (T, Int)
or
type mismatch;
found : (Playground.this.SomeOtherKey, Int)
required: (T, Int)
Upvotes: 2
Views: 581
Reputation: 48430
TypeTag
will carry over type information to runtime however return-type of a method is compile-time construct, hence the compiler error. Instead consider typeclass solution via extension method (yet again hijacked from Luis' suggestion)
sealed trait Key
final case class SomeKey(a: Int, b: String) extends Key
final case class SomeOtherKey(a: Int, b: String, c: Boolean) extends Key
trait MyTrait[T <: Key]
trait KeyFactory[T <: Key] {
def someFunc(): Map[T, Int]
}
object KeyFactory {
def someFunc[T <: Key](implicit ev: KeyFactory[T]) = ev.someFunc
implicit val someKeyFoo: KeyFactory[SomeKey] = () => Map(SomeKey(1,"2") -> 1)
implicit val someOtherKey: KeyFactory[SomeOtherKey] = () => Map(SomeOtherKey(1,"2", true) -> 2)
}
implicit final class MyTraitKeyFactory[T <: Key : KeyFactory](private val v: MyTrait[T]) {
def someFunc(): Map[T, Int] = implicitly[KeyFactory[T]].someFunc()
}
case class MyClass(s: Int) extends MyTrait[SomeKey]
case class MyOtherClass(s: Int) extends MyTrait[SomeOtherKey]
MyOtherClass(42).someFunc()
MyClass(11).someFunc()
which outputs
res0: Map[SomeOtherKey,Int] = Map(SomeOtherKey(1,2,true) -> 2)
res1: Map[SomeKey,Int] = Map(SomeKey(1,2) -> 1)
Upvotes: 3
Reputation: 10111
Here's a solution using type classes and implicits.
trait Key extends Product
case class SomeKey(a: Int, b: String) extends Key
case class SomeOtherKey(a: Int, b: String, c:Boolean) extends Key
trait TypeClass[T] {
def someFunc: Map[T, Int]
}
object TypeClass {
implicit def forSomeKey: TypeClass[SomeKey] = new TypeClass[SomeKey] {
override def someFunc: Map[SomeKey, Int] = Map(SomeKey(1, "2") -> 1)
}
implicit def forSomeOtherKey: TypeClass[SomeOtherKey] = new TypeClass[SomeOtherKey] {
override def someFunc: Map[SomeOtherKey, Int] = Map(SomeOtherKey(1, "2", true) -> 1)
}
}
trait MyTrait[T <: Key] {
def someField: Int
def someFunc(implicit tc: TypeClass[T]): Map[T, Int] = tc.someFunc
}
Upvotes: 4