无法让依赖于路径的类型在 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
。为了正确输入你的函数,编译器必须检查类型 Value
和 ValueSet
是否都属于 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
}
Animals
和Food
都是T
的实例,但是T # Value
、Animals.Value
和Food.Value
的类型不同。 T # Value
表示 T
的任何实例的任何类型 Value
,而 Animals.Value
(分别为 Food.Value
)是属于 Animals
和 Animals
一个人.
在错误 "Seq[t.Value] cannot be cast to Seq[t.Value]" 中,我承认这非常令人困惑,t
的两次出现是不同的实例。如果您使用类型 T
调用 enumerationReads[T]
,它会将其参数 t
视为 T
的任何未知实例。所以你失去了实例的踪迹。这就是为什么你必须用 t.type
.
类型调用它的原因
在为 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
。为了正确输入你的函数,编译器必须检查类型 Value
和 ValueSet
是否都属于 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
}
Animals
和Food
都是T
的实例,但是T # Value
、Animals.Value
和Food.Value
的类型不同。 T # Value
表示 T
的任何实例的任何类型 Value
,而 Animals.Value
(分别为 Food.Value
)是属于 Animals
和 Animals
一个人.
在错误 "Seq[t.Value] cannot be cast to Seq[t.Value]" 中,我承认这非常令人困惑,t
的两次出现是不同的实例。如果您使用类型 T
调用 enumerationReads[T]
,它会将其参数 t
视为 T
的任何未知实例。所以你失去了实例的踪迹。这就是为什么你必须用 t.type
.