在 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 的类型推断引擎提供类型.
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 的类型推断引擎提供类型.