6

A coworker of mine sent me a question as follows:

Implement a HOF(higher order function) that performs currying, the signature of your function is as follows:

def curry[A,B,C](f:(A,B) => C) : A => B => C

Similarly, implement a function that performs uncurrying as follows:

def uncurry[A,B,C](f:A => B => C): (A,B) => C

The way I understand currying is that if you have a function that takes multiple parameters, you can repeatedly apply the function to each one of the paramaters until you get the result.

So something along the lines of f:(A,B) => C turns into A => f(A,_) => f(B)????

And uncurrying would be to consolidate this application into one function as follows:

f:A=>B=>C would be f(A,B)?

Maybe I am just being confused by the syntax here but it would be great if somebody could point out what I am missing here.

Thanks

sc_ray
  • 7,803
  • 11
  • 63
  • 100

2 Answers2

12

Hopefully this fully worked example with a bunch of comments is easy to understand. Please reply if you have questions.

You can execute this code by dropping it in a Scala interpreter.

// Here's a trait encapsulating the definition your coworker sent.
trait Given {
  def curry[A,B,C](f:(A,B) => C) : A => B => C
  def uncurry[A,B,C](f:A => B => C): (A,B) => C
}

object Impl extends Given {
  // I'm going to implement uncurry first because it's the easier of the
  // two to understand.  The bit in curly braces after the equal sign is a
  // function literal which takes two arguments and applies the to (i.e.
  // uses it as the arguments for) a function which returns a function.
  // It then passes the second argument to the returned function.
  // Finally it returns the value of the second function.
  def uncurry[A,B,C](f:A => B => C): (A,B) => C = { (a: A, b: B) => f(a)(b) }

  // The bit in curly braces after the equal sign is a function literal
  // which takes one argument and returns a new function.  I.e., curry()
  // returns a function which when called returns another function
  def curry[A,B,C](f:(A,B) => C) : A => B => C = { (a: A) => { (b: B) => f(a,b) } }
}

def add(a: Int, b: Long): Double = a.toDouble + b
val spicyAdd = Impl.curry(add)
println(spicyAdd(1)(2L)) // prints "3.0"
val increment = spicyAdd(1) // increment holds a function which takes a long and adds 1 to it.
println(increment(1L)) // prints "2.0"
val unspicedAdd = Impl.uncurry(spicyAdd)
println(unspicedAdd(4, 5L)) // prints "9.0"

How about a less numerical example?

def log(level: String, message: String) { 
  println("%s: %s".format(level, message)) 
} 
val spicyLog = Impl.curry(log) // spicyLog's type is String => Unit
val logDebug = spicyLog("debug") // This new function will always prefix the log
                                 // message with "debug".
val logWarn = spicyLog("warn") // This new function will always prefix the log 
                               // message with "warn".
logDebug("Hi, sc_ray!") // prints "debug: Hi, sc_ray!"
logWarn("Something is wrong.") // prints "warn: Something is wrong."

Update You replied asking "How does the compiler evaluate expressions such as a => b => f(a,b)." Well it doesn't. At least the way things are defined in your coworker's snippet, that wouldn't compile. In general, though, if you see something of the form A => B => C that means "a function which takes an A as an argument; it returns a function which takes a B as an argument and returns a C."

Leif Wickland
  • 3,693
  • 26
  • 43
  • Thanks for the explanation. This is a very pragmatic example. I am just getting all muddled up trying to think how the compiler evaluates expressions such as a => b => f(a,b) – sc_ray Dec 10 '12 at 03:09
  • Thanks. It's much clearer now. I am marking this as the answer. – sc_ray Dec 10 '12 at 04:06
  • Glad to help. Good luck in journey learning functional programming. – Leif Wickland Dec 10 '12 at 04:09
  • @sc_ray Ah, looks like this answer did the trick. Always glad to know that one less person has one less issue grokking FP! Good luck! – Faiz Dec 10 '12 at 04:52
8

I'm not sure I really understand your question - what would you like to know, besides the actual implementation? As described, it should be quite trivial:

def curry[A,B,C](f:(A,B) => C): A => B => C = 
  a => b => f(a,b)

What a => b => f(a,b) means is, "a function of one argument, a, whose return value is b => f(a,b) which is again, a function of one argument, b, whose return value is what you get of you execute f(a,b) (whose type is C)"

a => b => f(a, b) can be written slightly more verbosely if it helps?

 { (a: A) => {           // a function of *one* argument, `a`
      (b: B) => {        // a function of *one* argument, `b`
         f(a, b)         // whose return value is what you get of you execute `f(a,b)` (whose type is `C`)
      }
   }
 }

and

def uncurry[A,B,C](f:A => B => C): (A,B) => C = 
  (a, b) => f(a)(b)

Where (a, b) => f(a)(b) means, "A function of two arguments (a, b), whose return value is what you get when you first apply a to the HoF f, which returns a function that in turn consumes the b to return a C".

Does that help?

Faiz
  • 16,025
  • 5
  • 48
  • 37
  • I guess I wanted to know what exactly happens when you type something as a => b => f(a,b)? – sc_ray Dec 10 '12 at 02:47
  • @sc_ray I hope the additional explanation helps? – Faiz Dec 10 '12 at 02:54
  • 1
    Thanks for your explanation. I am just finding this whole concept of currying rather trippy, so we have a function with multiple parameters and we are deferring the execution of the function a parameter at a time by rewriting the function as a => b => f(a,b)? How does the compiler evaluate an expression such as a => b => f(a,b)? – sc_ray Dec 10 '12 at 03:06