A pesar de que pueda parecer lo contrario, la currificación no tiene sus orígenes en alguna especia de la India, sino en el cálculo lambda. Una de las consideraciones que dicho método formal define, es que cualquier función de 2 argumentos y que devuelve un valor, puede ser transformada a otra función que acepta solo un parámetro y que devuelve una función, que al ser llamada, devuelve el valor devuelto por la función original.
De hecho el concepto puede ser extendido, de forma que una función que acepta N parámetros puede ser currificada a una función que acepte N-1 parámetros y así sucesivamente, hasta obtener una función que acepte un solo parámetro. Al invocar esta función se obtendrá otra función, que al invocarla devolverá otra función y así repitiendo el proceso N veces obtendremos el mismo resultado que la función original.
Otra forma de definir la currificación consiste en decir que es un mecanismo para la invocación parcial de funciones. Al invocar una función le pasamos todos sus parámetros y obtenemos un valor. Al invocarla parcialmente, le pasamos solo parte de esos parámetros y obtenemos una función. A esa otra función le pasamos el resto de parámetros para terminar obteniendo el resultado final.
¿Por qué es esto importante? Pues porque la currificación es una parte fundamental de la programación funcional. Veamos un ejemplo en F#.
Empecemos por definir una función add que nos sume dos parámetros:
let add x y =
x + y
Por supuesto podemos invocar esta función y obtener el resultado:
let result = add 10 20
Nota: Recuerda que, si tienes Visual Studio 2015, puedes probar este código de forma muy sencilla. Abre la “Developer Command Prompt” de VS2015. Eso te abrirá una línea de comandos. Teclea “fsi” y entrarás en el REPL de F#. La única diferencia de sintaxis con un programa F# es que debes usar ;; (dos puntos y coma) para terminar las sentencias:
Ahora veamos cómo podemos definir una versión currificada de la función. Esta función debe aceptar un solo parámetro y devolver a su vez una función. Al llamar a esa otra función con otro parámetro debemos obtener el mismo resultado que llamando a add. Al ser F# un lenguaje funcional, eso es muy simple:
let curryAdd x =
let subAdd y =
x + y
subAdd
La función curryAdd es la versión currificada de la función add. Esa función toma un parámetro (x) y devuelve una función (subAdd). La función subAdd toma un parámetro (y) y devuelve el resultado de sumar el parámetro x al parámetro y:
let add10 = curryAdd 10
let result = add10 30;
Observa que add10 es una función. Esta función acepta un parámetro y sumará 10 al valor del parámetro pasado. Al final de este código el valor de result será 40.
En este caso hemos hecho una currificación manual de la función add. Es decir, hemos creado otra función que es la versión currificada. Pero lo interesante es poder currificar cualquier función sin tener que crear otra de forma manual. Si currificar significa invocar parcialmente una función, eso es lo que queremos hacer. Y F# convierte esto en trivial: ¿No acepta la función add dos parámetros (x e y)? ¿Y no es currificar invocar parcialmente la función, pasándole un solo parámetro? Pues F# te permite hacer exactamente esto:
let suma10 = add 10
Observa que estamos llamando a la función add (que espera dos parámetros) con tan solo un parámetro. Ante eso, algunos lenguajes como C# se quejarían y otros como JavaScript tomarían el segundo parámetro como undefined o cualquier otro valor. Pero es que ni C# ni JavaScript son lenguajes con alma funcional (a pesar de que ciertos aspectos funcionales puedan aplicarse a ellos). Pero F# sí que es un lenguaje con alma funcional. Y por eso, F# ni se queja ni asigna ningún valor por defecto al segundo parámetro. Si llamas a una función de dos parámetros y le pasas tan solo uno, F# hace lo que el cálculo lambda presupone: currifica la función.
Así el resultado, suma10 no es ningún número, sino que es una función. Es la función resultado de invocar parcialmente a la función add con el parámetro 10. Es, de hecho, una función que sumará 10 a su argumento. Del mismo modo que invocar a add pasándole como primer parámetro 10 y como segundo parámetro cualquier segundo número, devolverá el resultado de sumar 10 al segundo número. Así, ahora podemos hacer:
let r = suma10 2
Y en r tenemos 12, que es el resultado esperado.
Por supuesto F# permite invocar parcialmente funciones de más de un parámetro:
let add x y z =
x+y+z
Dada esa función podemos invocarla parcialmente con un solo argumento (lo que nos devuelve una función que espera dos argumentos más) o invocarla parcialmente con dos argumentos (lo que nos devuelve una función que espera un solo argumento).
Currificación en JavaScript
Vale, hemos visto el concepto de currificación y hemos visto cómo un lenguaje con alma funcional como F# nos da soporte directo para él. Veamos ahora qué ocurre en JavaScript.
JavaScript no es un lenguaje funcional propiamente dicho. Pero, a semejanza de los lenguajes funcionales, las funciones son un ciudadano de primer orden y podemos hacer cosas interesantes con ellas. Cierto es que JavaScript no tiene soporte directo para currificación, pero eso no quita que no podamos simularla.
El objetivo es crear una función (vamos a llamarla curry) que nos devuelva la versión currificada de cualquier función. Luego la agregamos a Function.prototype y podremos hacer algo como add.curry(10), para invocar parcialmente la función add con un parámetro 10.
Realmente el código tampoco es tan complejo (aunque requiere entender los métodos apply y bind):
Function.prototype.curry = function() {
var parameters = Array.prototype.slice.call(arguments, 0);
return function() {
return this.apply(this, parameters.concat(
Array.prototype.slice.call(arguments, 0)
));
}.bind(this);
};
Ahora, armados con la función curry ya podemos currificar cualquier función que tengamos:
var add = (x,y,z) => x + y + z;
var add_10 = add.curry(10);
var result = add_10(30, 30); // 70
var add_10_1 = add.curry(10,1);
var result2 = add_10_1(30); // 41
Observa como la función curry nos permite invocar parcialmente una función al igual que hacíamos con F# (aunque sin duda la sintaxis no sea tan directa).
Espero que este post te haya ayudado a entender el concepto de currificación. Como hemos dicho se usa mucho en programación funcional y es un concepto realmente potente que deberías conocer.