jueves, diciembre 21, 2006

Métodos anónimos

Voy a ocuparme un poco del compilador de Freya, ahora que baja la carga de trabajo por fin de año. Quiero terminar de implementar el soporte de métodos genéricos, introducir la inferencia de tipos para argumentos genéricos en llamadas a métodos genéricos, y probablemente de los primeros pasos para implementar métodos anónimos.
Con estos últimos, voy a saltarme una versión, y los voy a implementar desde el principio como las expresiones lambda de C# 3.0:
var L: List[Integer] := [1, 2, 3, 4];
if L.Exists(x => x mod 2 = 0) then
Console.WriteLine('Pares en la lista');
La sintaxis del ejemplo (la porción resaltada en rojo) es la del caso más simple: un método anónimo que devuelve un valor calculado con una expresión, que recibe un solo parámetro, y que el tipo de dicho es parámetro es inferible a partir del contexto.
El problema consiste en qué hacer en el caso más general: en C#, gracias a su sintaxis espartana, la versión completa de un método anónimo es aceptable. En un lenguaje pascaloide (o "algoloide"), es otra cosa. Esto sería una expresión en C#:
delegate(int x) { return x % 2 == 0; }
Y esto sería el equivalente literal en Freya:
method(x: integer) begin result := x mod 2 = 0 end
Si no ve dónde está el problema, recuerde que estas "expresiones" pueden usarse ahora como parámetros en llamadas a métodos.
En realidad, hay otro problema por el que le estoy dando tantas vueltas a la sintaxis. Me gustaría poder plasmar la idea de método anónimo = extensión de estructuras de control. Las listas genéricas en .NET tienen un método ForEach, un iterador cerrado, que acepta un parámetro de tipo delegado:
lista.ForEach(x => Console.WriteLine(x));
En este caso, C# ha considerado que la llamada a WriteLine se puede considerar, en el sentido más laxo, una función que no devuelve nada. Por lo tanto, permite eliminar las llaves que de otro modo deberían encerrar el bloque de instrucciones.
A mí me gustaría que Freya pudiera hacer esto (olvide por un momento el uso incorrecto de las palabras reservadas):
with x apply lista.ForEach do Console.WriteLine(x);
Esto es: me gustaría disponer de una instrucción de control "comodín" que pudiese simplificar el uso de métodos anónimos como parámetros de métodos... siempre que se cumpliesen algunas condiciones, como que el método sólo admitiese un parámetro de tipo delegado. Claro: esto no tendría sentido con una llamada al método Exists que mostré en el primer ejemplo, pues este método debe devolver un valor.
¿Tendría sentido una instrucción como la que imagino? ¿Tendría aplicación más allá de su evidente utilidad con iteradores cerrados? Teniendo en cuenta que ya Freya cuenta con un mecanismo útil y eficiente de iteradores abiertos, si sólo valiese para iteradores cerrados, no creo que mereciese la pena: es incluso probable que fuese mejor adaptar la sintaxis del foreach de C# para estos casos. Por último, ¿podría utilizarse una instrucción de este tipo para simplificar la conexión de manejadores anónimos a eventos? Esto es C# 2.0:
button1.Click += delegate { Console.WriteLine("Click"); };
¿No sería preferible algo así en Freya?
when button1.Click do Console.WriteLine('Click');
¡Ojo! No estoy convencido de que sea buena idea: sólo pregunto. No me gustaría añadir dos instrucciones diferentes para estos dos casos... pero tampoco estoy seguro de poder unificarlos.

Etiquetas: