简体   繁体   English

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

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

I'm trying to spin my head around the path-dependent types in Scala's enums while making a Reads/Writes for Play2. 我试图在为Play2进行读/写操作时围绕Scala枚举中与路径相关的类型进行操作。 Here is the code I have so far, it works, but with an asInstanceOf: 这是我到目前为止的代码,它可以工作,但是带有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]]: _*))

What can I do to get rid of the asInstanceOf on the last line? 我该怎么做才能摆脱最后一行中的asInstanceOf? I tried typing the enumerationReads as enumerationReads[t.Value], but that doesn't work, the compiler complains in the argument of t.ValueSet that Seq[t.Value] cannot be cast to Seq[t.Value]. 我尝试将enumerationReads键入为enumerationReads [t.Value],但这没有用,编译器在t.ValueSet的参数中抱怨Seq [t.Value]无法转换为Seq [t.Value]。 Yes, that didn't make sense to me too, until I started to realize these different t's might actually be different, since they are used in a closure. 是的,这对我来说也没有意义,直到我开始意识到这些不同的t实际上可能有所不同,因为它们是在闭包中使用的。

So, what to do to make my code super-duper asInstanceOf free? 那么,该怎么做才能使我的代码超级重复asInstanceOf自由呢?

The following code might do the trick: 以下代码可以解决问题:

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 : _*))

or maybe more useful 也许更有用

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

An enumeration is already an object (of class Enumeration ) so you can use it directly. 枚举已经是( Enumeration类的)对象,因此您可以直接使用它。 You don't need to introduce a type T . 您不需要引入类型T To type your functions correctly the compiler has to check that types Value and ValueSet both belong to the correct instance of Enumeration (here t ). 为了正确键入函数,编译器必须检查类型ValueValueSet都属于Enumeration的正确实例(此处为t )。 That's called path-dependent types . 这就是依赖于路径的类型

In the following code 在下面的代码中

class T extends Enumeration

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

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

Both Animals and Food are instances of T but the types T # Value , Animals.Value and Food.Value are different. AnimalsFood都是T实例,但是类型T # ValueAnimals.ValueFood.Value不同。 T # Value represent any type Value of any instance of T whereas Animals.Value (respectively Food.Value ) is the type that belongs to Animals and Animals alone. T # Value表示任何类型的Value的任何实例的TAnimals.Value (分别Food.Value )是属于所述类型AnimalsAnimals独自。

In the error "Seq[t.Value] cannot be cast to Seq[t.Value]", which i admit is very confusing, the two occurences of t are different instances. 我承认错误“无法将Seq [t.Value]强制转换为Seq [t.Value]”,这是非常令人困惑的, t的两次出现是不同的实例。 If you call enumerationReads[T] with type T , it considers its argument t to be any, unknown, instance of T . 如果你打电话enumerationReads[T]类型T ,它认为它的参数t是任意的,未知的情况下T So you lose the track of the instance. 因此,您会失去实例的踪迹。 That's why you have to call it with type t.type . 这就是为什么必须使用类型t.type来调用它的原因。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM