在 Scala 中使用参数类型进行 Curry

Curry in scala with parametric types

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>

这显然不是我们想要的。 curry 应该有不同的定义,或不同的调用,还是在 Scala 中实现 curry 不切实际?

原来我可以这样调用curry:

curry(isSorted[Int])

产生:

scala> curry(isSorted[Int])
res41: Array[Int] => (((Int, Int) => Boolean) => Boolean) = <function1>

您 运行 遇到了两个不同的问题。第一个是 isSorted 传递给 curry 时被迫变为单态。第二个是 Scala 的类型推断让你失望了。

这是 function and a method matters in Scala. isSorted is eta-expanded into a function which in turn is a Scala value, not a method. Scala values are always monomorphic, only methods can be polymorphic. For any method of types (A, B) C (this is the syntax for a method type(A, B) => C 之间的差异之一,后者是一个函数,因此是一个值),默认的 eta 扩展将导致该元数的所有函数的超类,即 (Nothing, Nothing) => Any。这对您看到的所有 Nothing 负责(您没有任何 Any,因为 isSorted 在其 return 值中是单态的)。

尽管 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

另一方面,要在您想使用 curry 的场景中使用 curryTwo,您无论如何都需要为 Scala 的类型推断引擎提供类型.