RSS Feed

Curry

5

marzo 6, 2013 by - @pelicanorojo

Intro

Para contarles que es Curry me voy a basar en el libro   “JavaScript Patterns – Stoyan Stefanov“. ( Lo pueden comprar en su librería de confianza ;) )

En este link tienen una def un poco más genérica, desatada del mundo JS.

“Curry es una técnica de transformación de funciones que permite obtener a partir de una función A otra B que es la versión parcialmente aplicada de A.” Después de la teoría vienen un par de formas de hacerlo con JavaScript.

Que es una aplicación y una aplicación parcial?

Podemos tomar con fines prácticos hablando en un contexto menos matemático y mas JS que una aplicación es como una llamada a función y que aplicar parcialmente una función es llamarla con no todos los argumentos esperados sino con algunos de los primeros. Como de esta forma no podemos obtener el resultado final que obtendríamos de pasarle todos los parámetros, lo que se obtiene es una función que se puede llamar con los parámetros restantes y devolver el resultado como si hubiese sido llamada con todos los parámetros.

Implementaciones

Curry manual:

function sumar( x, y ) {
    if ( typeof y === 'undefined' ) { //detección de aplicación parcial
        return function ( y ) { //la transformación ( curring )
            return x + y;
        };
    } else {
        return x + y;//aplicación completa
    }
}

var sumaParcial = sumar( 4 );// Obtener suma parcialmente aplicada, memoriza x

var result = sumaParcial( 5 ); // uso de la versión parcialmente aplicada, el 5 se usa para y que al sumarle la x memorizada ( 4 ) dará como resultado 9. 

//También lo podemos usar así:
var result2 = sumar( 4 ) ( 5 );

En esta implementación del curry, la aplicación parcial está en la misma función sumar, si vamos a usar mucho esta técnica, se puede hacer una forma más versátil que pueda aplicar curry a cualquier función que querramos en el libro aparece el ejemplo, la función schonfinkelize de 7 lineas que le pone curry a lo que se le cruce:

Curry pro

function schonfinkelize(fn) {
   var slice = Array.prototype.slice,
     stored_args = slice.call(arguments, 1);
   return function () {
      var new_args = slice.call(arguments),
         args = stored_args.concat(new_args);
      return fn.apply(null, args);
   };
}

//Ejemplo de uso:
// Función a modificar:
function echo( x ) {
  return x;
}

// Función modificada
var elephantFunction = schonfinkelize( echo, 'algo para recordar' );//La Transformación ( curring )

// Aplicación, en un objeto mock, o un objeto que necesita tener un método que devuelva siempre el mismo valor con fines de polimorfimo
var mock = {
   getMessage: elephantFunction
}

Mas info sobre apply en Patrones de Invocación de Funciones: this y en Modificando el contexto: call, apply y bind

Implementación de Underscore.js

Underscore viene con la función partial que permite hacer las partial applications, el ejemplo anterior, hecho con esta funcionalidad de underscore sería :

var mock = {
   getMessage: _.partial( echo, 'algo para recordar' ) //La transformación ( curring )
}

Ejemplos de Uso:

Imagínense que tenemos una función densidad de tres variables de entrada ( x, y , z ) que nos devuelve la concentración de algo ( un compuesto por ejemplo ) en un cuerpo dado. Densidad podría tener información de una tomografía computada o de un examen de resonancia magnética nuclear, x podría representar la dimensión longitudinal de pies a cabeza, y con _.partial( densidad, 3 ), podríamos obtener un corte ( una imagen ) de esa información a 3 centímetros de la planta de los pies.

Ok, que pasa si se nos fué la mano con el fernet y queremos cortes que no sean perpendiculares a ninguno de los ejes xyz? Rta, tendríamos que aplicar un poco (bastante) de matemática, como para obtener una función similar partial, pero que se llame por ejemplo slice que reciba como entrada un vector con un punto por donde debe pasar el corte y otro perpendicular al plano de corte, ejemplo:  corte = _.slice( [3, 0, 0], [0, 0, 1], densidad ) // el corte pasa por x = 3 y el vector normal es el que tiene solo componente z.   La función devuelta tendría dos variables de entrada, que serían coordenadas x y y en el plano de corte ( que no necesariamente son las mismas que las originales).

Bueno, espero que les haya gustado.


  • matiasarriola

    Muy groso, esto me voló la cabeza cuando aprendí algo de haskell :{D
    No conocía el partial de underscore, viene al pelo.

  • tito hernandez

    Excelente entrada. De lo mejor que se ha publicado ultimamente en todos los blogs que leo ;)

  • pjnovas

    Copado post!, agrego un buen debate sobre este post en Twitter https://twitter.com/fernetjs/status/309285415536431104

  • http://twitter.com/AlejoNext Alejandro Ñext

    Gracias por ese post!!

  • Pingback: quest protein bars