仅使用implicit
参数
scala> def test1 (implicit i:Int )= Option(i)
test1: (implicit i: Int)Option[Int]
尝试将test1
转换为function
,如下所示,会抛出以下错误。我一定错过了一些明显的东西吗?
scala> def test2(implicit i:Int) = test1 _
<console>:8: error: type mismatch;
found : Option[Int]
required: ? => ?
def test2(implicit i:Int) = test1 _
答案 0 :(得分:0)
使用隐式参数时,范围中有一个。像这样:
object test{
implicit var i = 10
def fun(implicit i: Int) = println(i)
}
test.fun
如果你想做一些选择,你应该使用Some()
答案 1 :(得分:0)
因为在定义Int
时范围内有隐式test2
,所以它适用于test1
,因此您最终会得到test1(i) _
,这对于编译器。
如果它确实编译,test2
将返回与test1
相对应的独立于参数的相同函数。如果这是您真正想要的,您可以通过更明确地修复它:test1(_: Int)
或{ x: Int => test1(x) }
而不是test1 _
。
答案 2 :(得分:0)
这里有几种不同的可能答案取决于你想要达到的目标。如果你认为test2
是一个行为相同的方法到test1
那么就没有必要使用下划线(事实上你不能!)。
def test3(implicit i: Int) = test1(i)
如果您认为test2
与<{1}}具有行为相同的功能,那么您需要执行以下操作:
test1
请注意,我在这里使用的是val test4 = test1(_: Int)
而不是val
,因为在使用下划线时,我将原来是方法的def
转换为类的实例(特别是扩展Function2的功能,即功能)。还要注意test1
不再有隐式参数,因为它是一个函数(据我所知,函数不能有隐式参数),而test4
是一个方法。
为什么你需要这样做而不仅仅test1
变得相当复杂......
TLDR:Scala对test1 _
和foo(_)
之间的差异很有说服力,方法与函数不同,隐式解析优先于eta-expansion
通过Scala中的匿名函数将方法转换为函数对象称为&#34; eta-expansion。&#34;一般来说,eta扩展来自lambda演算并且指的是将一个术语转换为其等效的匿名函数(例如foo _
变为thing
)或者在lambda演算的语言中,在术语上添加一个抽象
请注意,如果没有eta-expansion,Scala中的编程将是非常痛苦的,因为方法不是函数。因此,它们不能直接作为参数传递给其他方法或其他函数。 Scala尝试尽可能通过eta-expansion将方法转换为函数,以便给出可以将方法传递给其他方法的外观。
那为什么不做以下工作呢?
x => thing(x)
好吧,让我们看看如果我们在没有类型签名的情况下尝试val test5 = test1 _ // error: could not find implicit value for parameter i: Int
会发生什么。
test4
啊哈,不同的错误!
这是因为val test6 = test1(_) // error: missing parameter type for expanded function ((x$1) => test1(x$1))
和test1(_)
略有不同。 test1 _
部分应用方法test1(_)
,而test1
是在test1 _
上执行eta-expansion的方向,直到完全应用。
例如,
test1
但是我们只有一个参数呢?只有一个抽象的部分应用和eta扩展之间的区别是什么?真的不是那么多。查看它的一种方法是eta扩展是实现部分应用程序的一种方式。事实上,对于单个参数方法,Scala Spec似乎对math.pow(_) // error: not enough arguments for method pow: (x: Double, y: Double)Double.
math.pow _ // this is fine
和foo(_)
之间的差异保持沉默。对我们来说唯一重要的区别是,foo _
中发生的扩展始终&#34;绑定&#34;比foo(_)
更紧密,实际上它似乎与方法应用程序一样紧密绑定。
这就是为什么foo _
给出了关于类型的错误的原因。部分应用与常规方法应用类似地进行处理。因为正常方法应用程序必须始终在隐式解析之前进行,否则我们永远不能用我们自己的值替换隐式值,部分应用程序在隐式解析之前发生。事实上,Scala实现部分应用程序的方式是通过eta扩展,因此我们扩展到匿名函数,然后抱怨缺少一个类型的参数。
我对Scala Spec第6.26节的解读表明,已经有了解决各种转换的顺序。特别是它似乎列出了在eta扩张之前出现的解决因素。实际上,对于完全应用的eta扩展,似乎必然会出现这种情况,因为Scala函数不能有隐式参数(只有它的方法可以)。
因此,对于test1(_)
,如@Alexey所说,当我们明确告诉Scala eta-expand test5
test1
时,隐式解决方案首先发生,然后eta-expansion试图发生,但是因为在隐式解决之后Scala的类型检查器意识到我们有test1 _
而不是方法。
这就是为什么我们需要Option
超过test1(_)
。最终类型注释test1 _
是必需的,因为Scala的类型推断不足以确定在eta-expansion test1(_: Int)
之后,您可以为匿名函数提供唯一可能的类型与方法的类型签名相同。事实上,如果我们给类型系统提供足够的提示,我们就可以使用test1
。
test1(_)