我一直试图找到一个权威的定义什么是证据参数,无济于事,解决一个案例"无法找到类型的证据参数的隐含值....".你能否为证据参数究竟是什么提供一个很好的解释?
我会尝试发布我自己的答案,随后改进它.让我们从一个激励场景开始,但你可以跳到下面的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
修改过,现在删除了评论)
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
.编译器只是通过在所有这些等效情况下使用的方式知道这是一个证据参数.
更一般地或在词源上,借用另一个答案,证据参数是"证明"某种类型的特定属性的参数,并且只要方法的签名表明这样的要求,编译器就需要它(在另一个答案中,有提供任何证据类型Any
之中<:< Foo
,所要求的方法的签名,因此,它是一个丢失的证据的情况).
could not find implicit value for evidence parameter of type ...
由于编译器知道这是证据模式的一部分而不仅仅是一个缺失的隐含(尽管这个差异对您很重要),因此未能将证据对象作为隐式提供.
简而言之,某个类的证据参数S
是一个类型的参数T[S]
(因此,一个类的参数),它定义了一个或多个事物S
- 因此"证明"某些东西S
- 这使得有S
资格被调用者扩展使用,超出原来的定义S
.这个T[S]
应该具有的确切形状,在我上面借用的例子中举例说明implicit object MakeItFoo
.
语言规范在§7.4上下文边界和视图边界中使用术语"证据" :
A
方法或非特征类的类型参数也可以具有一个或多个上下文边界A : T
.在这种情况下,类型参数可被实例化到任何类型的S
用于该证据存在于该实例化点S
满足约束T
.此类证据包含类型的隐含值T[S]
.
使用这种语法糖,你会得到合成参数,规范称之为"证据参数".(请注意,这也包括<%
现在已弃用的视图范围).
由于经常显式编写的隐式参数也被命名evidence
,我认为如果它见证了类型的特定属性,则将任何隐式参数称为"证据"是有效的.例如<:< [A, B]
,证据A
是以下的子类型B
:
trait Foo trait Bar[A] { def baz(implicit evidence: A <:< Foo): Unit }
那么如果你试试这个:
trait Test { def bar: Bar[Any] bar.baz }
这会因编译错误而失败:
:58: error: Cannot prove that Any <:< Foo.
bar.baz
^
可以使用implicitNotFound
注释指定确切的措辞.如果没有特定的代码示例,则不清楚是什么生成了"无法找到类型的证据参数的隐式值....".
以下是自定义消息的示例:
@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 }
自定义消息失败:
:58: error: Oh noes! No type class for Any
bar.baz
^