Scala Dynami c тип возврата в зависимости от типа ввода - PullRequest
2 голосов
/ 16 января 2020

Хорошо, так что я не знаю, что глючит в этом коде:

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

Я хочу (пример был упрощен), чтобы иметь возможность возвращать Map[SomeKey, Int], если someFunc вызывается из класс case, расширяющий MyTrait[SomeKey]. И вернуть Map[SomeOtherKey, Int] из MyTrait[SomeOtherKey]

case class MyClass(val s: Int) extends MyTrait[SomeKey] {
  override def someField = s
}

Здесь новый экземпляр MyClass должен возвращать Map[SomeKey, Int] при вызове someFunc.

Но это не так даже компиляция, компилятор жалуется на каждую строку совпадения шаблона:

type mismatch;
 found   : (Playground.this.SomeKey, Int)
 required: (T, Int)

или

type mismatch;
 found   : (Playground.this.SomeOtherKey, Int)
 required: (T, Int)

Ответы [ 2 ]

4 голосов
/ 16 января 2020

Вот решение с использованием классов типов и последствий.

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
}
3 голосов
/ 16 января 2020

TypeTag перенесет информацию о типе в время выполнения , однако тип возврата метода - конструкция времени компиляции , следовательно, ошибка компилятора. Вместо этого рассмотрим решение класса типов с помощью метода расширения (опять же угнанного по предложению Луиса)

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

, который выдает

res0: Map[SomeOtherKey,Int] = Map(SomeOtherKey(1,2,true) -> 2)
res1: Map[SomeKey,Int] = Map(SomeKey(1,2) -> 1)
...