有没有办法将存在性与类型组合起来?

时间:2016-10-27 15:40:33

标签: scala typeclass existential-type

问题

我正在使用spray,我试图表达异构JSON数组的概念 - 也就是说,一切都是JsonFormat成员的数组。例如,scala值可能如下所示:

case class Dog(name: String, age: Int)

// not sure how to express this type
val json: [???] = List(3, "abc", List(), Dog("Rover", 3))

并且相应的JSON看起来像这样:

[3, "abc", {"qrs": 4}, [], {"name": "Rover", "age": 3}]

我已经标记了“存在感”和“类型类”的问题,因为我猜这是解决方案将涉及的内容(如果这是错误的话,我很乐意改变它)。我已经找到了一个解决方法,所以这篇文章的目的是为了弄清楚如何在Scala类型系统中表达它。

我尝试了什么:

  • 这会强制所有元素属于同一类型,而我正在尝试表达异构列表:

    case class MyJson1[T](items: List[T])
    
    object JsonProtocol extends DefaultJsonProtocol {
      implicit def dogFormat = jsonFormat2(Dog)
      implicit def format[T : JsonFormat] = jsonFormat1(MyJson1[T])
    }
    
  • Any不允许类型系统证明值是JsonFormat的成员:

    case class MyJson2(items: List[Any])
    
    object JsonProtocol extends DefaultJsonProtocol {
      implicit def dogFormat = jsonFormat2(Dog)
      implicit val format = jsonFormat1(MyJson2)
    }
    
  • _(存在主义)存在同样的问题:编译器无法证明这些项是JsonFormat的成员:

    case class MyJson3(items: List[_])
    
    object JsonProtocol extends DefaultJsonProtocol {
      implicit def dogFormat = jsonFormat2(Dog)
      implicit val format = jsonFormat1(MyJson3)
    }
    
  • 好吧,这只是一个疯狂的猜测而且不起作用:

    case class MyJson4[T](items: List[T])
    
    object JsonProtocol extends DefaultJsonProtocol {
      implicit def dogFormat = jsonFormat2(Dog)
      implicit def format[C <: JsonFormat[T] forSome { type T}] = jsonFormat1(MyJson4[C])
    }
    

1 个答案:

答案 0 :(得分:1)

我认为最好的方法是创建一个&#34;包装器&#34;它包含和类型类实例

class Wrapper[T](val value: T, val format: JsonFormat[T])
object Wrapper {
  def apply[T](value: T)(implicit format: JsonFormat[T]) = new Wrapper(value, format)
}

val json: List[Wrapper[_]] = List(Wrapper(3), Wrapper("abc"), Wrapper(Dog("Rover", 3))

// or use an implicit conversion to wrapper
implicit def wrapValue[T: JsonFormat](value: T) = Wrapper(value)
// note that you *must* explicitly state the list type in order for the conversion to apply
val json: List[Wrapper[_]] = List(3, "abc", Dog("Rover", 3))

在执行以下操作时可能会遇到麻烦:

for(wrapper <- json) yield {
  wrapper.format.doWhateverJsonFormatDoes(wrapper.value) // error
}

某种&#34;类型不匹配&#34;错误,但你可以通过定义一个&#34;包装器来解决这个问题。对于类型类方法,在Wrapper类中:

class Wrapper[T](val value: T, val format: JsonFormat[T]){
  def doWhateverJsonFormatDoes = format.doWhateverJsonFormatDoes(value)
}

for(wrapper <- json) yield wrapper.doWhateverJsonFormatDoes