Scala中defs/lambdas的隐式转换?

Bjö*_*rge 8 scala implicit implicit-conversion

我刚刚遇到函数和对象之间的奇怪差异(scala 2.10):

implicit def conv(c: Int => String) : (PrintStream => Int => Unit) = p => v => p.println(c(v))
def f(h: PrintStream => Int => Unit) : Unit = h(System.out)(1)

def a(x: Int) = x.toString
val b = (x: Int) => x.toString

//    def main(args: Array[String]) = f(a) // fail
//    def main(args: Array[String]) = f((x: Int) => x.toString) // fail
def main(args: Array[String]) = f(b) // ok
Run Code Online (Sandbox Code Playgroud)

为什么defs/lambda文字和lambda vals之间有区别?

更新:显然,二进制函数不会出现问题:只有转换函数至少有两个参数时,才能将函数隐式转换为二阶函数

我检查过这个,确实以下代码有效:

implicit def conv(c: (Int,Unit) => String) : (PrintStream => Int => Unit) = p => v => p.println(c(v,()))
def f(h: PrintStream => Int => Unit) : Unit = h(System.out)(1)

def a(x: Int, y : Unit) = x.toString
val b = (x: Int, y : Unit) => x.toString

def main(args: Array[String]) = f(a) // ok
def main(args: Array[String]) = f((x: Int, y: Unit) => x.toString) // ok
def main(args: Array[String]) = f(b) // ok
Run Code Online (Sandbox Code Playgroud)

同样,Nullary函数也不会造成问题:

implicit def conv(c: () => String) : (PrintStream => Int => Unit) = p => v => p.println(c())
def f(h: PrintStream => Int => Unit) : Unit = h(System.out)(1)

def a() = "1"
val b = () => "1"

def main(args: Array[String]) = f(a) // ok
def main(args: Array[String]) = f(() => "1") // ok
def main(args: Array[String]) = f(b) // ok
Run Code Online (Sandbox Code Playgroud)

所以,重新提出这个问题:为什么这对UNARY方法和函数不起作用?

更新:问题似乎也与目标类型(f的参数h的类型)有关.以下也有效(这次,支持"eta-expansion计为跳数",因为我们需要从使用_创建方法值)

implicit def conv(c: Int => String) : Unit = ()
def f(h: Unit) : Unit = System.out.print("?")

def a(x: Int) = x.toString
val b = (x: Int) => x.toString

def main(args: Array[String]) = f(a _) // ok
def main(args: Array[String]) = f((x: Int) => x.toString) // ok
def main(args: Array[String]) = f(b) // ok
Run Code Online (Sandbox Code Playgroud)

Sar*_*ngh 3

在 scala 中defs, aremethods和 are 与functions.

scala> def a( x: Int, y: Int ): Int = x + y
a: (x: Int, y:Int)Int

scala> (x: Int, y: Int) => x + y
res0: (Int, Int) => Int = <function2>
Run Code Online (Sandbox Code Playgroud)

您可以通过部分应用 a 将其转换method为。function

scala> b _
res1: (Int, Int) => Int = <function2>
Run Code Online (Sandbox Code Playgroud)

所以..你可以这样做,

implicit def conv(c: Int => String) : (PrintStream => Int => Unit) = p => v => p.println(c(v))
def f(h: PrintStream => Int => Unit) : Unit = h(System.out)(1)

def a(x: Int) = x.toString
val af = a _

def main( args: Array[ String ] ) = f( af )
Run Code Online (Sandbox Code Playgroud)

另外,正如 @srgfed01 在他的评论中提到的......对于第二种情况,问题是......它们的类型没有明确指定,如果您正确指定类型......第二种情况将起作用。

scala> f( ( a => a.toString ): (Int => String) )
1
Run Code Online (Sandbox Code Playgroud)

或者

scala> f( ( _.toString ): (Int => String) )
1
Run Code Online (Sandbox Code Playgroud)

现在,关于methods和之间的差异functions...

您可以调用method不带括号的不带参数()...但是您不能调用没有括号的函数()

scala> def g() = 5
g: ()Int

scala> g
res15: Int = 5

scala> () => 5
res13: () => Int = <function0>

scala> res13
res14: () => Int = <function0>

scala> res13()
res15: 5
Run Code Online (Sandbox Code Playgroud)

methods与 Java 不同的最重要原因之一functions是 Scala 的创建者希望与 Java 实现无缝的互操作性,而不是受到 Java 的限制。

所以methods( def) 与 Java 非常相似,methods但又有所functions不同,这使methods他们能够以无限的自由度按照他们想要的方式创建 Scala。

另外...另一个主要区别是methods可以接受Type-classesfunctions不能接受。基本上你可以有通用的methods

scala> :paste

trait Behave {
    def behave
}

class A( elem: String ) extends Behave {
  def behave() {
    println( elem )
  }
}

// Exiting paste mode, now interpreting.

defined trait Behave
defined class A
Run Code Online (Sandbox Code Playgroud)

现在您可以定义一个通用方法,

scala> def check[ T <: Behave ]( t: T ): Unit = t.behave()
check: [T <: Behave](t: T)Unit
Run Code Online (Sandbox Code Playgroud)

但你不能定义这样的函数,

scala> ( t: T ) => t.behave()
<console>:8: error: not found: type T
          ( t: T ) => t.behave()
Run Code Online (Sandbox Code Playgroud)

或者像这样

scala> ( t: (T <: Behave) ) => t.behave()
<console>:1: error: ')' expected but '<:' found.
   ( t: (T <: A) ) => t.behave()
Run Code Online (Sandbox Code Playgroud)