如何在Scala中表达这种类型?存在类型类(即隐式)限制吗? [英] How to express this type in Scala? Existential with type class (ie, implicit) restriction?

查看:137
本文介绍了如何在Scala中表达这种类型?存在类型类(即隐式)限制吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用Play框架的JSON库,该库使用类型类来实现

I'm using the Play framework's JSON library, which uses a type class to implement the Json.toJson function. (I may decide to use another technique with less static typing, like reflection; but for now I want to use this library because it's helping me learn the Scala type system.)

我有一堆需要传递给toJson的简单案例类,因此我必须为它们每个实现一个隐式的Writes[T]对象.对于每个类,初裁看起来可能都是这样.

I have a bunch of simple case classes that need to be passed to toJson, so I have to implement an implicit Writes[T] object for each of them. A first cut might look like this, for each of the classes.

// An example class
case class Foo(title: String, lines: List[String])

// Make 'Foo' a member of the 'Writes' typeclass
implicit object FooWrites extends Writes[Foo] {
  def writes(f: Foo) : JsValue = {
    val fields = Seq("title" -> toJson(f.title), 
                     "lines" -> toJson(f.lines))                        
    JsObject(fields)
  }
}  

每个类都将具有相似的隐式值,因此我可以对公共部分进行抽象,如下所示.但这不能编译,因为我不确定如何声明类型.

Each class will have a similar implicit value, so I could abstract the common part, as below. But this doesn't compile, because I'm not sure how to declare the type.

def makeSimpleWrites[C](fields: (String, C => T??)*) : Writes[C] = {
  new Writes[C] {
    def writes(c: C) : JsValue = {
      val jsFields = fields map { case (name, get) => (name, toJson(get(c)))}
      JsObject(jsFields)
    }
  }
}

implicit val fooWrites : Writes[Foo] = 
    makeSimpleWrites[Foo]("title" -> {_.title}, "lines" -> {_.lines})                                 
implicit val otherWrites ...

问题是我要传递给makeSimpleWrites的类型T.它不能是普通的类型参数,因为对于fields中的每个项目,T都是不同的.这是存在类型吗?我尚未使用其中之一.语法不正确...

The issue is the type T that I want to pass to makeSimpleWrites. It can't be a normal type parameter because that T is different for each item in fields. Is this an existential type? I have yet to use one of these. Flailing at syntax...

def makeSimpleWrites[C](fields: (String, C=>T forSome { type T; implicit Writes[T] })*) 

在Scala中这可能吗?如果是这样,语法是什么?

Is this possible in Scala? If so, what is the syntax?

推荐答案

由于每个字段都有不同的类型,因此每个字段需要一个类型参数.这是因为要编写这些字段,您需要(隐式地)为相应的类型(针对方法toJson)提供Writes实例,并且这些实例是静态解析的.

Because each field has a different type, you would need one type parameter per field. This is because to write these fields, you need to provide (implicitly) the Writes instances for the corresponding types (to method toJson), and those are resolved statically.

解决此问题的一种解决方案是将过程分为两部分:一种方法,您需要为每个字段调用以提取字段访问器并将其与相应的WriteS实例打包在一起(甚至可以进行隐式转换)从您已经通过的paairs)和一种方法,将整体并创建最终的WriteS实例.这样的东西(说明性的,未经测试的):

One solution to work around this is to split the process in two parts: one method that you call for each field to extract the field accessor and pack it with the corresponding WriteS instance (this can even be maed an implicit conversion from the paairs that you are already passing), and one method that takes the whole and creates the final WriteS instance. Something like this (illustrative, untested):

class WriteSFieldAccessor[C,T] private ( val title: String, val accessor: C => Any )( implicit val writes: Writes[T] )

implicit def toWriteSFieldAccessor[C,T:Writes]( titleAndAccessor: (String, C => T) ): WriteSFieldAccessor = {
  new WriteSFieldAccessor[C,T]( titleAndAccessor._1, titleAndAccessor._2 )
}
def makeSimpleWrites[C](fields: WriteSFieldAccessor[C,_]*) : Writes[C] = {
  new Writes[C] {
    def writes(c: C) : JsValue = {
      val jsFields = fields map { f: WriteSFieldAccessor => 
        val jsField = toJson[Any](f.accessor(c))(f.writes.asInstanceOf[Writes[Any]])
        (f.title, jsField)
      }
      JsObject(jsFields)
    }
  }
}

// Each pair below is implicitly converted to a WriteSFieldAccessor  instance, capturing the required information and passing it to makeSimpleWrites
implicit val fooWrites : Writes[Foo] = makeSimpleWrites[Foo]("title" -> {_.title}, "lines" -> {_.lines}) 

有趣的部分是toJson[Any](f.accessor(c))(f.writes..asInstanceOf[Writes[Any]]).您只需将Any作为静态类型传递,但会显式传递(通常是隐式的)Writes实例.

The interesting part is toJson[Any](f.accessor(c))(f.writes..asInstanceOf[Writes[Any]]). You just pass Any as a the static type but explicitly pass the (normally implicit) Writes instance.

这篇关于如何在Scala中表达这种类型?存在类型类(即隐式)限制吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆