Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/scala/17.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何在Scala中表示此类型?存在类型类(即隐式)限制?_Scala_Existential Type_Implicits - Fatal编程技术网

如何在Scala中表示此类型?存在类型类(即隐式)限制?

如何在Scala中表示此类型?存在类型类(即隐式)限制?,scala,existential-type,implicits,Scala,Existential Type,Implicits,我正在使用Play框架的JSON库,它使用一个类型类来实现。(我可能决定使用另一种静态类型较少的技术,比如反射;但现在我想使用这个库,因为它帮助我学习Scala类型系统。) 我有一大堆简单的case类需要传递给toJson,因此我必须为每个类实现一个隐式Writes[T]对象。对于每个类,第一次剪切可能是这样的 // An example class case class Foo(title: String, lines: List[String]) // Make 'Foo' a membe

我正在使用Play框架的JSON库,它使用一个类型类来实现。(我可能决定使用另一种静态类型较少的技术,比如反射;但现在我想使用这个库,因为它帮助我学习Scala类型系统。)

我有一大堆简单的case类需要传递给
toJson
,因此我必须为每个类实现一个隐式
Writes[T]
对象。对于每个类,第一次剪切可能是这样的

// 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)
  }
}  
每个类都有一个类似的隐式值,因此我可以抽象出公共部分,如下所示。但这不会编译,因为我不确定如何声明类型

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
。它不能是普通类型参数,因为
字段中的每个项目的t都不同。这是存在主义类型吗?我还没有使用其中一个。在语法上挥舞

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

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

因为每个字段都有不同的类型,所以每个字段需要一个类型参数。这是因为要编写这些字段,您需要(隐式地)为相应类型(to method
toJson
)提供
Writes
实例,并且这些实例是静态解析的

解决此问题的一个解决方案是将流程分为两部分:一个方法,您为每个字段调用该方法,以提取字段访问器,并将其与相应的
WriteS
实例打包(这甚至可以被认为是您已经传递的paairs的隐式转换),还有一种方法,它获取整体并创建最终的
写入
实例。类似这样的内容(说明性的,未经测试):


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

在尝试解决第一个解决方案时,必须写入
“title”->{s:Section=>s.title}
,而不是
“title”->{uu.title}
,我对它做了一点修改,结果总是在scala的推理限制下运行。 所以我决定尝试从另一个角度来解决这个问题,并提出了一个完全不同的解决方案。 这基本上是一种准DSL:

class ExpandableWrites[C]( val fields: Vector[(String, C => Any, Writes[_])] ) extends Writes[C] {
  def and[T:Writes](fieldAccessor: C => T)(fieldName: String): ExpandableWrites[C] = {
    new ExpandableWrites( fields :+ (fieldName, fieldAccessor, implicitly[Writes[T]]) )
  }
  def writes(c: C) : JsValue = {
    val jsFields = fields map { case (name, get, writes) => (name, toJson[Any](get(c))(writes.asInstanceOf[Writes[Any]]) )}
    JsObject(jsFields)
  }
}

class UnaryExpandableWritesFactory[C] {
  def using[T:Writes](fieldAccessor: C => T)(fieldName: String): ExpandableWrites[C] = {
    new ExpandableWrites[C]( Vector( (fieldName, fieldAccessor, implicitly[Writes[T]] ) ) )
  }
}

def makeSimpleWritesFor[C] = new UnaryExpandableWritesFactory[C]

implicit val fooWrites : Writes[Foo] = 
  makeSimpleWritesFor[Foo].using(_.title)("title") .and (_.lines)("lines") .and (_.date)("date")
其思想是逐步创建
Writes
实例,并用新字段逐一充实它。
唯一的麻烦是你确实需要
分隔符,包括点。如果没有点(即使用中缀符号),编译器似乎会再次感到困惑,并抱怨如果我们只是执行
(u.title)
,而不是
(s:Section=>s.title)

,至少在2015年1月25日,play json已经有了一种内置的方式来完成您想要的任务:

import play.api.libs.json._
import play.api.libs.functional.syntax._

sealed case class Foo(title: String, lines: List[String])  // the `sealed` bit is not relevant but I always seal my ADTs

implicit val fooWrites = (
  (__ \ "title").write[String] ~
  (__ \ "lines").write[List[String]]
)(unlift(Foo.unapply))
事实上,这也适用于
读取[T]

implicit val fooReads = (
  (__ \ "title").read[String] ~
  (-- \ "lines").read[List[String]]
)(Foo.apply _)
格式[T]

implicit val fooFormat = (
  (__ \ "title").format[String] ~
  (-- \ "lines").format[List[String]]
)(Foo.apply _, unlift(Foo.unapply))
您还可以应用变换,例如:

implicit val fooReads = (
  (__ \ "title").read[String].map(_.toLowerCase) ~
  (-- \ "lines").read[List[String]].map(_.filter(_.nonEmpty))
)(Foo.apply _)
甚至双向变换:

implicit val fooFormat = (
  (__ \ "title").format[String].inmap(_.toLowerCase, _.toUpperCase) ~
  (-- \ "lines").format[List[String]]
)(Foo.apply _, unlift(Foo.unapply))

鉴于OP明确提到了类型类,我想他至少表面上知道这是什么,对吧?或者你可以指出类型类的一个具体细节,在这个例子中可能对他有所帮助?对,我想我理解类型类的模式。真的很抱歉,我读你的问题太快了。。。对类型T的唯一约束是它必须具有类型为Writes[T]的隐式?您可以使用
defmakesimplewrites[C,T:Writes](字段:(String,C=>T)*)
非常好的答案!我忘了在这里,每个字段需要一个类型参数……啊哈,我想我明白为什么我需要像你一样打包隐式写入。虽然现在我在编译这个时遇到了一些麻烦。我添加了(f.writes.asInstanceOf[Any]),但现在它不喜欢最后一行的匿名函数<代码>缺少扩展函数的参数类型((x$1)=>x$1.title)
。稍后我将报告更多内容。这将是
f.writes.asInstanceOf[writes[Any]]
。我将更新我的答案。似乎我必须编写
“title”->{s:Section=>s.title}
,而不是
“title”->{{uu.title}
来编译它。没什么大不了的,但你还有什么办法来解决这个问题吗?看看我的另一个答案。
implicit val fooFormat = (
  (__ \ "title").format[String].inmap(_.toLowerCase, _.toUpperCase) ~
  (-- \ "lines").format[List[String]]
)(Foo.apply _, unlift(Foo.unapply))