我一直试图找到证据参数是什么的权威性定义,无济于事,因为解决了#34的情况,无法找到类型的证据参数的隐含值.... #34 ;.你能否为证据参数究竟是什么提供一个很好的解释?
答案 0 :(得分:11)
我会尝试发布自己的答案,然后随着时间的推移进行改进。让我们从一个激励场景开始,但你可以跳到下面的TLDR然后根据需要回到这里。
在一种情况下,证据参数可以被视为一种丰富一个类的方法,该类具有一些来自其原始定义之外的行为(方法/ s)。
对这篇伟大帖子进行了温和的改编如果你之前还没有直观地编写过这样的代码,这里有一个使用证据参数的代码演示。
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
依赖于该关联。 / p>
我们甚至可以在没有暗示的情况下编写相同的模式,但是最后一行(调用该方法的那一行)需要一个额外的参数 - 无论是否使用隐式的经济学 - 完全取决于你。 / p>
你可以按照自己的意愿加糖或降糖,例如这里的语法改进很小:
(此处仅修改了最后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
命名任何东西。编译器只是通过在所有这些等效情况下使用的方式知道这是一个证据参数。
更一般地或在词源上,borrowing from the other answer,证据参数是"证据"类型的特定属性,编译器需要方法的签名表明这样的要求(在另一个答案中,没有为类型Any
提供的证据为<:< Foo
,根据方法签名的要求,因此是缺少证据的情况)。
失败有一个可用作隐式的证据对象,将导致着名的could not find implicit value for evidence parameter of type ...
因为编译器知道这是证据模式的一部分,而不仅仅是一个缺失的隐含(尽管这种差异对你很重要)。
简洁地说,某个类S
的证据参数是T[S]
类型的参数(因此,是一个类的参数),它定义了一个或多个关于S
的内容。 - 因此&#34;证明&#34;关于S
的一些内容 - 使得S
符合S
原始定义之外的调用者的扩展使用条件。 T[S]
在上面借用的例子中举例说明了implicit object MakeItFoo
应具有的确切形状。
答案 1 :(得分:7)
语言规范使用§7.4 Context Bounds and View Bounds中的“证据”一词:
方法或非特征类的类型参数
A
也可能具有一个或多个上下文边界A : T
。在这种情况下,可以将类型参数实例化为S
在S
满足绑定T
的实例化点处存在证据的任何类型T[S]
。此类证据包含<%
类型的隐含值。
使用这种语法糖,你得到合成参数,规范称之为“证据参数”。 (请注意,这也涵盖了现已弃用的视图范围evidence
。
由于通常显式编写的隐式参数也被命名为A
,我认为如果它见证了类型的特定属性,则将任何隐式参数称为“证据”是有效的。
以<:< [A, B]
为例,证明B
是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
^
这会因编译错误而失败:
@annotation.implicitNotFound(msg = "Oh noes! No type class for ${A}")
trait MyTypeClass[A]
trait Bar[A] {
def baz(implicit evidence: MyTypeClass[A]): Unit
}
可以使用implicitNotFound
annotation指定确切的措辞。如果没有特定的代码示例,则不清楚是什么生成了“无法找到类型的证据参数的隐式值....”。
以下是自定义消息的示例:
trait Test {
def bar: Bar[Any]
bar.baz
}
然后:
<console>:58: error: Oh noes! No type class for Any
bar.baz
^
自定义消息失败:
npm install
答案 2 :(得分:0)
标量证据参数位于隐式参数列表的前面。
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