无法让依赖于路径的类型在 Scala 枚举中工作

Can't get path-dependent types to work in scala enumerations

在为 Play2 制作 Reads/Writes 时,我正在努力思考 Scala 枚举中的路径相关类型。这是我到目前为止的代码,它可以工作,但是有一个 asInstanceOf:

implicit def enumerationReads[T <: Enumeration](implicit t: T): Reads[t.Value] = {
    val validationError = ValidationError("error.expected.enum.name", t.values.mkString(", "))
    Reads.of[String].filter(validationError)(s ⇒ t.values.exists(v ⇒ v.toString == s)).map(t.withName(_))
  }

implicit def enumerationValueSetReads[T <: Enumeration](implicit t: T): Reads[t.ValueSet] =
    Reads.seq(enumerationReads[T]).map(seq ⇒ t.ValueSet(seq.asInstanceOf[Seq[t.Value]]: _*))

我该怎么做才能去掉最后一行的 asInstanceOf?我尝试将 enumerationReads 键入为 enumerationReads[t.Value],但这不起作用,编译器在 t.ValueSet 的参数中抱怨 Seq[t.Value] 无法转换为 Seq[ t.Value]。是的,这对我来说也没有意义,直到我开始意识到这些不同的 t 实际上可能是不同的,因为它们在闭包中使用。

那么,如何使我的代码超级 asInstanceOf 免费?

以下代码可能会起作用:

implicit def enumerationReads(t : Enumeration) : Reads[t.Value] = {
    val validationError = ValidationError("error.expected.enum.name", t.values.mkString(", "))
    Reads.of[String].filter(validationError)(s ⇒ t.values.exists(v ⇒ v.toString == s)).map(t.withName(_))
}

implicit def enumerationValueSetReads(t: Enumeration): Reads[t.ValueSet] =
    Reads.seq(enumerationReads(t : t.type)).map(set => t.ValueSet(set : _*))

或者更有用

implicit def enumerationValueSetReads(t: Enumeration): Reads[Set[t.Value]] =
    Reads.set(enumerationReads(t : t.type))

一个枚举已经是一个对象(class Enumeration)所以你可以直接使用它。您不需要引入类型 T。为了正确输入你的函数,编译器必须检查类型 ValueValueSet 是否都属于 Enumeration 的正确实例(这里是 t)。那叫path-dependent types.

在下面的代码中

class T extends Enumeration

object Animals extends T {
    val Cow, Pig, Rabbit = Value
}

object Food extends T {
    val Rice, Potatoes, Fries = Value
}

AnimalsFood都是T的实例,但是T # ValueAnimals.ValueFood.Value的类型不同。 T # Value 表示 T 的任何实例的任何类型 Value,而 Animals.Value(分别为 Food.Value)是属于 AnimalsAnimals一个人.

在错误 "Seq[t.Value] cannot be cast to Seq[t.Value]" 中,我承认这非常令人困惑,t 的两次出现是不同的实例。如果您使用类型 T 调用 enumerationReads[T],它会将其参数 t 视为 T 的任何未知实例。所以你失去了实例的踪迹。这就是为什么你必须用 t.type.

类型调用它的原因