Reputation: 30615
I don't think I quite understand currying, since I'm unable to see any massive benefit it could provide. Perhaps someone could enlighten me with an example demonstrating why it is so useful. Does it truly have benefits and applications, or is it just an over-appreciated concept?
Upvotes: 10
Views: 2457
Reputation: 20950
(There is a slight difference between currying and partial application, although they're closely related; since they're often mixed together, I'll deal with both terms.)
The place where I realized the benefits first was when I saw sliced operators:
incElems = map (+1)
--non-curried equivalent: incElems = (\elems -> map (\i -> (+) 1 i) elems)
IMO, this is totally easy to read. Now, if the type of (+)
was (Int,Int) -> Int
*, which is the uncurried version, it would (counter-intuitively) result in an error -- but curryied, it works as expected, and has type [Int] -> [Int]
.
You mentioned C# lambdas in a comment. In C#, you could have written incElems
like so, given a function plus
:
var incElems = xs => xs.Select(x => plus(1,x))
If you're used to point-free style, you'll see that the x
here is redundant. Logically, that code could be reduced to
var incElems = xs => xs.Select(curry(plus)(1))
which is awful due to the lack of automatic partial application with C# lambdas. And that's the crucial point to decide where currying is actually useful: mostly when it happens implicitly. For me, map (+1)
is the easiest to read, then comes .Select(x => plus(1,x))
, and the version with curry
should probably be avoided, if there is no really good reason.
Now, if readable, the benefits sum up to shorter, more readable and less cluttered code -- unless there is some abuse of point-free style done is with it (I do love (.).(.)
, but it is... special)
Also, lambda calculus would get impossible without using curried functions, since it has only one-valued (but therefor higher-order) functions.
* Of course it actually in Num
, but it's more readable like this for the moment.
Update: how currying actually works.
Look at the type of plus
in C#:
int plus(int a, int b) {..}
You have to give it a tuple of values -- not in C# terms, but mathematically spoken; you can't just leave out the second value. In haskell terms, that's
plus :: (Int,Int) -> Int,
which could be used like
incElem = map (\x -> plus (1, x)) -- equal to .Select (x => plus (1, x))
That's way too much characters to type. Suppose you'd want to do this more often in the future. Here's a little helper:
curry f = \x -> (\y -> f (x,y))
plus' = curry plus
which gives
incElem = map (plus' 1)
Let's apply this to a concrete value.
incElem [1]
= (map (plus' 1)) [1]
= [plus' 1 1]
= [(curry plus) 1 1]
= [(\x -> (\y -> plus (x,y))) 1 1]
= [plus (1,1)]
= [2]
Here you can see curry
at work. It turns a standard haskell style function application (plus' 1 1
) into a call to a "tupled" function -- or, viewed at a higher level, transforms the "tupled" into the "untupled" version.
Fortunately, most of the time, you don't have to worry about this, as there is automatic partial application.
Upvotes: 13
Reputation: 62818
I used to think that currying was simple syntax sugar that saves you a bit of typing. For example, instead of writing
(\ x -> x + 1)
I can merely write
(+1)
The latter is instantly more readable, and less typing to boot.
So if it's just a convenient short cut, why all the fuss?
Well, it turns out that because function types are curried, you can write code which is polymorphic in the number of arguments a function has.
For example, the QuickCheck
framework lets you test functions by feeding them randomly-generated test data. It works on any function who's input type can be auto-generated. But, because of currying, the authors were able to rig it so this works with any number of arguments. Were functions not curried, there would be a different testing function for each number of arguments - and that would just be tedious.
Upvotes: 1
Reputation: 10781
It's not the best thing since sliced bread, but if you're using lambdas anyway, it's easier to use higher-order functions without using lambda syntax. Compare:
map (max 4) [0,6,9,3] --[4,6,9,4]
map (\i -> max 4 i) [0,6,9,3] --[4,6,9,4]
These kinds of constructs come up often enough when you're using functional programming, that it's a nice shortcut to have and lets you think about the problem from a slightly higher level--you're mapping against the "max 4
" function, not some random function that happens to be defined as (\i -> max 4 i)
. It lets you start to think in higher levels of indirection more easily:
let numOr4 = map $ max 4
let numOr4' = (\xs -> map (\i -> max 4 i) xs)
numOr4 [0,6,9,3] --ends up being [4,6,9,4] either way;
--which do you think is easier to understand?
That said, it's not a panacea; sometimes your function's parameters will be the wrong order for what you're trying to do with currying, so you'll have to resort to a lambda anyway. However, once you get used to this style, you start to learn how to design your functions to work well with it, and once those neurons starts to connect inside your brain, previously complicated constructs can start to seem obvious in comparison.
Upvotes: 7
Reputation: 120711
The "no-currying" form of partial application works like this:
f : (A ✕ B) → C
a : A
a
and f
(we don't evaluate f
at all, for the time being)b : B
A
and B
argument, we can evaluate f
in its original form...a
from the closure, and evaluate f(a,b)
.A bit complicated, isn't it?
When f
is curried in the first place, it's rather simpler:
f : A → B → C
a : A
– which we can just do: f a
b : B
f a
to b
.So far so nice, but more important than being simple, this also gives us extra possibilities for implementing our function: we may be able to do some calculations as soon as the a
argument is received, and these calculations won't need to be done later, even if the function is evaluated with multiple different b
arguments!
To give an example, consider this audio filter, an infinite impulse response filter. It works like this: for each audio sample, you feed an "accumulator function" (f
) with some state parameter (in this case, a simple number, 0 at the beginning) and the audio sample. The function then does some magic, and spits out the new internal state1 and the output sample.
Now here's the crucial bit – what kind of magic the function does depends on the coefficient2 λ
, which is not quite a constant: it depends both on what cutoff frequency we'd like the filter to have (this governs "how the filter will sound") and on what sample rate we're processing in. Unfortunately, the calculation of λ
is a bit more complicated (lp1stCoeff $ 2*pi * (νᵥ ~*% δs)
than the rest of the magic, so we wouldn't like having to do this for every single sample, all over again. Quite annoying, because νᵥ
and δs
are almost constant: they change very seldom, certainly not at each audio sample.
But currying saves the day! We simply calculate λ
as soon as we have the necessary parameters. Then, at each of the many many audio samples to come, we only need to perform the remaining, very easy magic: yⱼ = yⱼ₁ + λ ⋅ (xⱼ - yⱼ₁)
. So we're being efficient, and still keeping a nice safe referentially transparent purely-functional interface.
1 Note that this kind of state-passing can generally be done more nicely with the State
or ST
monad, that's just not particularly beneficial in this example
2 Yes, this is a lambda symbol. I hope I'm not confusing anybody – fortunately, in Haskell it's clear that lambda functions are written with \
, not with λ
.
Upvotes: 3
Reputation: 30227
Currying has the convenience features mentioned in other answers, but it also often serves to simplify reasoning about the language or to implement some code much easier than it could be otherwise. For example, currying means that any function at all has a type that's compatible with a ->b
. If you write some code whose type involves a -> b
, that code can be made work with any function at all, no matter how many arguments it takes.
The best known example of this is the Applicative
class:
class Functor f => Applicative f where
pure :: a -> f a
(<*>) :: f (a -> b) -> f a -> f b
And an example use:
-- All possible products of numbers taken from [1..5] and [1..10]
example = pure (*) <*> [1..5] <*> [1..10]
In this context, pure
and <*>
adapt any function of type a -> b
to work with lists of type [a]
. Because of partial application, this means you can also adapt functions of type a -> b -> c
to work with [a]
and [b]
, or a -> b -> c -> d
with [a]
, [b]
and [c]
, and so on.
The reason this works is because a -> b -> c
is the same thing as a -> (b -> c)
:
(+) :: Num a => a -> a -> a
pure (+) :: (Applicative f, Num a) => f (a -> a -> a)
[1..5], [1..10] :: Num a => [a]
pure (+) <*> [1..5] :: Num a => [a -> a]
pure (+) <*> [1..5] <*> [1..10] :: Num a => [a]
Another, different use of currying is that Haskell allows you to partially apply type constructors. E.g., if you have this type:
data Foo a b = Foo a b
...it actually makes sense to write Foo a
in many contexts, for example:
instance Functor (Foo a) where
fmap f (Foo a b) = Foo a (f b)
I.e., Foo
is a two-parameter type constructor with kind * -> * -> *
; Foo a
, the partial application of Foo
to just one type, is a type constructor with kind * -> *
. Functor
is a type class that can only be instantiated for type constrcutors of kind * -> *
. Since Foo a
is of this kind, you can make a Functor
instance for it.
Upvotes: 3
Reputation: 7672
It's somewhat dubious to ask what the benefits of currying are without specifying the context in which you're asking the question:
curry
and uncurry
also help for certain conveniences within functional programming languages too, I can think of arrows within Haskell as a specific example of where you would use curry
and uncurry
a bit to apply things to different pieces of an arrow, etc...Upvotes: 2
Reputation: 23782
One benefit of currying is that it allows partial application of functions without the need of any special syntax/operator. A simple example:
mapLength = map length
mapLength ["ab", "cde", "f"]
>>> [2, 3, 1]
mapLength ["x", "yz", "www"]
>>> [1, 2, 3]
map :: (a -> b) -> [a] -> [b]
length :: [a] -> Int
mapLength :: [[a]] -> [Int]
The map
function can be considered to have type (a -> b) -> ([a] -> [b])
because of currying, so when length
is applied as its first argument, it yields the function mapLength
of type [[a]] -> [Int]
.
Upvotes: 4