在Scala中使用函数参数行为进行奇怪的隐式def

时间:2015-10-26 21:29:27

标签: scala implicit-conversion

我在Scala中编写了一个简单的代码,将Function1隐式转换为某个case类。

object MyApp extends App{
  case class FunctionContainer(val function:AnyRef)

  implicit def cast(function1: Int => String):FunctionContainer = new FunctionContainer(function1)

  def someFunction(i:Int):String = "someString"
  def abc(f : FunctionContainer):String = "abc"

  println(abc(someFunction))
} 

但它不起作用。编译器并不想将someFunction作为参数传递给abc。我可以猜出它的原因,但不知道它为什么不起作用。

3 个答案:

答案 0 :(得分:3)

您的someFunction在此处显示为方法。 你可以试试

object MyApp extends App{
  case class FunctionContainer(val function:AnyRef)

  implicit def cast(function1: Int => String):FunctionContainer = new FunctionContainer(function1)

  val someFunction = (i:Int) => "someString"
  def abc(f : FunctionContainer):String = "abc"

  println(abc(someFunction))
}

object MyApp extends App{
  case class FunctionContainer(val function:AnyRef)

  implicit def cast(function1: Int => String):FunctionContainer = new FunctionContainer(function1)

  def someFunction(i:Int): String = "someString"
  def abc(f : FunctionContainer):String = "abc"

  println(abc(someFunction(_: Int)))
}

顺便说一下:隐含地将这些常用函数强制转换为其他东西会很快导致问题。你绝对确定你需要这个吗?重载abc会不会更容易?

答案 1 :(得分:3)

当您使用方法名称时,编译器必须选择如何将方法类型转换为值。如果期望的类型是一个函数,那么它会扩展;否则它提供空的parens来调用该方法。这被描述为here in the spec

但并非总是如此。十年前,只需使用方法名称即可得到函数值。

新的在线规范省略了“更改日志”附录,因此,对于记录,这是有人对parens感到沮丧并介绍当前规则的时刻。 (参见Scala参考2.9,第181页。)

这并没有消除所有irksome anomalies

转化

用于隐式转换方法到函数(第6.26节)的规则已经收紧。以前,用作值的参数化方法始终隐式转换为函数。当忘记方法参数时,这可能会导致意外结果。例如,考虑下面的陈述:

show(x.toString)

其中show定义如下:

def show(x: String) = Console.println(x)

最有可能的是,程序员忘了向toString提供一个空参数列表()。以前的Scala版本会将此代码视为部分应用的方法,并将其扩展为:

show(() => x.toString())

结果,将打印闭包的地址而不是s的值。仅当表达式的预期类型确实是函数类型时,Scala版本2.0才会将部分应用的方法转换为函数值。例如,转换不会应用于上面的代码中,因为show的参数的预期类型是String,而不是函数类型。新公约不允许一些以前合法的代码。例如:

def sum(f: int => double)(a: int, b: int): double =
  if (a > b) 0 else f(a) + sum(f)(a + 1, b)

val sumInts = sum(x => x) // error: missing arguments

上面代码的最后一行中sum的部分应用不会转换为函数类型。相反,编译器将生成一条错误消息,指出缺少方法sum的参数。可以通过为部分应用程序提供预期类型来解决该问题,例如通过使用其类型注释sumInts的定义:

val sumInts: (int, int) => double = sum(x => x) // OK

另一方面,Scala 2.0版现在会在必要时自动将具有空参数列表的方法应用于()参数列表。例如,上面的show表达式现在将扩展为

show(x.toString())

答案 2 :(得分:2)

你应该使用eta-expansion

println(abc(someFunction _))