编写一个函数来curry任何函数

时间:2016-06-24 21:23:24

标签: scala haskell functional-programming

对于记录,我发现在Scala中不会自动调整函数非常烦人。我正在尝试编写一个接受任何功能的工厂并返回一个curried版本:

def curry(fn:(_ => _)) = (fn _).curried

基本上我在这里定义的是一个函数curry,它将fn类型的函数_ => _作为参数,并返回函数fn的curried版本。显然这不起作用,因为Java。

这是我得到的错误:

error: _ must follow method; cannot follow fn.type
       def curry(fn:(_ => _)) = (fn _).curried

那里的任何大师都可以帮我弄清楚为什么这不起作用?我并不意味着听起来很讽刺,我习惯于将所有类型作为函数处理的函数式语言。请帮助这个Scala新手。

(我用haskell标记了这个问题,因为我试图让Scala函数像Haskell函数一样运行:'(

更新

只是为了澄清,我需要一个curryN函数,所以这个函数可以解释任何其他函数而不管它是什么。

旁注,有些人指出增加fn的参数可以解决问题。都能跟得上:

def curry2(fn:((_, _) => _)) = (fn _).curried
error: _ must follow method; cannot follow fn.type
       def curry2(fn:((_, _) => _)) = (fn _).curried

3 个答案:

答案 0 :(得分:7)

Scala不允许您对函数的arity进行抽象。因此,您需要使用类型类方法(在完成所有手动工作之后,它允许您抽象几乎所有内容)。

所以,特别是,你做了类似

的事情
sealed trait FunctionCurrier[Unc, Cur] { def apply(fn: Unc): Cur }
final class Function2Currier[A, B, Z]
extends FunctionCurrier[(A, B) => Z, A => B => Z] {
  def apply(fn: (A, B) => Z): (A => B => Z) = fn.curried
}
// Repeat for Function3 through Function21

implicit def makeCurrierForFunction2[A, B, Z]: Function2Currier[A, B, Z] =
  new Function2Currier[A, B, Z]
// Again, repeat for Function3 through Function21

def curryAll[Unc, Cur](fn: Unc)(implicit cf: FunctionCurrier[Unc, Cur]): Cur =
  cf(fn)

现在您可以这样使用它:

scala> def foo(a: Int, b: String) = a < b.length
foo: (a: Int, b: String)Boolean

scala> curryAll(foo _)
res0: Int => (String => Boolean) = <function1>

在Shapeless中可能已经有类似的东西了,但是在这种情况下你可以自己滚动,虽然有些乏味(和/或代码生成器)。

(注意:如果您想要&#34;咖喱&#34; A => Z,您可以编写一个Function1Currier,只返回未触及的功能。)

答案 1 :(得分:0)

这可以使用curried函数方法完成。你需要将函数本身作为一个部分应用的函数来访问并得到它的curried形式,如下所示:

def fn(i: Int, j: Int) = i + j

val fnCurryable = (fn _).curried

val fnCurried = fnCurryable(1)
println(fnCurried(2))
//prints 3

由于scala的强大类型推断,相同的第二行可以用2-22个参数来计算任何函数。另外,请记住,您可以在声明中声明您的函数是可以理解的。这将与上面相同:

def fnCurryable(i: Int)(j: Int) = i + j

使用多个参数列表意味着此函数被称为fnCurryable(1)(2),并且永远不能被称为fnCurryable(1, 2)。此转换基本上是.curried的作用。 这基于以下描述的功能特征:

http://www.scala-lang.org/api/2.11.8/index.html#scala.package

答案 2 :(得分:0)

def toCurry[A](f: (A, A) => A): A => A => A = x => f(x, _)
val addTwoNum = (x: Int, y: Int) => x + y
val curriedAddTwoNum = toCurry(addTwoNum)
val part1Curry = curriedAddTwoNum(5)
println(part1Curry(2))

为获得更多便利,您只需在上述函数定义中添加其他参数即可。

否则,您可能想要做类似Can you curry a function with varargs in scala?

的操作