Partial Application, Currying and a bit of Haskell
In the last Fun Club meetup the subject of currying and partial application came up. I must confess to have myself been unsure on the correct definitions of these things, thankfully Markus knows his stuff, so all was good. Well, anyway, I thought I should brush up, and then I thought it might be nice to share my findings.
First up partial application:
Partial application is the process of transforming a function by fixing some of its arguments to produce another function.
For example in Haskell you could make a function that adds two to everything by fixing the first argument of the addition function to 2:
Easy huh :-) In fact the hardest thing about partial application is to remember to refer to it as partial application or partial function application, but never as a partial function, because partial functions are in fact different things entirely. I hadn't realised this until today, so must've been embarrassing myself quite a bit in the past with my incorrect terminology use.
A partial function is a function that is not defined for all possible arguments of the specified type. For example a division function may not be defined when the divisor is 0.
Taken from the HaskellWiki
Okay, so now that's cleared up, and we're all going to remember this and never jumble our jargon again, next comes currying:
Currying is the process of transforming a function that takes multiple arguments into a function that takes just a single argument and returns another function if any arguments are still needed.
Taken from the HaskellWiki
Say we have a function, f, that adds up two arguments:
Then currying that function will give us a new function, g, that takes just one argument, and spits out as a result yet another function, which itself takes one argument.
g(x) = h(y) where h(y) = x + y
Now instead of adding up 3 and 4 by using f(3, 4), we could do, g(3) to get h, and then h(4) to give us 7, in other words g(3)(4).
These seem remarkably similar ...
Well yes, but they're not the same. In the case of partial application you're making a new function by fixing arguments, and in the case of currying you're making a new function by currying your original function, then calling this curried function to create a new function. Clear as mud.
Take the function, f(x,y,z). If we curry it we get a function g(x) such that for all values of a, b and c, g(a)(b)(c) = f(a,b,c).
Meanwhile, if for any value of a and b we partially applied them to f to get a function h(z), then for all values of c, h(c) = f(a,b,c).
See, completely different. Currying will give you a chain of single argument functions, whereas partial application will give you a new function, which may even have multiple arguments, depending on how many arguments you fixed.
I'm a Haskell programmer so let's look at currying through Haskell's eyes. In the Haskell Prelude there's a curry function whose type is as follows:
curry :: ((a, b) -> c) -> a -> b -> c
The Haskell type of a function that takes a pair as a single argument and returns a single value is:
The Haskell type of a function that takes two arguments and returns a single value is:
The Haskell type of the curry function indicates that it turns functions like f with a single pair argument, into functions like g with two arguments. The curry function is converting functions into what is known as curried form. This form is far more convenient for partial applications.
Although it's not immediately obvious, despite what the curry function says, all functions in Haskell are in fact curried. In Haskell functions are really just chains of single argument functions. A call like f 3 5, is actually being evaluated as (f 3) 5. Looking at it like this starts to make the type notation feel more intuitive.
If everything's curried already what do you do with a curry function?
In Haskell I mostly find curry and its opposite number uncurry come in handy to spare me from an anonymous function.
So let's imagine you have pairs of numbers, and you'd like to sum each together, you could do this like:
map (\(x,y) -> x + y) [(1,5), (3,4), (5,3)]
Using uncurry on addition instead, makes things nicer:
map (uncurry (+)) [(1,5), (3,4), (5,3)]
Also apart from curry and uncurry, there are a couple of other Haskell Prelude functions that I also find often come in handy to save yourself from writing an annoying little anonymous function, these are:
flip :: (a -> b -> c) -> b -> a -> c