使用ONLY隐式参数参数编写函数

时间:2015-09-14 18:55:42

标签: scala

仅使用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 _

3 个答案:

答案 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(_)