What is it and what are the advantages of Currying?

19

This is a very widespread concept in functional languages, but what exactly is it?

Why is it advantageous?

Bonus point : Is it possible to use it in non-functional languages? Or more generically, in languages that do not have specific facilities? Can you give an example? And would the same advantages apply?

    
asked by anonymous 12.06.2014 / 18:59

2 answers

14

Currying and partial application

It is extremely common to find definitions and examples of currying that actually refer to partial function application , or partial application of functions. For those who, like me, are interested in functional programming without actually mastering any purely functional language, the two concepts are usually even more confusing. I will try to explain the difference as I use it to consolidate my own understanding of the subject. All my examples use JavaScript, but I imagine they are understandable for those who use C, Java, C #, among other languages.

Partial application

In functional languages, functions are not "invoked," but "applied" to their arguments. Functions are also known as "first class citizens" of the language, that is, they can be assigned to variables, and interact with other functions that can be passed as parameters or returned.

A function is "partially applied" when it is applied to only part of the parameters it expects, and returns another function that expects the remaining parameters. So partial application is a way to get a function and fix (or, as said Zuul , "stuck") certain parameters .

For example, the following JavaScript function expects three parameters:

function soma(a, b, c) {
    return a + b + c;
}

// Duas maneiras diferentes de invocar: 
soma(1, 2, 3);               // 6
soma.apply(null, [1, 2, 3]); // 6

Let's create a function that applies soma partially, ie fixes the first parameter and returns a function that expects the other two:

function somap(a) {
    return function(b, c) {
        return soma(a, b, c)
    }
}

// Uso:
var somadois = somap(2);
somadois(3, 4); // 9

This operation can be generalized to any function:

function partial(fn) {
    // Guarda argumentos após fn numa array
    var args = Array.prototype.slice.call(arguments, 1);
    return function() {
        return fn.apply(null, args.concat(Array.prototype.slice.call(arguments, 0)));
    };
}

var somadois = partial(soma, 2);
somadois(3, 4); // 9

In fact, JavaScript already supports this natively with bind that the functions have:

var somadois = soma.bind(null, 2);

Currying

The definition of currying from the Zuul's answer is accurate and succinct (emphasis added):

  

Currying is the name given to the technique of dividing a function that takes several arguments in a series of functions each dealing with an argument of the initial function.

Notice that our example above does not meet this definition, because partial(soma, 2) returns a function that handles two arguments of soma , not one. If our function did currying, we would have to call it like this to get the final result:

var somadois = curry(soma, 2);
somadois(3)(4); // 9

The practical use of this in JavaScript (among other languages) is debatable. There are examples of justified uses, but in most cases there is not much advantage. Implementations of currying functions are possible in JavaScript, but not without some limitations.

Currying is very useful in languages where functions are always unary, that is, they accept a single argument. In them, currying is just a way to implement functions that accept more than one argument. And certain languages implement functions with multiple parameters using currying internally . From this link, the following examples in OCaml are equivalent:

let foo = fun a b ->
  a * a + b * b

and

let foo = fun a ->
  fun b ->
    a * a + b * b

In OCaml, the first way to define the function is only syntactic sugar for the second. Both produce identical function objects. And in both cases the call is equal, for example foo 1 2 - that would be foo(1)(2) in JavaScript.

I found in Programmers an interesting point of view on this (in free translation):

  

For me currying is important as a theoretical construct that allows us to consider all functions as unary, since any other function can be reduced to such a function.

    
13.06.2014 / 07:04
13

Currying (English) is the name given to the technique of dividing a function that receives multiple arguments in a series of functions each dealing with an argument of the initial function.

  

From mathematical origins, created by Moses Schönfinkel and later perfected by Haskell Curry .

After applying the Currying technique a function accepts the first argument of the original function returning a function that accepts the second argument of the original function and so on until all the arguments are being worked by individual functions .

In practical terms, the advantage of Currying is that we have, especially in large projects, functions that are called multiple times with one or more parameters of equal value between them. The Currying technique avoids code repetition and / or values in the parameters.

Practical Example

View in JSFiddle

The JavaScript framework Prototype (English) has an implementation of Curry (English) that allows you to "spike" parameters in the original function and subsequently pass a number infinite number of additional parameters.

The practical example they present in the documentation demonstrates well the use and benefit of Currying :

// Função de exemplo para fazer "alert" do argumentos
function showArguments() {
  alert($A(arguments).join(', '));
}

// Vai gerar um "alert" de três argumentos "1, 2, 3"
showArguments(1, 2, 3);

/* "cravar" três argumentos em f e passar mais dois, resultando numa função
 * com 5 argumentos 3 dos quais comuns a outras funções
 * alerta "1, 2, 3, a, b"
 */
var f = showArguments.curry(1, 2, 3);
f('a', 'b');

Having f with 3 Curry arguments, we can call it again without having to repeat them, thus saving said code repetition and arguments:

// alerta "1, 2, 3, c, d"
f('c', 'd');

Learn more

You can read more about this subject not only in the links already provided but also in the answers given to these questions in SOEN:

12.06.2014 / 23:16