【问题标题】:Play source file "Reads.scala" use of generalized type constraint播放源文件“Reads.scala”使用广义类型约束
【发布时间】:2013-07-04 17:46:06
【问题描述】:

在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) }

witness 是 A <: jsvalue map f: a=""> B 时,它是如何作为参数传递给 map 方法的呢?

谁能解释一下?谢谢!

【问题讨论】:

    标签: scala playframework playframework-2.0 playframework-2.1


    【解决方案1】:

    这是因为这种类型的witness也是一个函数。它在Predef 中声明为:

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

    所以A &lt;:&lt; JsValue 也是一个函数(A) =&gt; JsValue。你可能想知道这个函数做了什么:它什么都不做,它接受 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
    }
    

    但这感觉不对。如果你有办法将a 转换为Root...但是等等,你可以做到:证据本身!

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

    而且由于隐式参数可用作方法中的隐式参数,a.bip() 将自动转换为 ev(a).bip(),您无需知道其中涉及到函数。

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

    所以在你的情况下,this.map(witness) 只是让类型系统明白 Reads[A]Reads[JsValue] 通过应用什么都不做的函数,以便它可以与需要 JsValue 和返回B

    有关更多信息,请参阅关于 SO 的Generalized type constraits 问题。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-07-17
      • 2018-03-30
      • 1970-01-01
      • 2021-02-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多