184

I just learned about currying, and while I think I understand the concept, I'm not seeing any big advantage in using it.

As a trivial example I use a function that adds two values (written in ML). The version without currying would be

fun add(x, y) = x + y

and would be called as

add(3, 5)

while the curried version is

fun add x y = x + y 
(* short for val add = fn x => fn y=> x + y *)

and would be called as

add 3 5

It seems to me to be just syntactic sugar that removes one set of parentheses from defining and calling the function. I've seen currying listed as one of the important features of a functional languages, and I'm a bit underwhelmed by it at the moment. The concept of creating a chain of functions that consume each a single parameter, instead of a function that takes a tuple seems rather complicated to use for a simple change of syntax.

Is the slightly simpler syntax the only motivation for currying, or am I missing some other advantages that are not obvious in my very simple example? Is currying just syntactic sugar?

8
  • 57
    Currying alone is essentially useless, but having all functions curried by default makes a lot of other features much nicer to use. It's hard to appreciate this until you've actually used a functional language for a while. Feb 1, 2013 at 20:13
  • 4
    Something that was mentioned in passing by delnan in a comment on JoelEtherton's answer, but that I thought I would mention explicitly, is that (at least in Haskell) you can partially apply with not only functions but also type constructors -- this can be quite handy; this might be something to think on.
    – paul
    Feb 1, 2013 at 21:44
  • All have given examples of Haskell. One may wonder currying is useful only in Haskell.
    – Manoj R
    Feb 4, 2013 at 5:37
  • @ManojR All have not given examples in Haskell.
    – phwd
    Feb 5, 2013 at 16:12
  • 1
    The question generated a fairly interesting discussion on Reddit.
    – yannis
    Feb 8, 2013 at 4:49

15 Answers 15

136

With curried functions you get easier reuse of more abstract functions, since you get to specialize. Let's say that you have an adding function

add x y = x + y

and that you want to add 2 to every member of a list. In Haskell you would do this:

map (add 2) [1, 2, 3] -- gives [3, 4, 5]
-- actually one could just do: map (2+) [1, 2, 3], but that may be Haskell specific

Here the syntax is lighter than if you had to create a function add2

add2 y = add 2 y
map add2 [1, 2, 3]

or if you had to make an anonymous lambda function:

map (\y -> 2 + y) [1, 2, 3]

It also allows you to abstract away from different implementations. Let's say you had two lookup functions. One from a list of key/value pairs and a key to a value and another from a map from keys to values and a key to a value, like this:

lookup1 :: [(Key, Value)] -> Key -> Value -- or perhaps it should be Maybe Value
lookup2 :: Map Key Value -> Key -> Value

Then you could make a function that accepted a lookup function from Key to Value. You could pass it any of the above lookup function, partially applied with either a list or a map, respectively:

myFunc :: (Key -> Value) -> .....

In conclusion: currying is good, because it lets you specialize/partially apply functions using a lightweight syntax and then pass these partially applied functions around to higher order function such as map or filter. Higher order functions (which take functions as parameters or yield them as results) are the bread and butter of functional programming, and currying and partially applied functions enable higher order functions to be used much more effectively and concisely.

3
  • 32
    It's worth noting that, because of this, the argument order used for functions in Haskell is often based on how likely partial application is, which in turn makes the benefits described above apply (ha, ha) in more situations. Currying by default thus ends up being even more beneficial than is apparent from specific examples like the ones here. Feb 1, 2013 at 21:08
  • wat. "One from a list of key/value pairs and a key to a value and another from a map from keys to values and a key to a value" May 6, 2017 at 7:46
  • @MateenUlhaq It is a continuation of the previous sentence, where I suppose that we want to get a value based on a key, and we have two ways of doing that. The sentence enumerates those two ways. In the first way, you are given a list of key/value pairs and the key that we want to find the value for, and in the other way we are given a proper map, and again a key. It might help to look at the code immediately following the sentence.
    – Boris
    May 8, 2017 at 13:30
56

The practical answer is that currying makes creating anonymous functions much easier. Even with a minimal lambda syntax, it's something of a win; compare:

map (add 1) [1..10]
map (\ x -> add 1 x) [1..10]

If you have an ugly lambda syntax, it's even worse. (I'm looking at you, JavaScript, Scheme and Python.)

This becomes increasingly useful as you use more and more higher-order functions. While I use more higher-order functions in Haskell than in other languages, I've found I actually use the lambda syntax less because something like two thirds of the time, the lambda would just be a partially applied function. (And much of the other time I extract it into a named function.)

More fundamentally, it isn't always obvious which version of a function is "canonical". For example, take map. The type of map can be written in two ways:

map :: (a -> b) -> [a] -> [b]
map :: (a -> b) -> ([a] -> [b])

Which one is the "correct" one? It's actually hard to say. In practice, most languages use the first one--map takes a function and a list and returns a list. However, fundamentally, what map actually does is map normal functions to list functions--it takes a function and returns a function. If map is curried, you don't have to answer this question: it does both, in a very elegant way.

This becomes especially important once you generalize map to types other than list.

Also, currying really isn't very complicated. It's actually a bit of a simplification over the model most languages use: you don't need any notion of functions of multiple arguments baked into your language. This also reflects the underlying lambda calculus more closely.

Of course, ML-style languages do not have a notion of multiple arguments in curried or in uncurried form. The f(a, b, c) syntax actually corresponds to passing in the tuple (a, b, c) into f, so f still only takes on argument. This is actually a very useful distinction that I wish other languages would have because it makes it very natural to write something like:

map f [(1,2,3), (4,5,6), (7, 8, 9)]

You couldn't easily do this with languages that have the idea of multiple arguments baked right in!

5
  • 1
    "ML-style languages do not have a notion of multiple arguments in curried or in uncurried form": in this respect, is Haskell ML-style?
    – Giorgio
    Feb 2, 2013 at 14:35
  • 1
    @Giorgio: Yeah. Feb 2, 2013 at 17:47
  • 1
    Interesting. I know some Haskell and I am learning SML right now, so it is interesting to see differences and similarities between the two languages.
    – Giorgio
    Feb 2, 2013 at 18:02
  • Great answer, and if you're still not convinced just think about Unix pipelines which are similar to lambda streams Oct 14, 2016 at 23:23
  • The "practical" answer is not relevant much because the verbosity is usually avoided by partial application, not currying. And I'd argue here the syntax of lambda abstraction (despite the type declaration) is uglier than that (at least) in Scheme as it needs more built-in special syntactic rules to parse it correctly, which bloats the language spec without any gain about semantic properties.
    – FrankHB
    Aug 21, 2018 at 16:34
25

Currying may be useful if you have a function that you are passing around as a first class object, and you don't receive all of the parameters needed to evaluate it in one place in the code. You can simply apply one or more parameters when you get them and pass the result to another piece of code that has more parameters and finish evaluating it there.

The code to accomplish this is going to be simpler than if you need to get all the parameters together first.

Also, there is the possibility of more code reuse, since functions taking a single parameter (another curried function) don't have to match as specifically with all the parameters.

1
  • Yet, reuse is also limited because currying typically necessitates a strict ordering of arguments, which means you, at the time of declaration, will need to predict the order in which arguments will be available at the point of use.
    – Magne
    May 1, 2023 at 13:18
18

The main motivation (at least initially) for currying was not practical but theoretical. In particular, currying allows you to effectively get multi-argument functions without actually defining semantics for them or defining semantics for products. This leads to a simpler language with as much expressiveness as another, more complicated language, and so is desirable.

4
  • 2
    While the motivation here is theoretical, I think simplicity is almost always a practical advantage as well. Not worrying about multi-argument functions makes my life easier when I program, just as it would if I was working with semantics. Feb 3, 2013 at 20:41
  • 4
    @TikhonJelvis When you're programming, though, currying gives you other things to worry about, like the compiler not catching the fact that you passed too few arguments to a function, or even getting a bad error message in that case; when you don't use currying, the error is much more apparent.
    – Alex R
    Feb 4, 2013 at 4:29
  • I've never had problems like that: GHC, at the very least, is very good in that regard. The compiler always catches that sort of issue, and has good error messages for this error as well. Feb 4, 2013 at 6:35
  • 1
    I can't agree that the error messages qualify as good. Serviceable, yes, but they are not yet good. It also only catches that sort of issue if it results in a type error, i.e. if you later try to use the result as something other than a function (or you've type annotated, but relying on that for readable errors has its own problems); the error's reported location is divorced from its actual location.
    – Alex R
    Feb 4, 2013 at 14:45
15

(I'll give examples in Haskell.)

  1. When using functional languages it's very convenient that you can partially apply a function. Like in Haskell's (== x) is a function that returns True if its argument is equal to a given term x:

    mem :: Eq a => a -> [a] -> Bool
    mem x lst = any (== x) lst
    

    without currying, we'd have somewhat less readable code:

    mem x lst = any (\y -> y == x) lst
    
  2. This is related to Tacit programming (see also Pointfree style on Haskell wiki). This style focuses not on values represented by variables, but on composing functions and how information flows through a chain of functions. We can convert our example into a form that doesn't use variables at all:

    mem = any . (==)
    

    Here we view == as a function from a to a -> Bool and any as a function from a -> Bool to [a] -> Bool. By simply composing them, we get the result. This is all thanks to currying.

  3. The reverse, un-currying, is also useful in some situations. For example, let's say we want to split a list into two parts - elements that are smaller than 10 and the rest, and then concatenate those two lists. Splitting of the list is done by partition (< 10) (here we also use curried <). The result is of type ([Int],[Int]). Instead of extracting the result into its first and second part and combining them using ++, we can do this directly by uncurrying ++ as

    uncurry (++) . partition (< 10)
    

Indeed, (uncurry (++) . partition (< 10)) [4,12,11,1] evaluates to [4,1,12,11].

There are also important theoretical advantages:

  1. Currying is essential for languages that lack data types and have only functions, such as the lambda calculus. While these languages aren't useful for practical use, they're very important from a theoretical point of view.
  2. This is connected with the essential property of functional languages - functions are first class object. As we've seen, the conversion from (a, b) -> c to a -> (b -> c) means that the result of the latter function is of type b -> c. In other words, the result is a function.
  3. (Un)currying is closely connected to cartesian closed categories, which is a categorical way of viewing typed lambda calculi.
2
  • For the "much less readable code" bit, shouldn't that be mem x lst = any (\y -> y == x) lst ? (With a backslash).
    – stusmith
    Feb 7, 2013 at 10:02
  • Yes, thanks for pointing that out, I'll correct it.
    – Petr
    Feb 7, 2013 at 10:08
11

Another thing I haven't seen mentioned yet is that currying allows (limited) abstraction over arity.

Consider these functions that are part of Haskell's library

(.) :: (b -> c) -> (a -> b) -> a -> c
either :: (a -> c) -> (b -> c) -> Either a b -> c
flip :: (a -> b -> c) -> b -> a -> c
on :: (b -> b -> c) -> (a -> b) -> a -> a -> c

In each case the type variable c can be a function type so that these functions work on some prefix of their argument's parameter list. Without currying, you'd either need a special language feature to abstract over function arity or have many different versions of these functions specialized for different arities.

9

Currying is not just syntactic sugar!

Consider the type signatures of add1 (uncurried) and add2 (curried):

add1 : (int * int) -> int
add2 : int -> (int -> int)

(In both cases, the parentheses in the type signature are optional, but I've included them for clarity's sake.)

add1 is a function that takes a 2-tuple of int and int and returns an int. add2 is a function that takes an int and returns another function that in turn takes an int and returns an int.

The essential difference between the two becomes more visible when we specify function application explicitly. Let's define a function (not curried) that applies its first argument to its second argument:

apply(f, b) = f b

Now we can see the difference between add1 and add2 more clearly. add1 gets called with a 2-tuple:

apply(add1, (3, 5))

but add2 gets called with an int and then its return value is called with another int:

apply(apply(add2, 3), 5)

EDIT: The essential benefit of currying is that you get partial application for free. Lets say you wanted a function of type int -> int (say, to map it over a list) that added 5 to its parameter. You could write addFiveToParam x = x+5, or you could do the equivalent with an inline lambda, but you could also much more easily (especially in cases less trivial than this one) write add2 5!

2
  • 3
    I understand that there is a large differenc behind the scenes for my example, but the result seems to be a simple syntactic change. Feb 1, 2013 at 19:54
  • 5
    Currying isn't a very deep concept. It is about simplifying the underlying model (see Lambda Calculus) or in languages that have tuples anyway it is in fact about syntactic convenience of partial application. Don't underestimate the importance of syntactic convenience.
    – Peaker
    Feb 2, 2013 at 13:15
9

Currying is just syntactic sugar, but you're slightly misunderstanding what the sugar does, I think. Taking your example,

fun add x y = x + y

is actually syntactical sugar for

fun add x = fn y => x + y

That is, (add x) returns a function that takes an argument y, and adds x to y.

fun addTuple (x, y) = x + y

That is a function that takes a tuple and adds its elements. Those two functions are actually quite diffent; they take different arguments.

If you wanted to add 2 to all numbers in a list:

(* add 2 to all numbers using the uncurried function *)
map (fn x => addTuple (x, 2)) [1,2,3]
(* using the curried function *)
map (add 2) [1,2,3]

The result would be [3,4,5].

If you wanted to sum each tuple in a list, on the other hand, the addTuple function fits perfectly.

(* Sum each tuple using the uncurried function *)
map addTuple [(10,2), (10,3), (10,4)]    
(* sum each tuple using curried function *)
map (fn (a,b) => add a b) [(10,2), (10,3), (10,4)]

The result would be [12,13,14].

Curried functions are great where partial application is useful - for example map, fold, app, filter. Consider this function, that returns the biggest positive number in the list supplied, or 0 if there are no positive numbers:

- val highestPositive = foldr Int.max 0;   
val highestPositive = fn : int list -> int 
1
  • 1
    I did understand that the curried function has a different type signature, and that it is actually a function that returns another function. I was missing the partial application part though. Feb 2, 2013 at 15:01
7

My limited understanding is such:

1) Partial Function Application

Partial Function Application is the process of returning a function that takes a lesser number of arguments. If you provide 2 out of 3 arguments, it'll return a function that takes 3-2 = 1 argument. If you provide 1 out of 3 arguments, it'll return a function that takes 3-1 = 2 arguments. If you wanted, you could even partially apply 3 out of 3 arguments and it would return a function that takes no argument.

So given the following function:

f(x,y,z) = x + y + z;

When binding 1 to x and partially applying that to the above function f(x,y,z) you'd get:

f(1,y,z) = f'(y,z);

Where: f'(y,z) = 1 + y + z;

Now if you were to bind y to 2 and z to 3, and partially apply f'(y,z) you'd get:

f'(2,3) = f''();

Where: f''() = 1 + 2 + 3;

Now at any point, you can choose to evaluate f, f' or f''. So I can do:

print(f''()) // and it would return 6;

or

print(f'(1,1)) // and it would return 3;

2) Currying

Currying on the other hand is the process of splitting a function into a nested chain of one argument functions. You can never provide more than 1 argument, it's one or zero.

So given the same function:

f(x,y,z) = x + y + z;

If you curried it, you would get a chain of 3 functions:

f'(x) -> f''(y) -> f'''(z)

Where:

f'(x) = x + f''(y);

f''(y) = y + f'''(z);

f'''(z) = z;

Now if you call f'(x) with x = 1:

f'(1) = 1 + f''(y);

You are returned a new function:

g(y) = 1 + f''(y);

If you call g(y) with y = 2:

g(2) = 1 + 2 + f'''(z);

You are returned a new function:

h(z) = 1 + 2 + f'''(z);

Finally if you call h(z) with z = 3:

h(3) = 1 + 2 + 3;

You are returned 6.

3) Closure

Finally, Closure is the process of capturing a function and data together as a single unit. A function closure can take 0 to infinite number of arguments, but it's also aware of data not passed to it.

Again, given the same function:

f(x,y,z) = x + y + z;

You can instead write a closure:

f(x) = x + f'(y, z);

Where:

f'(y,z) = x + y + z;

f' is closed on x. Meaning that f' can read the value of x that's inside f.

So if you were to call f with x = 1:

f(1) = 1 + f'(y, z);

You'd get a closure:

closureOfF(y, z) =
                   var x = 1;
                   f'(y, z);

Now if you called closureOfF with y = 2 and z = 3:

closureOfF(2, 3) = 
                   var x = 1;
                   x + 2 + 3;

Which would return 6

Conclusion

Currying, partial application and closures are all somewhat similar in that they decompose a function into more parts.

Currying decomposes a function of multiple arguments into nested functions of single arguments that return functions of single arguments. There's no point in currying a function of one or less argument, since it doesn't make sense.

Partial application decomposes a function of multiple arguments into a function of lesser arguments whose now missing arguments were substituted for the supplied value.

Closure decomposes a function into a function and a dataset where variables inside the function that were not passed in can look inside the dataset to find a value to bind against when asked to evaluate.

What's confusing about all these is that they can kind of each be used to implement a subset of the others. So in essence, they're all a bit of an implementation detail. They all provide similar value in that you don't need to gather all values upfront and in that you can reuse part of the function, since you've decomposed it into discreet units.

Disclosure

I'm by no means an expert of the topic, I've only recently started learning about these, and so I provide my current understanding, but it could have mistakes which I invite you to point out, and I will correct as/if I discover any.

2
  • 1
    So the answer is: currying has not advantage?
    – ceving
    Dec 13, 2016 at 16:30
  • 1
    @ceving As far as I know, that's correct. In practice currying and partial application will give you the same benefits. The choice of which to implement in a language is made for implementation reasons, one might be easier to implement then another given a certain language.
    – Didier A.
    Dec 13, 2016 at 17:20
5

Currying (partial application) lets you create a new function out of an existing function by fixing some parameters. It is a special case of lexical closure where the anonymous function is just a trivial wrapper which passes some captured arguments to another function. We can also do this by using the general syntax for making lexical closures, but partial application provides a simplified syntactic sugar.

This is why Lisp programmers, when working in a functional style, sometimes use libraries for partial application.

Instead of (lambda (x) (+ 3 x)), which gives us a function that adds 3 to its argument, you can write something like (op + 3), and so to add 3 to every element of a some list would then be (mapcar (op + 3) some-list) rather than (mapcar (lambda (x) (+ 3 x)) some-list). This op macro will make you a function which takes some arguments x y z ... and invokes (+ a x y z ...).

In many purely functional languages, partial application is ingrained into the syntax so that there is no op operator. To trigger partial application, you simply call a function with fewer arguments than it requires. Instead of producing an "insufficient number of arguments" error, the result is a function of the remaining arguments.

1
  • "Currying ... lets you create a new function ... by fixing some parameters" - no, a function of type a -> b -> c doesn't have parameters (plural), it has only one parameter, c. When called, it returns a function of type a -> b.
    – Max Heiber
    May 26, 2017 at 3:48
4

For the function

fun add(x, y) = x + y

It is of the form f': 'a * 'b -> 'c

To evaluate one will do

add(3, 5)
val it = 8 : int

For the curried function

fun add x y = x + y

To evaluate one will do

add 3
val it = fn : int -> int

Where it is a partial computation, specifically (3 + y), which then one can complete the computation with

it 5
val it = 8 : int

add in the second case is of the form f: 'a -> 'b -> 'c

What currying is doing here is transforming a function that takes two agreements into one that only takes one returning a result. Partial evaluation

Why would one need this?

Say x on the RHS isn't just a regular int, but instead a complex computation that takes a while to complete, for augments, sake, two seconds.

x = twoSecondsComputation(z)

So the function now looks like

fun add (z:int) (y:int) : int =
    let
        val x = twoSecondsComputation(z)
    in
        x + y
    end;

Of type add : int * int -> int

Now we want to compute this function for a range of numbers, let's map it

val result1 = map (fn x => add (20, x)) [3, 5, 7];

For the above the result of twoSecondsComputation is evaluated every single time. This means it takes 6 seconds for this computation.

Using a combination of staging and currying one can avoid this.

fun add (z:int) : int -> int =
    let
        val x = twoSecondsComputation(z)
    in
        (fn y => x + y)
    end;

Of the curried form add : int -> int -> int

Now one can do,

val add' = add 20;
val result2 = map add' [3, 5, 7, 11, 13];

The twoSecondsComputation only needs to be evaluated once. To up the scale, replace two seconds with 15 minutes, or any hour, then have a map against 100 numbers.

Summary: Currying is great when using with other methods for higher level functions as a tool of partial evaluation. Its purpose cannot really be demonstrated by itself.

3

Currying allows flexible function composition.

I made up a function "curry". In this context, I don't care what kind of logger I get or where it comes from. I don't care what the action is or where it comes from. All I care about is processing my input.

var builder = curry(function(input, logger, action) {
     logger.log("Starting action");
     try {
         action(input);
         logger.log("Success!");
     }
     catch (err) {
         logger.logerror("Boo we failed..", err);
     }
});
var x = "My input.";
goGatherArgs(builder)(x); // Supplies action first, then logger somewhere.

The builder variable is a function that returns a function that returns a function that takes my input that does my work. This is a simple useful example and not an object in sight.

2

Currying is an advantage when you don't have all of the arguments for a function. If you happen to be fully evaluating the function, then there's no significant difference.

Currying lets you avoid mentioning not-yet-needed parameters. It is more concise, and doesn't require finding a parameter name that doesn't collide with another variable in scope (which is my favorite benefit).

For example, when using functions that take functions as arguments, you'll often find yourself in situations where you need functions like "add 3 to input" or "compare input to variable v". With currying, these functions are easily written: add 3 and (== v). Without currying, you have to use lambda expressions: x => add 3 x and x => x == v. The lambda expressions are twice as long, and have a small amount of busy work related to picking a name besides x if there's already an x in scope.

A side benefit of languages based on currying is that, when writing generic code for functions, you don't end up with hundreds of variants based on the number of parameters. For example, in C#, a 'curry' method would need variants for Func<R>, Func<A, R>, Func<A1, A2, R>, Func<A1, A2, A3, R>, and so forth forever. In Haskell, the equivalent of a Func<A1, A2, R> is more like a Func<Tuple<A1, A2>, R> or a Func<A1, Func<A2, R>> (and a Func<R> is more like a Func<Unit, R>), so all the variants correspond to the single Func<A, R> case.

2

The primary reasoning I can think of (and I'm not an expert on this subject by any means) begins to show its benefits as the functions move from trivial to non-trivial. In all trivial cases with most concepts of this nature you'll find no real benefit. However, most functional languages make heavy use of the stack in processing operations. Consider PostScript or Lisp as examples of this. By making use of currying, functions can be stacked more effectively and this benefit becomes apparent as the operations grow less and less trivial. In the curried manner, the command and arguments can be thrown on the stack in order and popped off as needed so they are run in the proper order.

3
  • 1
    How exactly does requiring a lot more stack frames to be created make things more efficient? Feb 1, 2013 at 19:55
  • 1
    @MasonWheeler: I wouldn't know, as I said I'm not an expert on functional languages or currying specifically. I labeled this community wiki specifically because of that. Feb 1, 2013 at 19:58
  • 4
    @MasonWheeler Your have a point w.r.t. the phrasing of this answer, but let me chip in and say that the amount of stack frames actually created depends a lot on the implementation. For example, in the spineless tagless G machine (STG; the way GHC implements Haskell) delays actual evaluation until it accumulates all (or at least as many as it knows to be required) arguments. I can't seem to recall whether this is done for all functions or only for constructors, but I think it ought to be possible for most functions. (Then again, the concept of "stack frames" doesn't really apply to the STG.)
    – user7043
    Feb 1, 2013 at 20:09
1

Currying depends crucially (definitively even) on the ability to return a function.

Consider this (contrived) pseudo code.

var f = (m, x, b) => ... return something ...

Let's stipulate that calling f with less than three arguments returns a function.

var g = f(0, 1); // this returns a function bound to 0 and 1 (m and x) that accepts one more argument (b).

var y = g(42); // invoke g with the missing third argument, using 0 and 1 for m and x

That you can partially apply arguments and get back a re-usable function (bound to those arguments that you did supply) is quite useful (and DRY).

Not the answer you're looking for? Browse other questions tagged or ask your own question.