【问题标题】:What precisely is a scala evidence parameter究竟什么是 scala 证据参数
【发布时间】:2020-09-09 21:12:21
【问题描述】:

我一直试图找到一个关于什么是证据参数的权威定义,但无济于事,以解决“找不到类型的证据参数的隐含值......”的情况。您能否很好地解释一下证据参数到底是什么?

【问题讨论】:

  • 这是一篇不错的短文:cakesolutions.net/teamblogs/…
  • 伟大的@corn_dog。那个帖子真的很到位。只是想……我们能不能为了同样的目的使用结构类型。
  • 不,根本不是因为Foos 整数的方式可能与Foos 的方式java.sql.Timestamp 不同,因此您不能依赖特定的方法形状。
  • 顺便说一句——original comment 引用了 this article,尽管几乎所有关于该主题的资源都指向它,但它已被删除!

标签: scala


【解决方案1】:

我会尝试发布我自己的答案,稍后会改进它。让我们从一个激励场景开始,但您可以跳到下面的 TLDR,然后根据需要返回这里。

在一种情况下,证据参数可以被视为一种通过原始定义之外的某些行为(方法/s)来丰富类的方法。

Cake Solutions 的精彩帖子进行了温和的重新讨论:

如果您之前没有直观地编写过这样的代码,这里有一个正在使用的证据参数的代码演示。

object EvidenceExample {

  // class with no methods
  case class Bar(value: String)

  // a trait the class Bar had not implemented
  trait WithFoo[A] {
    def foo(x: A): String
  }

  // object that attaches an implementation of the trait for Bar - for methods
  // willing to play along with this kind of trait attachment - see immediately below
  implicit object MakeItFoo extends WithFoo[Bar] {
    def foo(x: Bar) = x.value
  }

  // method willing to recognize anything as having trait WithFoo, 
  // as long as it has evidence that it does - the evidence being the previous object
  def callFoo[A](thing: A)(implicit evidence: WithFoo[A]) = evidence.foo(thing)
  
  callFoo(Bar("hi")) // and it works
}

您可能会从下往上阅读该代码,发现类 Bar 已在其原始定义之外进行了丰富。然而——只有与证据仪式一起发挥作用的功能才能将其视为丰富。

这种模式几乎没有什么神奇之处——尽管这是一种独特的语言特性——包装器对象将特征与Bar 相关联,callFoo 依赖于该关联。

我们甚至可以在没有隐式的情况下编写相同的模式,但是最后一行,即调用该方法的那一行,将需要一个额外的参数——是否使用隐式的经济学——完全取决于你。

您可以根据需要对其进行加糖或减糖,例如,这里有一个小的语法改进:

(这里只修改了最后一个def,现在移除了cmets)

object EquivalentEvidenceExample {

  case class Bar(value: String)

  // a trait the class Bar had not implemented
  trait WithFoo[A] {
    def foo(x: A): String
  }
  
  implicit object MakeItFoo extends WithFoo[Bar] {
    def foo(x: Bar) = x.value
  }
  
  def callFoo[A:WithFoo](thing: A) = implicitly[WithFoo[A]].foo(thing) // lightly sugared syntax, frankly only more confusing
  
  callFoo(Bar("hi"))
}

您无需使用字符串evidence 命名任何内容。编译器只是通过在所有这些等效情况下使用它的方式知道这是一个证据参数。

更一般地或词源上,borrowing from the other answer,证据参数是“证明”一种类型的特定属性的参数,编译器需要它,只要方法的签名表明这样的要求(在另一个答案中,有没有为类型Any 提供<:< Foo 的证据,这是方法签名所要求的,因此这是缺少证据的情况。

Failure 将证据对象作为隐式提供,将导致著名的could not find implicit value for evidence parameter of type ...,因为编译器知道这是证据模式的一部分,而不仅仅是缺少的隐式(同样因为这种差异对您很重要)。

TLDR:

简而言之,某个类S 的证据参数是T[S] 类型的参数(因此,是一个类的参数),它定义了关于S 的一个或多个事物——因此“证明”某些事物关于S —— 这使得S 有资格被调用者扩展使用,超出S 的原始定义。 T[S] 应该具有的确切形状在我上面借用的示例中得到了例证,implicit object MakeItFoo

【讨论】:

    【解决方案2】:

    语言规范在§7.4 Context Bounds and View Bounds中使用术语“证据”:

    方法或非特征类的类型参数A 也可以有一个或多个上下文边界A : T。在这种情况下,类型参数可以被实例化为任何类型S,其证据存在于S 满足绑定T 的实例化点。此类证据由类型为 T[S] 的隐式值组成。

    使用这种语法糖,您可以获得规范称为“证据参数”的合成参数。 (请注意,这也涵盖了视图边界<%,现在已弃用)。

    由于通常显式编写的隐式参数也被命名为evidence,我认为如果任何隐式参数见证了一个类型的特定属性,则将其称为“证据”是有效的。 以<:< [A, B] 为例,证明AB 的子类型:

    trait Foo
    
    trait Bar[A] {
      def baz(implicit evidence: A <:< Foo): Unit
    }
    

    如果你试试这个:

    trait Test {
      def bar: Bar[Any]
    
      bar.baz
    }
    

    这会因编译错误而失败:

    <console>:58: error: Cannot prove that Any <:< Foo.
             bar.baz
                 ^
    

    可以使用implicitNotFound annotation 指定确切的措辞。如果没有具体的代码示例,不清楚是什么生成了“找不到类型的证据参数的隐式值......”。

    这是一个自定义消息的示例:

    @annotation.implicitNotFound(msg = "Oh noes! No type class for ${A}")
    trait MyTypeClass[A]
    
    trait Bar[A] {
      def baz(implicit evidence: MyTypeClass[A]): Unit
    }
    

    然后:

    trait Test {
      def bar: Bar[Any]
    
      bar.baz
    }
    

    自定义消息失败:

    <console>:58: error: Oh noes! No type class for Any
             bar.baz
                 ^
    

    【讨论】:

    • 它真的只是一种约定,并没有融入语言吗?使用该术语而不是镜像参数名称的编译器错误,并在language spec 中提到它,我可以进一步说服..
    • 谢谢@matt - 我不知道规范中使用了这个词。我现在已经详细说明了。
    • 诚然,我有时会觉得这与常规隐式转换和丰富模式有何不同有点令人困惑。一篇好的博客文章可能会以一种更有凝聚力的方式将它们全部联系在一起(我想说,为这些语言功能重用现有语言标记如此经济可能是一个争论的话题,至少与下划线哲学一致)。
    • 一些不同之处在于,即使最终使用了某种转换,您也可以将其用作远离实际转换应用的约束。此外,您实际上不需要进行一些实际转换。参见例如dcsobral.blogspot.com/2010/06/…
    【解决方案3】:

    Scala 证据参数被附加到隐式参数列表中。

    def foo[A: M](implicit b: B): C
    // expands to:
    // def foo[A](implicit evidence$1: M[A], b: B): C
    

    有关详细信息,请参阅规格。 https://www.scala-lang.org/files/archive/spec/2.11/07-implicits.html

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-04-25
      • 1970-01-01
      • 2014-06-18
      • 1970-01-01
      • 2021-04-26
      • 1970-01-01
      • 2014-10-28
      • 2012-08-27
      相关资源
      最近更新 更多