Scala中的函数式编程的作者give this作为scala中curry
的定义:
def curry[A,B,C](f: (A, B) => C): A => (B => C) =
a => b => f(a, b)
但是,如果我们将它应用于采用参数类型的函数,例如:
def isSorted[A](as: Array[A], ordered:(A,A)=>Boolean) =
if(as.size < 2)
true else
as.zip(as.drop(1)).map(ordered.tupled).reduce(_ && _)
然后结果希望A
(在isSorted
中)无效:
scala> curry(isSorted)
res29: Array[Nothing] => (((Nothing, Nothing) => Boolean) => Boolean) = <function1>
这显然不是所期望的。咖喱应该用不同的方式定义,还是用不同的方式调用,或者在Scala中实现curry是不切实际的?
答案 0 :(得分:3)
事实证明我可以这样叫咖喱:
curry(isSorted[Int])
哪个收益率:
scala> curry(isSorted[Int])
res41: Array[Int] => (((Int, Int) => Boolean) => Boolean) = <function1>
答案 1 :(得分:3)
你在这里遇到了两个不同的问题。第一个是isSorted
传递给curry
时被强制变为单态。第二个是Scala的类型推断让你失败了。
这是Scala中function and a method之间的区别之一。 isSorted
被eta扩展为一个函数,而函数又是一个Scala值,而不是一个方法。 Scala值始终是单态的,只有方法可以是多态的。对于任何类型(A, B) C
的方法(这是method type的语法,并且不同于(A, B) => C
,它是一个函数,因此是一个值),默认的eta-expansion将会产生结果在该arity的所有函数的超类中,即(Nothing, Nothing) => Any
。这对您看到的所有Nothing
负责(您没有Any
因为isSorted
的返回值是单态的。)
你可能会想象尽管Scala值的单形性质,你可以理想地做一些像
这样的事情。def first[A, B](x: A, y: B): A = x
curry(first)(5)(6) // This doesn't compile
这是Scala的本地类型推断咬你。它适用于从左到右的单独参数列表first
是获得类型推断的第一件事,如上所述,它被推断为(Nothing, Nothing) => Any
。这与随后的Int
冲突。
正如您已经意识到的,解决此问题的一种方法是注释您传递给curry
的多态方法,以便它可以扩展为正确的类型。这几乎肯定是要走的路。
你可能做的另一件事(虽然我不认为除了教学目的之外它会提供任何服务)是为了讨论curry
本身,并按如下方式定义:
def curryTwo[A, B, C](x: A)(y: B)(f: (A, B) => C): C = f(x, y)
一方面,由于从左到右的类型推断,现在下面的工作正常。
curryTwo(5)(6)(first) // 5
另一方面,要在您希望使用curryTwo
的情况下使用curry
,您需要向Scala&#39提供类型;无论如何都是类型推理引擎。