Menú de navegaciónMenú
Categorías

La mejor forma de Aprender Programación online y en español www.campusmvp.es

Qué es la "Currificación" en programación funcional

CurryficacionA 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:

image

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.

Eduard Tomás Eduard es ingeniero informático, atesora muchos años de experiencia como desarrollador y ha sido galardonado como MVP por Microsoft en diez ocasiones. Está especializado en el desarrollo de aplicaciones web y móviles. Mantiene un conocido blog sobre desarrollo en .NET en general y ASP.NET MVC en particular. Colabora con la comunidad impartiendo charlas en formato webcast y en eventos de distintos grupos de usuarios. Puedes seguirlo en Twitter en @eiximenis. Ver todos los posts de Eduard Tomás
Archivado en: Lenguajes y plataformas

Boletín campusMVP.es

Solo cosas útiles. Una vez al mes.

🚀 Únete a miles de desarrolladores

DATE DE ALTA

x No me interesa | x Ya soy suscriptor

La mejor formación online para desarrolladores como tú

Comentarios (2) -

Modesto San Juan
Modesto San Juan

Perdona el atrevimiento porque soy bastante novato en estos temas y tal vez estoy patinando.

Tal y como yo había entendido hasta ahora, la currificación toma una función con N parámetros y transformarla en N funciones con un parámetro.

Por otro lado, la aplicación parcial de funciones toma una función con N parámetros y la transforma en una función con N-1 parámetros.

Currificación y aplicación parcial están relacionados, pero creo que el mecanismo concreto que describes en este post no es la currificación, es la aplicación parcial de funciones.

Responder

Eduard Tompàs
Eduard Tompàs

De atrevimiento nada... ;-)
Sí, estríctamente hablando tienes razón. Simplemente no se ha introducido la distinción en el artículo.

Pero sí, la currificación consiste en obtener N funciones a partir de una función de aridad N y la invocación parcial es obtener funciones de aridad M a partir de una de aridad N donde (M<N). En cierto modo la currificación es la invocación parcial "extrema", y realmente, si dispones de uno de los dos mecanismos, no necesitas el otro.

Aquí se explica la diferencia: stackoverflow.com/.../what-is-the-difference-between-currying-and-partial-application

Gracias!

Responder

Agregar comentario

Los datos anteriores se utilizarán exclusivamente para permitirte hacer el comentario y, si lo seleccionas, notificarte de nuevos comentarios en este artículo, pero no se procesarán ni se utilizarán para ningún otro propósito. Lee nuestra política de privacidad.