dopatraman
dopatraman

Reputation: 13908

Writing a function to curry any function

For the record I find it very annoying that functions are not automatically curried in Scala. I'm trying to write a factory that takes in any function and returns a curried version:

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

Basically what I have defined here is a function curry that takes as an argument a function fn that is of type _ => _ and returns a curried version of function fn. Obviously this didnt work because Java.

This was the error I got:

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

Can any gurus out there help me figure out why this doesnt work? I don't mean to sound snarky, I am used to functional languages treating all types as functions. Please help this Scala newbie.

(I tagged this question with haskell because I'm trying to get Scala functions to behave like Haskell functions :'(

UPDATE

Just to clarify, I need a curryN function, so a function that curries any other function regardless of its arity.

Side note, some people have pointed out that increasing the number of fn's arguments would solve the problem. Nope:

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

Upvotes: 3

Views: 528

Answers (3)

Aaron Rumery
Aaron Rumery

Reputation: 562

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))

For additional arity, you would simply need to add additional params to the above function definition.

Otherwise, you may want to do something like Can you curry a function with varargs in scala?

Upvotes: 0

robot1208
robot1208

Reputation: 311

This can be done using the curried method of functions. You need to access the function itself as a partially applied function and get its curried form, like so:

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

val fnCurryable = (fn _).curried

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

The same second line would work to curry any function with 2-22 arguments due to scala's powerful type inference. Also, remember that you can declare your functions to be curryable in their declaration. This would do the same as above:

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

The use of multiple argument lists means this function is called as fnCurryable(1)(2) and can NEVER be called as fnCurryable(1, 2). This conversion is basically what .curried does. This is based on the function traits described on:

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

Upvotes: 0

Rex Kerr
Rex Kerr

Reputation: 167891

Scala doesn't allow you to abstract over the arity of a function. Thus, you need to use a typeclass-style approach (which allows you to abstract over just about anything, after you do all the manual work for it).

So, in particular, you do something like

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)

Now you can use it like so:

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

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

There is probably already something like this in Shapeless, but in this case you can roll your own, albeit with some tedium (and/or a code generator).

(Note: if you want to "curry" A => Z, you can write a Function1Currier that just returns the function untouched.)

Upvotes: 7

Related Questions