2

在 Play 框架源文件中,trait Reads[A] 内部有以下方法:

def andThen[B](rb: Reads[B])(implicit witness: A <:< JsValue): Reads[B] =
    rb.compose(this.map(witness))

和这样的地图方法:

def map[B](f: A => B): Reads[B] =
    Reads[B] { json => self.reads(json).map(f) }

见证的类型是 A <:< JsValue (这是一个广义的类型约束)。那么,当 map 方法的参数采用函数 f: A => B 时,它是如何作为参数传递给 map 方法的?

有人可以解释一下吗?谢谢!

4

1 回答 1

4

这是因为这种类型的见证也是一个函数。它被声明Predef为:

sealed abstract class <:<[-From, +To] extends (From => To) with Serializable

所以A <:< JsValue也是一个函数(A) => JsValue。你可能想知道这个函数做了什么:它什么都不做,它接受 aA并直接返回它(作为 a JsValue)。

要了解为什么这很有用,请考虑以下示例:

sealed trait Root { def bip() { println("bip") } }

def makeBip[A <: Root](a: A) {
  a.bip() // works because a is known to the type system to be a Root
}

def makeBip2[A](a: A)(implicit ev: A <:< Root) {
  a.bip() // works, because implicit resolution turns it into `ev(a).bip()`
}

如果没有隐式,最后一种方法makeBip2将无法编译,因为即使知道这aRoot归功于证据,但类型系统却没有。你可以投射它,它肯定会起作用:

def makeBip3[A](a: A)(implicit ev: A <:< Root) {
  a.asInstanceOf[Root].bip() // ugly
}

但这感觉不对。如果您有办法转换aRoot……但是等等,您确实做到了:证据本身!

def makeBip4[A](a: A)(implicit ev: A <:< Root) {
  ev(a).bip() // works!
}

并且由于隐式参数可用作方法中的隐式参数,a.bip()因此将自动转换为ev(a).bip()并且您永远不需要知道涉及的函数。

但是,类型系统仅使用隐式将 an 解析A为 a JsValue,而不是 a Seq[A]into aSeq[JsValue]或 a Reads[A]into a Reads[JsValue]

因此,在您的情况下,this.map(witness)只需通过应用什么都不做的函数来让类型系统了解 aReads[A]是 a Reads[JsValue],这样它就可以与接受 aJsValue并返回 a 的东西组合在一起B

有关更多信息,请参阅 SO 上的广义类型约束问题。

于 2013-07-07T13:05:44.097 回答