lunes, marzo 24, 2008

El mundo de los Closures

La ejecución de un método manipulando variables dependientes de otro método o contexto, se denomina “CLOSURE”, o simplemente podemos decir que es una función definida dentro de otra función donde las variables son ligadas en el momento de ejecución, para ser un poco mas puristas diríamos que podemos enviar como parámetro de un mensaje, un bloque de código capaz de ser ejecutado en este nuevo contexto.
Este concepto nació al rededor de los años 60 con el lenguaje SCHEME y es aprovechado por muchos otros lenguajes como Smalltalk, Ruby, ML, Lisp, Haskell, etc. y si todo sale bien pronto tendremos soporte nativo en Java.
Vamos a ver unos ejemplos prácticos para entender un poco mas el concepto, los ejemplos los voy a escribir en Javascript para que puedan probarlos rápidamente sobre el navegador y luego les mostrare una forma de implementar CLOSURES en java 5.

El siguiente CLOSURE es muy simple pero es para que vallan viendo el manejo de contexto, ademas observemos como se accede a las variables.

function addOne(aNumber)
{
var result = 1 + parseInt(aNumber);
var viewResult = function() { alert(result); }
viewResult();
}

En el ejemplo anterior vemos que pudimos guardar comportamiento dentro de una variable, es decir dentro de la variable viewResult nos guardamos la función que nos muestra el resultado en pantalla y los imprimimos.
No es un CLOSURE muy útil pero la idea del mismo es que comprendamos la utilización de una variable para poder guardar una función y ejecutarla después.
Ahora lo que aremos es dar la posibilidad de que nuestra funcion de visualizacion del resultado sea utilizada en otro momento, para eso cambiamos un poco la funcion anterior.

function addOne(aNumber)
{
var result = 1 + parseInt(aNumber);
var viewResult = function() { alert(result); }
return viewResult;
}

Ahora en vez de ejecutar la función la estoy devolviendo, con esto puedo transportar mi CLOSURE donde quiera y ejecutarlo cuando sea conveniente.
Algunos puntos a tener en cuenta es la forma en la que se realiza la ligadura de variables (binding) miremos este ejemplo para observar que pasa dentro de nuestro CLOSURE.

function addOne(aNumber)
{
var result = 1 + parseInt(aNumber);
var viewResult = function() { alert(result); }
result++;
return viewResult;
}

Bien ahora probemos la nueva función

var myClosure = addOne(4);
myClosure();

Cual sera el numero que arroje? 6 correcto!! Eso es porque el CLOSURE tiene como colaborador interno a la variable result y cualquier modificación sobre el se vera reflejada en nuestro CLOSURE.
Ahora veamos una aplicación un poco mas real (eso espero) para poder utilizar la potencia de esta característica.
Pensemos en que debemos actualizar precios de monedas con respecto al precio del dólar, claramente se que en sus cabezas esta el patrón “Observer”, pero dejemos de pensar en el un rato y veamos la aplicación de los CLOSURES en el problema. Como el ejemplo lo vamos a codificar en Javascript definiremos los CLOSURES pertinentes para actualizar el precio del dólar y las otras monedas, ademas de la lista de monedas.

//Funcion que actualiza el precio de una moneda con respecto al dólar.
function _updateWith(aDolarPrice)
{
this.value = aDolarPrice * this.ratio;
}
//definimos la lista de monedas
var moneyList=[{value:1,ratio:1.1,updateWith:_updateWith},
{value:1,ratio:1.2,updateWith:_updateWith},
{value:1,ratio:1.3,updateWith:_updateWith}];


function setup() {
// Inicializo el valor del dolar
var dolarPrice = 430;
// Guardo la referencia dentro de los CLOSURES
printDolarPrice = function() { alert(dolarPrice); }
setDolarPrice = function(x) { dolarPrice = parseInt(x); }
refreshAMoney = funtion(aMoney) { aMoney.updateWith(dolarPrice);return aMoney;}
}

Bien ahora con estos datos debemos tener una manera de poder recorrer toda la lista de monedas y actualizarlas una a una, para esto construiremos una función llamada map (los amantes de funcional lloran de la emoción...).

function map(list, _function)
{
var result;
for (var i = 0; i < list.length; i++) {
var item = list[i];
result = _function(item);
list[i] = result;
}
return list;
}

Ahora estamos listos para utilizar nuestros CLOSURES y actualizar la lista de monedas!

//inicializamos todo;
setup();
//luego actualizamos el precio del dólar en todas las monedas.
map(moneyList,refreshAMoney);
//modificamos el precio del dólar
setDolarPrice(700);
//y ahora actualizamos el precio de las monedas...
map(moneyList,refreshAMoney);

Se ve con claridad la potencia de disponer de CLOSURES para poder transportar bloques de código capaces de encapsular comportamiento especifico a través de todo nuestro código, lamentablemente hoy en java no disponemos de esta capacidad tan natural como lo es en Javascript.
Sin bien dentro de Java podemos emular a los CLOSURES por medio de generics, se esta discutiendo la posibilidad de incluir en la nueva versión del lenguaje soporte nativo a estos. Las alternativas de CLOSURES actuales son tres BGGA, CICE, FCM sus diferencias varían desde como se escribirán hasta su compilación, tipado, etc. pero como nosotros somos muy inquietos les voy a pasar una forma de implementar CLOSURES con generics y no tener que esperar hasta la versión 7!!!

Lo primero que implementaremos sera una función, por medio de una clase abstracta modelamos el método de aplicación.

// Haskelianos (a->a)
public abstract class Function <A,B>
{

public abstract B apply (A x);

}


Ahora debemos crear una función concreta, nosotros crearemos la función addOne.
public class AddOne extends Function<Integer,Integer> {

public Integer apply(Inetger x) { return x + 1; }
//funciona gracias al outboxing e inboxing.
}


Ahora implementemos el map

<A,B> Iterator<B> map(Function<A,B> f, Iterator<A> xs) {

LinkedList<B> l = new LinkedList<B>();

while (xs.hasNext())

l.add(f.apply(xs.next()));

return l.iterator();

}


Como se observa podemos crear CLOSURES en java con generics (de la versión 5 en delante) , pero no es la unica manera. Si nosotros estamos usando una version de Java menos a la 5 podemos crear CLOSURES por medio de clases anonimas, de todas maneras igual es tediosa la escritura, veamos un ejemplo.

public interface UnaryFunction
{
Object apply(Object o);
}

Ahora creemos una función unaria concreta.

UnaryFunction addOne = new UnaryFunction() { public Object apply( Object o)
{
return ((Integer)o) + 1
}}

Como conclusión los CLOSURES son muy utilices para el programador reduciendo (no en caso de java) el código y ayudando a la reutilización, esperemos que dentro de poco podamos tener una forma nativa de escritura de CLOSURES en java asi podremos disfrutar de esta característica mas sanamente.

Tomas Vera