【问题标题】:Strange implicit def with function parameter behaviour in ScalaScala中带有函数参数行为的奇怪隐式def
【发布时间】:2015-10-27 12:04:25
【问题描述】:

我用 Scala 编写了一个简单的代码,将 Function1 隐式转换为某个案例类。

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。我可以猜到它的原因,但不知道它为什么不起作用。

【问题讨论】:

标签: scala implicit-conversion


【解决方案1】:

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

但并非总是如此。十年前,只要使用方法名,你就可以得到你的函数值。

新的在线规范省略了“更改日志”附录,因此为了记录,这里是有人对括号感到沮丧并引入当前规则的时刻。 (参见 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 参数的预期类型是字符串,而不是函数类型。新公约不允许某些以前的合法代码。示例:

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】:

    您的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不是更容易吗?

    【讨论】:

      【解决方案3】:

      你应该使用 eta-expansion

      println(abc(someFunction _))
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-05-02
        • 2021-02-23
        • 1970-01-01
        • 1970-01-01
        • 2014-01-28
        • 2011-04-07
        相关资源
        最近更新 更多