Está en la página 1de 19

Programaci on funcional para el resto de nosotros

Slava Akhmechet 29 de agosto de 2010


Resumen Un excelente punto de partida para comprender mejor la programaci on funcional. Original de Slava Akhmechet y traducido al espa nol por Luis Mendoza con el permiso del autor. En la direcci on electr onica defmacro.org/ramblings/fp.html puedes consultar el original.

1.

Introducci on

Los programadores son procrastinadores (o sea, personas que aplazan las cosas). Llegan, toman un poco de caf e, revisan su bandeja de entrada, leen sus actualizaciones de RSS, leen las noticias, dan un vistazo a los art culos m as recientes en los sitios de tecnolog a, examinan las discusiones pol ticas en los secciones designadas de los foros de programaci on. . . se restriegan los ojos y echan otro vistazo para asegurarse de no perderse nada. Van a comer. Regresan, inician el IDE por unos minutos. Revisan la bandeja de entrada. Toman un poco de caf e. Antes de darse cuenta, el d a de trabajo ya termin o. Pero, de vez en cuando, te encuentras con art culos verdaderamente desaantes. Si buscas en el lugar correcto encontrar as al menos uno cada pocos d as. Como son dif ciles de entender y necesitas tiempo para leerlos, empiezan a acumularse. Antes de darte cuenta, tienes una larga lista de v nculos y una carpeta llena de archivos PDF y quisieras tener un a no en una peque na caba na a la mitad del bosque sin nadie a kil ometros a la redonda para que nalmente puedas comprenderlos. Estar a bien que alguien viniera cada ma nana mientras das un paseo por el r o para dejarte algo de comida y llevarse la basura. No s e de tu lista, pero una buena parte de la m a tiene que ver con programaci on funcional. Estos son generalmente los art culos m as dif ciles de entender. Escritos en un aspero lenguaje acad emico, aun los veteranos de la industria de Wall Street con diez a nos de experiencia no entienden de qu e tratan los art culos sobre programaci on funcional (tambi en llamada FP, por sus siglas en ingl es). Si preguntas al administrador de proyectos de Citi Group o de Deutsche Bank por qu e usan JMS en lugar de Erlang te dir an que no pueden usar lenguajes acad emicos para aplicaciones de fortaleza industrial 1 . El problema es que algunos de los sistemas m as complejos con los requerimientos m as r gidos est an escritos usando elementos de programaci on funcional. Algo no cuadra. Es cierto que los art culos sobre FP (recuerda que son las siglas en ingl es para programaci on funcional) son dif ciles de entender, pero no tendr an por qu e serlo. Las razones para que haya ese obst aculo al conocimiento son meramente hist oricas. No hay nada inherentemente dif cil en los conceptos de FP. Considera este art culo como una gu a accesible para entener FP, un puente entre nuestras mentes imperativas hacia el mundo de FP. Prep arate un caf e y sigue leyendo. Con un poco de suerte, en poco tus amigos estar an bromeando sobre tus nuevas ideas sobre FP. As que, qu e es la programaci on funcional o FP? C omo se produce? Es comestible? Si es tan u til como proclaman sus defensores, por qu e no es m as usada en la industria? Por qu e parece
1 Cuando buscaba trabajo en el oto no de 2005 a menudo hice esa pregunta. Es bastante divertido recordar cuantos an rostros con signos de interrogaci on vi. Uno podr a pensar que a 300,000 la pieza de estas personas, al menos tendr un buen entendimiento de las herramientas que tienen disponibles.

que solo personas con grado de doctorado lo quieren usar? M as importante, por qu e es tan dif cil de aprender? Qu e es todo eso de cierres(closures), continuaciones, currying, evaluaci on tard a y cero efectos colaterales? C omo puede usarse en proyectos que no tengan que ver son universidades? Por qu e parece tan distinto de todo lo bueno, santo y querido por nuestros corazones imperativos? Aclararemos todo esto pronto. Empecemos explicando las razones por las que existe esa gran barrera entre el mundo real y los art culos acad emicos. La respuesta es tan sencilla como dar un paseo por el parque.

2.

Un paseo por el parque

Enciende la m aquina del tiempo! Nuestro paseo empieza unos dos mil a nos atr as, en una bello y soleado d a de la primavera de 380 a.C. A las afueras de las murallas de Atenas, bajo la pl acida sombra de los olivos, Plat on caminaba rumbo a la Academia con un joven pupilo. El clima estaba agradable, la cena hab a estado deliciosa, y la conversaci on empez o a tomar tintes los ocos. Mira a esos dos estudiantes, dijo Plat on escogiendo cuidadosamente las palabras para hacer una pregunta educativa. Cual de ellos te parece m as alto? El joven pupilo mir o hacia la cuenca de agua en la que dos hombres estaban parados. Son m as o menos de la misma altura, contest o. Plat on pregunt o: Qu e quieres decir con m as o menos ?. El joven contesto: Bueno, desde aqu se ven de la misma estatura, pero si estuviera m as cerca seguramente ver a alguna diferencia. Plat on sonri o. Hab a llevado al joven en la direcci on correcta. As que dir as que no hay ninguna cosa perfectamente igual a otra en nuestro mundo? Despu es de pensar un poco, el joven respondi o: No lo creo. Toda cosa es al menos un poco diferente de otra, aunque no podamos ver la diferencia. Hab a dado en el clavo! Plat on dijo: Entonces, si ninguna cosa es perfectamente igual a otra en este mundo, c omo crees que entendemos el concepto de equidad perfecta ? El joven se qued o perplejo. No lo s e, contest o. As nacio el primer esfuerzo por entender la naturaleza de las matem aticas. Plat on sugiri o que todo en nuestro mundo es solo una aproximaci on de la perfecci on. Adem as, se dio cuenta de que entendemos el concepto de perfecci on aunque no la hayamos visto. Lleg o a la conclusi on de que las formas matem aticas perfectas viven en otro mundo y que de alguna forma sabemos de ellas al tener una conexi on con ese universo alternativo. Sabemos bien que no hay un c rculo perfecto que podamos observar. Pero entendemos qu e es un c rculo perfecto y podemos describirlo mediante ecuaciones. Qu e son, entonces, las matem aticas? Por qu e esta descrito nuestro universo con leyes matem aticas? Ser a posible que todos los fen omenos del universo sean descritos mediante las matem aticas?2 La losof a de las matem aticas es una materia de estudio muy compleja. Como muchas disciplinas los ocas genera m as preguntas que respuestas. Buena parte de los concensos alcanzados giran en torno a que las matem aticas son realmente un rompecabezas: podemos declaramos un conjunto b asico de principios que no entran en conicto, y un conjunto de reglas sobre c omo operan estos principios. Entonces podemos usar estas reglas como base para reglas m as y m as complejas. Los matem aticos le llaman a esto un sistema formal o c alculo. Podr amos construir un sistema formal del Tetris si quisieramos. De hecho, una implementaci on del Tetris que funcione es un sistema formal, solo que especicado usando una representaci on inusual. Una civilizaci on de criaturas peludas que existiera en Alfa Centauri no podr a leer nuestros formalismos del Tetris y de los c rculos porque su u nico organo sensorial solo percibiera olores. Lo m as probable es que nunca sabr an nada del Tetris, pero s tendr an un formalismo para los c rculos. Probablemente nosotros no podr amos entenderlo, pues nuestro sentido del olfato no ser a tan sosticado, pero una vez que dejamos de lado la representaci on del formalismo (a trav es de
2 Esta parece ser una cuesti on muy controversial. Los f sicos y los matem aticos se ven obligados a reconocer que no esta del todo claro si todo en el universo obedece leyes matem aticas o no.

diversos instrumentos sensoriales y t ecnicas de lectura para entender el lenguaje ), los conceptos fundamentales son entendibles para cualquier civilizaci on inteligente. Es interesante notar que aunque no existiera una civilizaci on inteligente en el universo, los formalismos del Tetris y de los c rculos estar an ah para ser examinados, solo que nadie los estar a buscando. Si una civilizaci on inteligente apareciese, es muy probable que descubrir a algunos formalismos para ayudarle a describir las leyes de nuestro universo. Ser a poco probable que alguna vez encontraran algo sobre el Tetris porque nada en el universo observable se le parece. El Tetris es uno de los incontables ejemplos de sistemas formales, o rompecabezas, que no tienen nada que ver con el mundo real. De hecho, no podemos estar seguros de que todos los n umeros naturales tengan referencia en el mundo real, ya que podemos pensar en un n umero tan grande que no pueda describir nada en un universo donde las cosas tienden a ser nitas.

3.

Un poco de historia3

Cambiemos de velocidad en la m aquina del tiempo. Esta vez viajaremos a un punto m as cercano, a la decada de 1930. La Gran Depresi on estaba asolando a todo el mundo. Casi toda familia de toda clase social se vi o afectada por la tremenda recesi on econ omica. Muy pocos santuarios quedaron donde la gente estaba a salvo de la pobreza. Pocas personas tuvieron la fortuna de estar en alguno de esos santuarios, pero exist an. Nuestro inter es se centrar a en los matem aticos de la Universidad de Princeton. Las nuevas ocinas construidas en estilo g otico daban a Princeton un aire de para so. L ogicos (de la rama matem atica de la l ogica) de todo el mundo fueron invitados a Princeton para construir un nuevo departamento. Mientras muchos en norteam erica no pod an ni poner una pieza de pan en sus mesas para la cena, techos altos, paredes cubiertas de madera tallada, discusiones diarias con una taza de t e, y paseos por el bosque eran algunas de las condiciones de Princeton. Uno de los matem aticos viviendo ese lujoso estilo de vida fue un joven llamado Alonzo Church. Alonzo recibi o un grado acad emico en Princeton, y fue persuadido a quedarse para un postgrado. Alonzo sent a que la arquitectura del lugar era m as un lujo que algo necesario. Rara vez se le ve a discutiendo sobre matem aticas con una taza de t e, y no le gustaban los paseos por el bosque. Alonzo era solitario. Era m as productivo cuando trabajaba por su cuenta. No obstante, Alonzo ten a contacto regular con otros habitantes de Princeton, entre ellos Alan Turing, John von Neumann, y Kurt G odel. Los cuatro estaban interesados en los sistemas formales. No prestaban mucha atenci on al mundo f sico; les resultaban m as interesantes problemas con rompecabezas matem aticos abstractos. Sus rompecabezas ten an algo en com un: los cuatro estaban interesados en responder preguntas sobre computaci on. Si tuvieramos m aquinas con innito poder computacional, Que problemas podr amos resolver? Podr an darse soluciones autom aticamente? Quedar an algunos problemas sin resolver? Por qu e? Podr an maquinas con diferentes dise nos ser iguales en poder? En cooperaci on con otros, Alonzo desarroll o un sistema formal llamado c alculo lambda. El sistema era esencialmente un lenguaje de programaci on para una de esas m aquinas imaginarias. Se basaba en funciones que tomaban otras funciones como par ametros, y que devolv an funciones como resultado. La funci on en su papel de materia prima fue identicada con la letra griega lambda(), de ah el nombre del sistema 4 . Usando este formalismo, Alonzo pudo razonar sobre muchas de las
3 Siempre he odiado las lecciones de historia que presentan cronol ogicamente fechas, nombres y eventos de forma mec anica. Para m , la historia es sobre las vidas de las personas que cambiaron el mundo. Es sobre sus razones personales que les llevaron a actuar de cierta forma, y los mecanismos por medio de los cuales cambiaron millones de vidas. Por esta raz on, esta secci on hist orica es irremediablemente incompleta. Solo las personas y los eventos importantes son discutidos. 4 Cuando estaba aprendiendo programaci on funcional me qued e muy confundido por el t ermino lamda porque no sab a qu e signicaba realmente. En este contexto lambda es una funci on. El uso de la letra griega es porque era m as f acil escribirla en notaci on matem atica. Cada vez que escuches el t ermino lambda cuando se habla de programaci on funcional traducelo en tu mente como funci on.

cuestiones antes planteadas, y lleg o a respuestas concluyentes. Independientemente de Alonzo, Alan Turing realiz o un trabajo similar. Desarroll o un formalismo diferente (ahora conocido como la m aquina de Turing), y lo us o para llegar a conclusiones similares a las de Alonzo. Despu es se demostr o que la m aquina de Turing y el c alculo lamda son equivalentes en poder. Aqu es donde la historia terminar a. Yo terminar a el art culo, y tu navegar as a otra p agina, si no fuera por el comienzo de la Segunda Guerra Mundial. El mundo estaba encendido. La armada y la marina estadounidenses usaban artiller a m as que nunca. En un esfuerzo por mejorar la precisi on de artiller a, el ejercito emple o a un gran grupo de matem aticos que continuamente calculaban las ecuaciones diferenciales requeridas para resolver tablas de bal stica. Se fue haciendo evidente que la tarea era demasiado complicada para resolverla manualmente, y se desarrollaron varios equipos para solucionar el problema. La primer m aquina para resolver tablas de bal stica fue Mark I, construida por IBM. Pesaba cinco toneladas, ten a 750,000 partes y pod a hacer hasta tres operaciones por segundo. La carrera, por supuesto, no hab a terminado. En 1949 fue presentada la computadora EDVAC y tuvo un exito tremendo. Fue el primer ejemplo de la arquitectura de von Neumann y fue efectivamente una implementaci on en el mundo real de la m aquina de Turing. Alonzo Church no tuvo mucha suerte aquella vez. A nales de la d ecada de 1950, el profesor del MIT John McCarthy (tambi en graduado de Princeton) se interes o en el trabajo de Alonzo Church. En 1958 present o el lenguaje de procesamiento de listas (Lisp). Lisp era una implementaci on del c alculo lambda que trabajaba en computadoras von Neumann! Muchos cient cos en computaci on reconocieron el poder expresivo de Lisp. En 1973, un grupo de programadores del Laboratorio de Inteligencia Articial del MIT desarrollaron hardware al que llamaron m aquina Lisp, una implementaci on nativa en hardware del c alculo lambda de Alonzo!

4.

Programaci on funcional

La programaci on funcional es la implementaci on pr actica de las ideas de Alonzo Church. No todas las ideas del c alculo lambda se implementan en la pr actica porque el c alculo lambda no fue dise nado para trabajar bajo limitaciones f sicas. Por tanto, como en la programaci on orientada a objetos, la programaci on funcional es un conjunto de ideas, no un conjunto estricto de reglas. Hay muchos lenguajes de programaci on funcional, y la mayor a hacen las cosas de formas muy diferentes entre s . En este art culo explicar e las ideas m as ampliamente usadas en los lenguajes funcionales usando ejemplos tomados del lenguaje Java (s , puedes escribir programas funcionales en Java si eres masoquista). En las siguientes secciones tomaremos Java tal cual, y le haremos modicaciones para transformarlo en lenguaje funcional u til. Empecemos nuestro viaje. El c alculo lambda fue creado para investigar problemas relacionados con c alculo. La programaci on funcional, por tanto, trata principalmente con c alculo, y, sorpresa!, lo hace mediante funciones. La funci on es la unidad b asica en programaci on funcional. Las funciones son usadas para pr acticamente todo, aun para los c alculos m as simples. Hasta las variables son reemplazadas con funciones. En programaci on funcional las variables son simplemente accesos directos a expresiones (para no tener que escribir todo en una misma l nea). No pueden ser modicadas. Todas las variables pueden ser asignadas solo una vez. En t erminos de Java esto signica que cada variable es declarada como nal (o const si hablamos de C++). No hay variables mutables en FP (ve el Listado 1).
Listado 1: Todas las variables son nales f i n a l i n t i = 5; f i n a l i n t j = i + 3;

Dado que cada variable en FP es nal, podemos llegar a dos conclusiones interesantes. Primero, que no tiene sentido escribir la palabra clave nal y segundo, que no tiene sentido llamar a las variables, pues. . . variables. Haremos estas dos modicaciones a Java: cada variable declarada en nuestro Java funcional sera nal por default, y nos referiremos a las variables como s mbolos. Para ahora probablemente te estas preguntando c omo podr as escribir algo razonablemente complicado en nuestro lenguaje reci en creado. Si todo s mbolo es inmutable, entonces no podemos cambiar el estado de nada! Esto no es estrictamente cierto. Cuando Alonzo estaba trabajando en el c alculo lambda no estaba interesado en mantener un estado para ser modicado posteriormente. Estaba interesado en realizar operaciones sobre datos (tambien conocidos como material de c alculo). Como sea, el c alculo lambda es equivalente en poder a la m aquina de Turing. Un lenguaje funcional puede hacer lo mismo que un lenguaje imperativo. C omo, entonces, podemos obtener los mismos resultados? En realidad los programas funcionales pueden mantener un estado, pero no usan las variables para eso. Usan funciones. El estado es mantenido en los par ametros de la funci on, en la pila. Si deseas mantener un estado para modicarlo posteriormente, escribes una funci on recursiva. Como ejemplo, escribamos una funci on que devuelva una cadena de car acteres de Java al rev es. Recuerda que cada variable (m as bien, cada s mbolo) es nal por default5 .
Listado 2: Funci on que devuelve una cadena al rev es String al_reves ( String arg ) { i f ( arg . length () == 0) { return arg ; } else { return al_reves ( arg . substring (1 , arg . length ()) + arg . substring (0 , 1)); } }

Esta funci on es lenta porque se llama a s misma repetidamente 6 . Es una devoradora de memoria porque repetidamente asigna memoria a los objetos. Pero est a escrita en estilo funcional. Quiz a te preguntes porque alguien querr a un programa de esta forma. Bueno, estaba a punto de dec rtelo.

5.

Benecios de FP

Probablemente pienses que no hay forma en que yo pueda defender la monstruosidad de funci on mostrada arriba. Cuando estaba aprendiendo programaci on funcional pensaba igual. Pero estaba equivocado. Hay muy buenos argumentos para usar este estilo de programaci on. Algunos son subjetivos. Por ejemplo, algunos defensores de la programaci on funcional arguyen que son m as entendibles. Dejar e de lado esos argumentos porque todos sabemos que la facilidad para entender algo es completamente subjetivo. Afortunadamente, hay una buena cantidad de argumentos objetivos.

5.1.

Prueba de unidades

Dado que todo s mbolo en FP es nal, ninguna funci on puede causar efectos colaterales. No puedes modicar los s mbolos que ya tienen un valor, y ninguna funci on puede modicar un valor fuera de su ambito para ser usado por otra funci on (a diferencia de un miembro de clase o una variable global). Eso signica que el u nico efecto de evaluar una funci on es su valor de retorno y que la u nica cosa que afecta el valor de retorno de la funci on son sus argumentos.
5 Es interesante notar que las cadenas de Java son inmutables de todas formas. Es aun m as interesante explorar las razones de esta falsedad, pero nos distraer amos de nuestro objetivo. 6 Muchos de los compiladores de lenguajes funcionales optimizan las funciones recursivas transformandolas en sus contrapartes iterativas siempre que sea posible. A eso se le llama optimizaci on de llamadas a la inversa (Tail recursion).

Eso es el sue no de todo probador de unidades. Puedes probar cada funci on de tu programa preocupandote unicamente por sus argumentos. No hay que preocuparse por llamar las funciones en un orden correcto, o congurar apropiadamente un escenario basado en estados externos. Todo lo que necesitas es pasar a la funci on los argumentos que representan los casos de prueba. Si cada funci on en tu programa pasa las pruebas de unidad puedes tener m as conanza sobre la calidad de tu software que si estas usando un lenguaje imperativo. En Java o C++ revisar el valor de retorno de una funci on no es suciente (esto es porque la funci on pudo haber modicado el estado externo, que tambi en debe vericarse). No as en un lenguaje funcional.

5.2.

Depuraci on

Si un programa funcional no trabaja como se esperar a, depurarlo es cosa f acil. El problema se repetir a siempre, pues un programa funcional no depende de lo que ocurra antes o despu es en otra parte que no sea la funci on misma. En un programa imperativo, un problema puede aparecer algunas veces y otras no. Dado que las funciones en un programa imperativo dependen del estado externo producido por efectos colaterales de otras funciones, quiz a tengas que revisar subrutinas de c odigo que no est an relacionadas directamente con el problema. No as en un programa funcional. Si el valor de retorno de una funci on es err oneo, entonces siempre ser a err oneo, sin importar el c odigo ejecutado antes o despu es de la funci on en cuesti on. Una vez reproducido el problema, llegar al fondo del asunto es trivial, es casi placentero. Detienes la ejecuci on del programa y examinas la pila. Cada argumento en la llamada a una funci on est a en la pila disponible para su inspecci on, igual que en un programa imperativo. Excepto que en un programa imperativo esto no es suciente, pues adem as hay que revisar variables miembro, variables globales y el estado de otras clases (que a su vez pueden depender de m as y m as cosas). Una funci on en un programa funcional depende u nicamente de sus argumentos, y esa informaci on esta justo frente a tus ojos! M as aun, en un programa imperativo examinar el valor de retorno de una funci on no te dar a una idea completa de si la funci on trabaja correctamente. Debes revisar docenas de objetos en el exterior de la funci on para vericar que hizo su trabajo bien. En un programa funcional lo u nico que tienes que revisar es el valor de retorno! Al examinar la pila ver as los argumentos pasados a las funciones y sus valores de retorno. En el momento en que alg un valor de retorno no tenga sentido, pasas a examinar la funci on implicada. Repites este proceso recursivamente hasta llegar a la fuente del problema!

5.3.

Concurrencia

Un programa funcional est a listo para ejecuci on concurrente sin ningun tipo de adaptaci on. No necesitas preocuparte por condiciones de carrera o bloqueos mutuos, porque no usas bloqueos! Si ninguna pieza de datos en un programa funcional es modicada dos veces en el mismo hilo de ejecuci on, mucho menos en hilos diferentes. Eso signica que puedes agregar hilos a tu aplicaci on, libre de los problemas que plagan a las aplicaciones concurrentes en los programas imperativos! Si este es el caso, por qu e nadie usa programas funcionales para aplicaciones altamente concurrentes? Bueno, a decir verdad, s lo hacen. Ericsson dise no un lenguaje funcional llamado Erlang para ser usado en conmutadores de telecomunicaciones escalables y altamente tolerantes a fallos. Muchos m as han reconocido los benecios que ofrece Erlang y han comenzado a usarlo. Estamos hablando de sistemas de telecomunicaciones y control de tr aco que son, por mucho, m as conables y escalables que los sistemas t picamente dise nados para Wall Street. A decir verdad, los sistemas Erlang no son escalables ni conables. Los sistemas Java s . Los sistemas Erlang son simplemente roca s olida. La historia de la concurrencia no termina aqu . Si tu aplicaci on es inher entemente de un solo hilo, el compilador puede aun optimizar programas funcionales para correr en m ultiples CPUs. Hecha un vistazo al siguiente fragmento de c odigo (Listado 3):

Listado 3: Funciones potencialmente paralelizables String s1 = u n a O p e r a c i o n M u y L a r g a 1 (); String s2 = u n a O p e r a c i o n M u y L a r g a 2 (); String s3 = concatenar ( s1 , s2 );

En un lenguaje funcional el compilador puede analizar el c odigo, clasicar las funciones que crean las cadenas s1 y s2 como potenciales consumidoras de tiempo, y ejecutarlas concurrentemente. Esto es imposible de hacer en un lenguaje imperativo porque cada funci on puede modicar el estado externo y la siguiente funci on a ejecutarse quiz a dependa de ello. En los lenguajes funcionales el an alisis autom atico de funciones para encontrar buenos candidatos para ejecuci on concurrente es tan trivial como reducir funciones a funciones inline en C++. As de f acil! En este sentido, los programas en estilo funcional son a prueba del futuro (por mucho que odie las expresiones de moda, esta vez hare una excepci on). Los creadores de hardware ya no pueden hacer que las CPUs sean m as r apidas. En su lugar, incrementan el n umero de n ucleos y atribuyen el cu adruple de aumento de velocidada a la concurrencia. Por supuesto, convenientemente olvidan mencionar que solo obtendremos benecios de eso si el software que usamos esta listo para ser paralelizable. Esto apenas es cierto en una peque na fracci on del software imperativo, pero es verdad en el 100 % del software funcional, porque los programas funcionales son paralelizables reci en salidos de la caja.

5.4.

Actualizaciones en caliente(Hot Code Deployment)

Hace tiempo, para instalar actualizaciones de Windows era necesario reiniciar la computadora. Varias veces. Y eso solo para instalar una nueva versi on del reproductor multimedia. Con Windows XP la situaci on ha mejorado bastante, pero aun esta lejos de ser ideal (hoy corr Windows Update en el trabajo y ahora un molesto icono en la bandeja del sistema no desaparecer a hasta que reinicie). Los sistemas Unix han tenido un mejor modelo desde hace tiempo. Para instalar una actualizaci on, solo necesitas detener los componentes relevantes, no el sistema operativo entero. Aunque esto es mejor, para cierto tipo de aplicaciones de servidor aun es inaceptable. Los sistemas de telecomunicaciones necesitan estar al 100 % todo el tiempo, pues si por una actualizaci on del sistema no se puede realizar una llamada de emergencia algunas vidas podr as perderse. No hay raz on para que las rmas de Wall Street deban detener sus sistemas para actualizaciones durante el n de semana. Una situaci on ideal ser a actualizar las partes importantes del c odigo sin detener ninguna parte del sistema. En un mundo imperativo, eso ser a imposible. Piensa en lo que implicar a reemplazar una clase Java en tiempo de ejecuci on. Si lo hicieramos, entonces toda instancia de dicha clase se volver a inutil porque el estado que encapsula se perder a. Tendr amos que escribir c odigo de control de versiones muy complicado para manejar la situaci on. Primero, tendr an que serializarse todas las instancias de clase en ejecuci on, destruirlas, crear nuevas instancias con la nueva denici on de la clase e intentar recargar los datos serializados en estas nuevas instancias, esperando que los datos migrados efectivamente funcionen en las nuevas instancias. Encima de todo, cada vez que hagamos cambios en la denici on de una clase, tendr amos que escribir el c odigo de migraci on manualmente. Y el c odigo de migraci on necesitar a prestar cuidadosa antenci on para no romper las relaciones entre los objetos. Tal vez suene bien en teor a, pero nunca funcionar a bien en la pr actica. En un programa funcional todo el estado est a guardado en la pila, en los argumentos pasados a las funciones. Esto hace que las actualizaciones en caliente sean signicativamente m as f aciles! De hecho, todo lo que necesitamos hacer es ejecutar di entre el c odigo en producci on y la nueva versi on, y entonces actualizar con el c odigo nuevo. El resto puede ser hecho por las herramientas de lenguaje, autom aticamente! Si crees que esto es ciencia cci on, pi ensalo de nuevo. Los ingenieros de Erlang han estado actualizando sistemas sin detenerlos, y eso ya por varios a nos.

5.5.

Pruebas y optimizaciones asistidas por computadora

Una caracter stica interesante de los lenguajes funcionales es que se puede razonar sobre ellos matem aticamente. Dado que un lenguaje funcional es simplemente una implementaci on de un sistema formal, todas las operaciones matem aticas que pueden hacerse sobre papel tambi en aplicar an a los programas escritos en dicho lenguaje. El compilador puede, por ejemplo, convertir piezas de c odigo en equivalentes m as ecientes con la comprobaci on matem atica de que ambas piezas de c odigo son equivalentes7 . Las bases de datos relacionales han usado este tipo de optimizaciones por a nos. No hay raz on por la que estas mismas t ecnicas no se usen en otro tipo de software. Adicionalmente, puedes usar estas t ecnicas para probar que ciertas partes de tu programa son correctas. Hasta es posible crear herramientas que analicen el c odigo y generen casos de pruebas para comprobaci on de unidades autom aticamente! Esta funcionalidad es invaluable para sistemas s olidos como las rocas. Si est as dise nando marcapasos o sistemas de control de tr aco a ereo estas herramientas son casi siempre un requerimiento. Si estas escribiendo aplicaciones que no pertenezcan a las verdaderas industrias de misi on cr tica, de todas formas estas herramientas te dar an una tremenda ventaja sobre tus competidores.

6.

Funciones de orden superior

Recuerdo que cuando estaba aprendiendo sobre estos benecios de los que he hablado pensaba: eso es muy bonito, pero inutil si tengo que escribir en un lenguaje lisiado donde todo es nal. Pero estaba en un error. Hacer todas las variables nales es lisiarlas en el contexto de un lenguaje imperativo como Java, pero no en el contexto de los lenguajes funcionales. Los lenguajes funcionales ofrecen un tipo diferente de herramientas de abstracci on que te haran olvidar siquiera haber querido modicar variables. Una de esas herramientas es la capacidad de trabajar con funciones de orden superior. Una funci on en un lenguaje funcional es diferente de una funci on en C o en Java. Es un superconjunto (es decir, puede hacer todo lo que una funci on Java puede hacer, y m as). Crearemos una funci on de la misma manera que lo hacemos en C (Listado 4.)
Listado 4: Funci on que suma dos n umeros i n t suma ( i n t i , i n t j ) { return i + j ; }

Sin embargo, este trozo de c odigo diere del equivalente en C. Extendamos nuestro compilador Java para soportar esta nueva notaci on de la que hablo. Cuando escribamos algo como lo anterior, nuestro compilador lo convertir a en el fragmento de c odigo Java del Listado 5 (no olvides que todo es nal):
Listado 5: Funci on expandida en nuestro Java funcional c l a s s t_funcion_suma { i n t suma ( i n t i , i n t j ) { return i + j ; } } t_funcion_suma suma = new t_funcion_suma ();

El s mbolo suma no es realmente una funci on. Es la instancia de una peque na clase con una sola funci on miembro. Por eso, podemos pasar suma en nuestro c odigo como un argumento para otras
7 Lo contrario no siempre es verdad. Mientras que a veces es posible probar que dos piezas de c odigo son equivalentes, esto no es posible en todas las situaciones.

funciones. Podemos asignarla a otro s mbolo. Podemos crear instancias de t funcion suma en tiempo de ejecuci on y ser an eliminadas por el recolector de basura cuando ya no se necesiten. Esto hace de las funciones objetos de primera clase, no distintos de los enteros o las cadenas. Las funciones que operan sobre otras funciones (o sea, que aceptan funciones como argumentos) son llamadas funciones de orden superior (higher order functions). No dejes que dicho t ermino te intimide. No es diferente de las clases Java que operan sobre otras (podemos pasar instancias de una clase a otra). Podr amos llamarlas clases de orden superior, pero a nadie le importa porque no hay una fuerte comunidad acad emica detr as de Java. C omo y cuando usaremos funciones de orden superior? Que bueno que preguntes. Cuando empiezas un programa como un aglutinado de c odigo monol tico sin preocuparte por jerarqu a de clases, y luego te das cuenta de que una pieza particular de c odigo se repite muchas veces, entonces la conviertes en una funci on (afortunadamente aun ense nan esto en las escuelas). Si ves que una pieza de l ogica dentro de tu funci on necesita comportarse diferente en diferentes situaciones, entonces la conviertes en una funci on de orden superior. Confundido? He aqu un ejemplo de la vida real tomado de mi trabajo. Supon que tenemos la pieza de c odigo Java del Listado 6 que recibe un mensaje, lo transforma de varias maneras, y lo reenv a a otro servidor.
Listado 6: Controlador de mensajes para un solo servidor class ControladorMensajes { void c o n t r o l a r M e n s a j e ( Mensaje msg ) { // ... msg . c o n f i g u r a r C o d i g o C l i e n t e ( " ABCD_123 " ); // ... enviarMensaje ( msg ); } // .. }

Ahora imagina que nuestro sistema ha cambiado y que tenemos que encaminar mensajes a dos servidores en lugar de uno. Todo es manejado de la misma forma a excepci on del c odigo del cliente (el segundo servidor lo quiere en un formato diferente). C omo abordamos la situaci on? Revisar amos donde es enviado el mensaje y elegir amos entre los c odigos del cliente para elegir el correcto, como se muestra en el Listado 7:
Listado 7: Controlador de mensajes para dos servidores class ControladorMensajes { void c o n t r o l a r M e n s a j e ( Mensaje msg ) { // ... i f ( msg . obtenerDestino (). equals ( " server1 " ) { msg . c o n f i g u r a r C o d i g o C l i e n t e ( " ABCD_123 " ); } else { msg . c o n f i g u r a r C o d i g o C l i e n t e ( " 123 _ABCD " ); } // ... enviarMensaje ( msg ); } // .. }

Sin embargo esta soluci on no es escalable. Si se agregaran m as servidores nuestra funci on crecer a linealmente y actualizarla ser a una pesadilla. Una soluci on de enfoque orientado a objetos es hacer

ControladorMensajes una clase base y especializarla con el c odigo de cada cliente en las clases derivadas (ve el Listado 8):
Listado 8: Controlador de mensajes orientado a objetos abstract c l a s s C o n t r o l a d o r M e n s a j e s { void c o n t r o l a r M e n s a j e ( Mensaje msg ) { // ... msg . c o n f i g u r a r C o d i g o C l i e n t e ( o b t e n e r C o d i g o C l i e n t e ()); // ... enviarMensaje ( msg ); } abstract String o b t e n e r C o d i g o C l i e n t e (); // .. } c l a s s C o n t r o l a d o r M e n s a j e s 1 extends C o n t r o l a d o r M e n s a j e s { String o b t e n e r C o d i g o C l i e n t e () { return " ABCD_123 " ; } } c l a s s C o n t r o l a d o r M e n s a j e s 2 extends C o n t r o l a d o r M e n s a j e s { String o b t e n e r C o d i g o C l i e n t e () { return " 123 _ABCD " ; } }

Ahora podemos instanciar una clase apropiada para cada servidor. A nadir m as servidores es ahora m as escalable. Aun as , es mucho c odigo para una modicaci on tan simple. Tuvimos que crear dos nuevos tipos solo para soportar distintos tipos de c odigos de clientes! Ahora, hagamos la misma cosa en nuestro lenguaje que soporta funciones de orden superior (Listado 9):
Listado 9: Controlador de mensajes con funciones de o rden superior class ControladorMensajes { void c o n t r o l a r M e n s a j e ( Mensaje msj , Function codigoCliente ) { // ... Mensaje msj1 = msg . c o n f i g u r a r C o d i g o C l i e n t e ( codigoCliente ()); // ... enviarMensaje ( msj1 ); } // ... } String codigoCliente1 () { return " ABCD_123 " ; } String codigoCliente2 () { return " 123 _ABCD " ; } C o n t r o l a d o r M e n s a j e s controlador = new C o n t r o l a d o r M e n s a j e s (); controlador . c o n t r o l a r M e n s a j e ( unMsj , codigoCliente1 );

10

En este caso, no creamos tipos nuevos, ni tuvimos que lidiar con jerarqu as de clases. Simplemente pasamos la funci on apropiada como par ametro. Conseguimos lo mismo que con la contraparte orientada a objetos, pero con varias ventajas adicionales. No nos restringimos a jerarqu as de clases: pordemos pasar nuevas funciones en tiempo de ejecuci on y cambiarlas en cualquier momento con un mayor grado de granularidad y menos c odigo. El compilador ha escrito c odigo orientado a objetos aglutinante para nosotros! Adem as, tenemos todos los otros benecios de FP. Por supuesto, las abstracciones provistas por los lenguajes funcionales no terminan aqu . Las funciones de orden superior son solo el principio.

7.

Currying

Mucha gente que conozco ha le do el libro Patrones de Dise no (Design Patterns ) escrito por La Pandilla de los Cuatro. Todo programador que se precie te dir a que el libro es agn ostico respecto al lenguaje de implementaci on y que los patrones aplican a la ingenier a de software en general, sin importar el lenguaje que uses. Esa es una declaraci on noble. Desafortunadamente est a lejos de la realidad. Los lenguajes funcionales son extremadamente expresivos. En un lenguaje funcional uno no necesita los patrones de dise no porque el lenguaje es de tan alto nivel, que terminas programando en conceptos que los hacen innecesarios. Uno de esos patrones es el patr on de Adaptador (C omo se diferencia del patron Facade ? Suena como si algui en necesitara llenar m as p aginas para satisfacer su contrato). Este es innecesario en un lenguaje que soporta una t ecnica llamada currying. El patr on de Adaptador es mejor conocido cuando se aplica a la unidad de abstracci on por default en Java (una clase). En lenguajes funcionales este patr on es aplicado a funciones. Dicho patr on toma una interfaz y la transforma en otra interfaz que alguien m as espera. Aqu hay un ejemplo de un patr on de Adaptador (Listado 10):
Listado 10: Ejemplo de un patron de adaptaci on i n t potencia ( i n t i , i n t j ); i n t cuadrado ( i n t i ) { return potencia (i , 2); }

El c odigo arriba mostrado adapta una interfaz de una funci on que eleva un entero a un exponente entero, a una interfaz de la funci on que eleva al cuadrado un entero. En c rculos acad emicos esta t ecnica trivial es llamada currying (en honor a Haskell Curry, quien realiz o las acrobacias matem aticas necesarias para formalizar esto). Dado que las funciones en FP son pasadas como argumentos (opuesto a las clases), currying es usado a menudo para adaptar funciones a una interfaz que alguien m as espera. Dado que la interfaz de las funciones son sus argumentos, la t ecnica currying es usada para reducir el n umero de argumentos (como en el ejemplo anterior). Los lenguajes funcionales vienen con esta t ecnica incorporada. No necesitas crear manualmente una funci on que envuelva a la otra, el lenguaje mismo lo har a por ti. Como antes, extendamos nuestro lenguaje para incorporar esta t ecnica.
Listado 11: Currying autom atico a una funci on cuadrado = i n t potencia ( i n t i , 2);

El c odigo del Listado 11 crear a por nosotros una funci on llamada cuadrado con un solo argumento. Llamar a a la funci on potencia con el segundo argumento establecido a 2. Esto compilar a en el siguiente c odigo Java (Listado 12):

11

Listado 12: Expansi on de currying en nuestro Java funcional class t_funcion_cuadrado { i n t cuadrado ( i n t i ) { return potencia (i , 2); } } t _ f u n c i o n _ c u a d r a d o cuadrado = new t _ f u n c i o n _ c u a d r a d o ();

Como puedes ver, de forma simple creamos una envoltura de la funci on original. En FP currying es solo eso (una forma f acil de y r apida de crear envolturas de otras funciones). Tu te concentras en tu trabajo, y el compilador escribe el c odigo apropiado para ti! Cuando usar currying? Es f acil de responder. Cada vez que quieras usar el patr on de Adaptador (una envoltura).

8.

Evaluaci on tard a

La evaluaci on tard a (o perezosa) es un t ecnica interesantes que se vuelve posible una vez que adoptamos una losof a funcional. Ya hab amos visto el fragmento de c odigo del Listado 13 cuando hablamos sobre concurrencia:
Listado 13: Fragmento de c odigo candidato a evaluaci on tard a String s1 = u n a O p e r a c i o n M u y L a r g a 1 (); String s2 = u n a O p e r a c i o n M u y L a r g a 2 (); String s3 = concatenar ( s1 , s2 );

En un lenguaje imperativo el orden de evaluaci on queda claro. Dado que cada funci on pudiera afectar o depender del estado externo, es necesario ejecutarlas en orden: primero unaOperacionMuyLarga1, luego unaOperacionMuyLarga2, seguida de concatenar. No as en un lenguaje funcional. Como vimos antes, unaOperacionMuyLarga1 y unaOperacionMuyLarga2 pueden ser ejecutadas concurrentemente porque est a garantizado que ninguna funci on afecta o depende del estado global. Pero si no es necesario ejecutarlas concurrentemente, necesitamos ejecutarlas en orden? La respuesta es no. Solo necesitamos ejecutar estas operaciones cuando otra funci on dependa de s1 y s2. Ni siquiera tenemos que ejecutarlas antes de concatenar. Si reemplazamos concatenar con una funci on que tenga una condici on y use solo uno de sus par ametros, quiz a nunca evaluemos el otro par ametro! Haskell es un ejemplo de lenguaje de evaluaci on tard a. En Haskell no hay garant a de que el c odigo sea ejecutado en orden (o que siquiera sea ejecutado), pues Haskell solo ejecuta el c odigo cuando es requerido. La evaluaci on tard a tiene numerosas ventajas as como desventajas. Discutiremos las ventajas aqu , y veremos c omo lidiar con las desventajas en la siguiente secci on.

8.1.

Optimizaci on

La evaluaci on tard a ofrece un tremendo potencial para optimizaciones. Un compilador de este tipo ve al c odigo funcional exactamente como un matem atico ve una expresi on algebraica. Puede cancelar cosas y prevenir completamente su ejecuci on, reacomodar las piezas para mayor eciencia, incluso reacomodar el c odigo de una forma que reduzca los errores, todo esto garantizando que la l ogica permanecer a intacta. Este es el mayor benecio de representar programas usando estrictamente primitivas formales: c omo el c odigo se adhiere a leyes matem aticas, se puede pensar en el matem aticamente.

12

8.2.

Abstracci on de estructuras de control

La evaluaci on tard a provee un nivel superior de abstracci on que permite implementar cosas que de otra forma ser an imposibles. Por ejemplo, considere implentar la estructura de control del Listado 14:
Listado 14: Estructura de control personalizada aMenosQue ( stock . esEuropeo ()) { enviarASEC ( stock ); }

Deseamos ejecutar eviarASEC a menos que el stock sea europeo. C omo podr amos implementar aMenosQue ? Sin evaluaci on tard a, necesitariamos alguna tipo de macro, pero en un lenguaje como Haskell no. Podemos implementar aMenosQue como una funci on! (Ve el Listado 15)
Listado 15: Implementaci on de la estructura de control con evaluaci on tard a void aMenosQue ( boolean condicion , List codigo ) { i f (! condicion ) codigo ; }

Nota que codigo nunca es evaluado si la condici on es verdadera. No podemos hacer los mismo en un lenguaje de evaluaci on estricta porque los argumentos ser an evaluados antes de entrar en aMenosQue.

8.3.

Estructuras de datos innitas

Los lenguajes de evaluaci on tard a permiten la denici on de estructuras de datos innitas, algo que es mucho m as complicado en un lenguaje de evaluaci on estricta. Por ejemplo, considera una lista con los n umeros de Fibonacci. Est a claro que no podemos realizar c alculos sobre una lista innita en un tiempo razonable, o guardarla en memoria. En lenguajes de evaluaci on estricta como Java, simplemente denimos una function bonacci que devuelva un miembro particular de la secuencia. En un lenguaje como Haskell podemos abstraerlo aun m as y simplemente denir una lista innita con los n umeros de Fibonacci. Como el lenguaje es de evaluaci on tard a, solo las partes necesarias de la lista que son usadas realmente por el programa ser an evaluadas. Esto permite abstraer muchos problemas y verlos desde una persepectiva de m as alto nivel (por ejemplo, podemos usar procesamiento de listas sobre una lista innita).

8.4.

Desventajas

Por supuesto, no todo es miel sobre hojuelas. La evaluaci on tard a implica tambi en desventajas. La principial es que, pues, es tard a (o perezosa). Muchos de los problemas del mundo real requieren evaluaci on estricta. Por ejemplo, considera el Listado 16:
Listado 16: C odigo que requiere evaluaci on estricta System . out . println ( " Por favor ingresa tu nombre : " ); System . in . readLine ();

En un lenguaje de evaluaci on tard a no hay garant a de que la primera l nea se ejecutar a antes de la segunda! Esto signica que no podemos hacer operaciones de E/S, ni usar funciones nativas de ninguna forma util (puesto que necesitan ser llamadas en orden, dado que dependen de efectos colaterales), ni interactuar con el mundo exterior! Si hicieramos que las primitivas fueran ejecutadas en un orden espec co, perder amos los benecios de poder ver nuestro c odigo desde un punto de vista matem atico (con lo que se ir an tambi en todos los benecios de la programaci on funcional). 13

Afortunadamente, no todo est a perdido. Los matem aticos han trabajado y desarrollado algunos trucos para asegurarse de que porciones del c odigo sean ejecutadas en un orden particular en un ambiente funcional. Tenemos lo mejor de los dos mundos! Estas t ecnicas incluyen continuaciones, monads, y escritura singular. En este art culo solo veremos las continuaciones. Dejaremos las otras dos para otra ocasi on. Es interesante que las continuaciones son u tiles para m as cosas que permitir un orden particular de evaluaci on. Hablaremos de esto tambi en.

9.

Continuaciones

Las continuaciones son a la programaci on lo que el C odigo Da Vinci a la historia de la humanidad: la revelaci on de uno de los m as grandes encubrimientos conocidos por el hombre. Bueno, quiz a no tanto, pero ciertamente es una revelaci on del enga no en la misma forma que las ra ces cuadradas de n umeros negativos. Cuando aprendimos sobre funciones, solo aprendimos la verdad a medias, pues asumimos f alsamente que las funciones deben regresar su valor de retorno a quien la llam o. En este sentido, las continuaciones son una generalizaci on de las funciones. Una funci on no necesita regresar a quien la llam o, y m as bien puede ir a cualquier otra parte del programa. Una continuaci on es un par ametro opcional que le dice a la funci on donde debe continuar la ejecuci on del programa. La descripci on quiz a suene m as complicada de lo que parece. Mira el fragmento de c odigo del Listado 17:
Listado 17: Dos l neas de c odigo dependientes i n t i = suma (5 , 10); i n t j = cuadrado ( i );

La funci on suma devuelve 15, valor que es asignado al s mbolo i, en el lugar donde suma fue llamado. Despu es el valor de i es usado para llamar cuadrado. Note que un compilador de evaluaci on tard a no puede reacomodar estas l neas de c odigo pues la segunda l nea depende de la evaluaci on correcta de la primera. Podemos reescribir este bloque de c odigo usando el Estilo de Pase de Continuaci on o CPS (por las siglas en ingl es para Continuation Pass Style ), donde la funci on suma no regresa a quien la llam o sino entrega su resultado a la funci on cuadrado (Listado 18).
Listado 18: Estilo de pase de continuaci on (CPS ) i n t j = suma (5 , 10 , cuadrado );

En este caso a suma se le pasa otro par ametro, una funci on a la que suma debe llamar cuando haya terminado su trabajo. En este caso, cuadrado es la continuaci on de suma. En ambos casos (Listados 17 y 18) j ser a igual a 225. Aqu encontramos el primer truco para forzar a nuestro lenguaje de evaluaci on tard a a evaluar dos expresiones en orden. Considera el fragmento de c odigo de E/S del Listado 19 (muy familiar por cierto):
Listado 19: E/S que depende del o rden de ejecuci on System . out . println ( " Por favor ingresa tu nombre : " ); System . in . readLine ();

Estas dos l neas no dependen una de la otra, por lo que el compilador tiene libertad para reacomodarlas como desee. Sin embargo, si reescribimos esto en CPS (Estilo de Pase de Continuaci on), entonces habr a una dependencia y el compilador se ver a forzado a evaluar las dos l neas en orden! (Listado 20)
Listado 20: E/S escrita en estilo de pase de continuaci on (CPS ) System . out . println ( " Por favor ingresa tu nombre : " , System . in . readLine );

14

En este caso println necesita llamar a readLine con su resultado, y devolver el resultado de readLine. Esto nos permite asegurarnos de que de que las dos l neas ser an ejecutadas en el orden deseado y que readLine ser a evaluada (porque toda la expresi on depende del u ltimo valor como resultado). En el caso de la funci on println devuelve void, pero si devolviera cualquier valor abstracto que readLine acepte, entonces nuestro problema queda solucionado! Por supuesto, encadenar funciones de esta forma se vuelve pronto en algo ilegible, pero no tiene por qu e ser as . Podemos a nadir un poco de az ucar sint actica al lenguaje, de tal forma que nos permita simplemente escribir las expresiones en orden, y que el compilador encadene las llamadas por nosotros autom aticamente. Ahora podemos evaluar las expresiones en el orden que queramos sin perder ninguno de los benecios de FP (incluyendo al habilidad de razonar sobre nuestro programas matem aticamente)! Si aun te resulta confuso, recuerda que una funci on es solo una instancia de una clase con una solo m etodo. Reescribe las dos l neas de tal forma que println y readLine sean instancias de clases y lo entender as m as claramente. En este momento yo cerrar a esta secci on, si no fuera porque solo hemos ara nado la supercie de las continuaciones y su utilidad. Podemos escribir programas enteros en estilo CPS, donde cada funci on reciba como par ametro adicional una continuaci on (la funci on donde el programa debe continuar) y entregar el resultado a esa funci on de continuaci on. Adem as podemos convertir cualquier programa a CPS simplemente al tratar las funciones como casos especiales de continuaciones (funciones que siempre regresan a quien las llam o). Esta conversi on es trivial y puede efectuarse autom aticamente (de hecho, muchos compiladores lo hacen). Una vez que un programa es convertido al estilo CPS queda claro que cada instrucci on tiene un tipo de continuaci on, una funci on a la que llamar a con su resultado, que en un programa regular ser a el punto donde debe retornar. Tomemos una instrucci on del c odigo anterior, digamos suma(5, 10). En un programa en estilo CPS esta claro cual es la continuaci on de suma (es la funci on a la que llama suma cuando ha terminado). Pero, que hay en el caso de un programa que no est a escrito en estilo CPS ? Podr amos, por supuesto, convertir el programa a CPS, pero, es necesario? Resulta que no. Mira con cuidado a nuestra conversi on CPS. Si trataras de escribir un compilador para esto y piensas lo suciente en ello, te dar as cuenta de que un programa en estilo CPS, no necesita pila para las llamadas a funciones! Ninguna funci on regresa en el sentido tradicional, sino que simplemente llama a otra funci on. No necesitamos empujar los argumentos de la funci on en la pila con cada llamada para luego sacarlos otra vez. Podemos simplemente guardarlos en un bloque de memoria y usar la instrucci on jump en su lugar. No necesitaremos m as los argumentos originales (nunca son usados de nuevo pues ninguna funci on regresa!). As que los programas en estilo CPS no necesitan pila sino un argumento extra con la funci on a la cual llamar a continuaci on. Los programas que no est an escritos en estilo CPS no tiene el argumento de continuaci on extra, sino la pila. Que contiene la pila en esos casos? Simplemente los argumentos, y un puntero a donde la funci on debe retornar. Se te prendi o el foco? La pila contiene u nicamente informaci on de continuaci on! El puntero para la instrucci on de retorno en la pila es esencialmente lo mismo que la funci on a llamar en programas CPS ! Si deseas saber cual es la continuaci on para add(5, 10), simplemente examinas la pila en ese punto! As de simple. Una continuaci on y un puntero para la instrucci on de retorno son la misma cosa, solo que la continuaci on es pasada expl citamente, de tal forma que no necesita ser el mismo lugar desde donde la funci on fue llamada. Si recuerdas que una continuaci on es una funci on, y que una funci on en nuestro lenguaje es compilado a una instancia de una clase, te dar as cuenta de que un puntero para la instrucci on de retorno y el argumento de continuaci on son realmente la misma cosa, dado que nuestra funci on (al igual que una instancia de clase) es simplemente un puntero. Esto signica que en algun punto de tu programa puedes averiguar cu al es la continuaci on actual (que es simplemente la informaci on que se encuentra en la pila). Bien, ahora ya sabemos lo que es una continuaci on actual. De que nos sirve? Cuando obtenemos la continuaci on actual y la guardamos en algun lugar, en realidad estamos guardando el estado actual de nuestro programa (como si lo congelaramos en el tiempo). Esto es similar a cuando el sistema 15

operativo entra en hibernaci on. Un objeto de continuaci on contiene la informaci on necesaria para reiniciar el programa desde el punto donde fue creado. Un sistema operativo hace esto todo el tiempo cuando cambia de contexto entre los hilos. La u nica diferencia es que el sistema operativo lo controla todo. Si tu solicitas un objeto de continuaci on (en el lenguaje Scheme se hace mediane llamar a la funci on call-with-current-continuation) obtienes un objeto que contiene la continuaci on actual (toda la informaci on en la pila, o la siguiente funci on a ser llamada en el caso de CPS ). Ahora puedes guardar este objeto en una variable (o en el disco). Cuando deseas reiniciar tu programa con este objeto de continuaci on, transformas el estado de tu programa al que est a guardado en el objeto de continuaci on. Es parecido a regresar a un hilo suspendido o despertar a una computadora de hibernaci on, a excepci on de que lo puedes una y otra y otra vez. Cuando un sistema operativo despierta, la informaci on de hibernaci on es destruida. Si no fuera as , podr as despertarla una y otra vez en el mismo punto, como si volvieras en el tiempo. Tienes esa clase de control con las continuaciones! En que situaciones resultan u tiles las continuaciones? Usualmente cuando tratas de simular estado en aplicaci on que inherentemente no tienen estado. Una gran aplicaci on de las continuaciones son las aplicaciones web. ASP.NET de Microsoft hace un esfuerzo enorme para tratar de simular estado en aplicaciones web de tal forma que puedas escribir aplicaciones sin tanto l o. Si C# soportara continuaciones, la mitad de la complejidad de ASP.NET desaparecer a (solo guardar as un objeto de continuaci on y lo reiniciar as cuando el usuario hiciera una nueva petici on al servidor web). Desde el punto de vista del programador de la aplicaci on web, no habr a interrupci on (el programa simplemente continuar a en la siguiente l nea!). Las continuaciones son una abstracci on incre blemente u til para algunos problemas. Considerando que muchos de los pesados cliente tradicionales se est an moviendo a la web, las continuaciones cobrar an mayor relevancia en el futuro.

10.

Coincidencia de patrones (Pattern Matching )

La coincidencia de patrones no es una caracter stica inovadora. De hecho, tiene poco que ver con la programaci on funcional. La u nica raz on por la que usualmente se le atribuye a FP es porque los lenguajes funcionales han tenido coincidencia de patrones por algun tiempo, mientras que los lenguajes imperativos modernos aun no. Echemos un vistazo a la coincidencia de patrones mediante un ejemplo. El listado 21 muestra la funci on Fibonacci en Java:
Listado 21: Funci on Fibonacci en Java i n t fib ( i n t n ) { i f ( n == 0) return 1; i f ( n == 1) return 1; return fib (n -2) + fin (n -1); }

El Listado 22 muestra la funci on Fibonacci en nuestro lenguaje derivado de Java con soporte para coincidencia de patrones. Cual es la diferencia? Que el compilador implementa la bifurcaci on por nosotros. Cual es la gran cosa con esto? Ninguna. Alguien not o que un gran n umero de funciones contienen muchas sentencias switch complicadas (esto es particularmente cierto en los programas funcionales) y decidi o que ser a una buena idea abstraerlo de esta forma. Dividimos la funci on en muchas, y ponemos patrones en lugar de algunos de los argumentos (algo parecido a la sobrecarga de funciones). Cuando la funci on es llamada, el compilador compara los argumentos con las deniciones en tiempo de ejecuci on y elige la correcta. Esto se hace usualmente eligiendo la denici on m as espec ca. Por ejemplo, int b(int n) podr a ser llamada siendo n igual a 1, pero en este caso int b(1) es m as espec ca.

16

Listado 22: Funci on Fibonacci con conincidencia de patrones i n t fib (0) { return 1; } i n t fib (1) { return 1; } i n t fib ( i n t n ) { return fib (n -2) + fib (n -1); }

La coincidencia de patrones es usualmente m as compleja de la que se observa en estos ejemplos. Por ejemplo, un sistema avanzado de coincidencia de patrones nos permitir a hacer lo siguiente (Listado 23):
Listado 23: Patrones de coincidencia avanzados i n t f ( i n t n < 10){ ... } i n t f ( i n t n ) { ... }

Cuando resulta u til usar coincidencia de patrones? En un gran n umero de situaciones! Cada vez que tengas una compleja estructura de ifs anidados, la coincidencia de patrones puede hacer un mejor trabajo con menos c odigo de tu parte. Una funci on que viene a la mente es la funci on est andar WndProc que todas las aplicaciones Win32 deben proveer (y esto a menudo se hace mediante abstracciones). Usualmente un sistema de coincidencia de patrones puede examinar tanto colecciones como valores simples. Por ejemplo, si pasas un arreglo (array ) a tu funci on, podr as elegir entre los arreglos cuyo primer elemento sea 1 y el tercero sea mayor que 3. Otro benecio de la coincidencia de patrones es que si necesitas a nadir o modicar condiciones, no tienes que lidiar con una u nica funci on enorme. Simplemente a nades (o modicas) las deniciones apropiadas. Esto elimina la necesidad de un buen n umero de los patrones de dise no descritos en el libro de la Pandilla de los Cuatro. Mientras m as complejas sean las condiciones, m as te ser au til la coincidencia de patrones. Una vez que te acostumbras, empiezas a preguntarte c omo pudiste vivir tanto tiempo sin esto.

11.

Cierres (Closures )

Hasta ahora hemos discutido caracter sticas de lenguajes funcionales puros (lenguajes que implementan el c alculo lambda y que no entran en conicto con los formalismos de Church). Sin embargo, muchas de las caracter sticas de los lenguajes funcionales son u tiles fuera del ambito del c alculo lambda. Mientras que la implemetaci on de un sistema axiom atico es util pues nos permite pensar en nuestros programas desde el punto de vista matem atico, quiz a esto no siempre sea pr actico. Muchos de los lenguajes eligen incorporar algunos elementos funcionales sin adherirse estrictamente a la doctrina funcional. Muchos de esos lenguajes (como Common Lisp) no requieren que todas las variables sean nales (puedes modicarlas en cualquier momento). Adem as, no requieren que las funciones dependan s olo de sus argumentos (las funciones pueden accesar al estado externo). Pero incluyen caracter sticas funcionales (como las funciones de orden superior). El paso de funciones en un lenguaje impuro es un poco diferente que hacerlo en los connes del c alculo lambda, y precisa una interesante caracter stica a veces llamada cierre l exico. Veamos el c odigo de ejemplo del Listado 24. Recuerda, en este caso las variables no son nales y las funciones pueden referirse a variables fuera de su ambito: 17

Listado 24: Ejemplo de cierre l exico Function c r e a r F u n c i o n P o t e n c i a ( i n t potencia ) { i n t funcionPotencia ( i n t base ) { return elevar ( base , potencia ); } return funcionPotencia ; } Funcion cuadrado = h a c e r F u n c i o n P o t e n c i a (2); cuadrado (3) ; // D e v u e l v e 9

La funci on crearFuncionPotencia devuelve una funci on que toma un u nico argumento y que lo eleva a cierta potencia. Que sucede cuando evaluamos cuadrado(3)? La variable potencia ya no est a en el ambito de funcionPotencia porque crearFuncionPotencia ya ha terminado y lo que hab a en la pila ya no est a. C omo puede funcionar cuadrado entonces? El lenguaje debe, de alguna forma, guardar el valor de potencia para que cuadrado funcione. Que pasar a si crearamos otra funci on llamada cubo que elevara a la tercera potencia? En tiempo de ejecuci on existen dos copias de potencia, una por cada funci on generada usando crearFuncionPotencia. Al fen omeno de guardar estos valores se les llama cierres. Los cierres no guardan solo argumentos de la funci on antriona. Por ejemplo, un cierre puede tomar esta forma (Listado 25):
Listado 25: Incrementador usando cierres Function c r e a r I n c r e m e n t a d o r () { i n t n = 0; i n t incremento () { return ++ n ; } } Function inc1 = c r e a r I n c r e m e n t a d o r (); Function inc2 = c r e a r I n c r e m e n t a d o r (); inc1 (); inc1 (); inc1 (); inc2 (); inc2 (); inc2 (); // // // // // // devuelve devuelve devuelve devuelve devuelve devuelve 1 2 3 1 2 3

En tiempo de ejecuci on se guarda el valor de n, para que los incrementadores puedan accesarlo. M as que eso, guarda varias copias de n, una por cada incrementador, aunque se suponga que desaparecer an cuando crearIncrementador retorne. En que se convierte este c odigo al compilar? C omo funcionan los cierres detr as del tel on? Afortunadamente, tenemos un pase para los camerinos. Un poco de sentido com un nos ayudar a. La primera observaci on que hacemos es que las variables locales no est an limitadas a reglas simples de resoluci on de ambito y pueden tener un tiempo de vida indeterminado. La conclusi on obvia es que no est an guardadas en la pila (m as bien est an guardadas en el mont culo8 ). Un cierre, entonces, es implementado justo como cualquiera de las funciones antes vistas, a excepci on de que tiene referencias adicionales a las variables en el ambito de su antrin on (Ve el Listado 26). Cuando un cierre hace referencia a una variable que no est a en el ambito local, entonces consulta esta referencia en el ambito del padre (o funci on antriona). Eso es todo! Los cierres hacen que los mundos funcional y orientado a objetos est en m as cerca. Cada vez que creas una clase que mantiene
8 Esto no es m as lento que guardas valores en la pila, pues una vez que introduces el recolector de basura, la asignaci on de memoria se vuelve una operaci on O(1).

18

Listado 26: Tabla de s mbolos del ambito superior class t_alguna_funcion { SymbolTable ambientePadre ; // ... }

alg un estado y lo pasa a algun otro lugar, piensa en los cierres. Un cierre es solamente un objeto que crea variables miembro al vuelo al tomarlas de su ambito, para que no tengas que hacerlo tu!

12.

Que sigue?

Este art culo solo toca la supercie de la programaci on funcional. A veces un peque no esbozo puede convertirse en algo grande, y en nuestro caso eso es algo muy bueno. En el futuro planeo escribir sobre teor a de categor as, monads, estructuras de datos funcionales, sistemas de escritura en lenguajes funcionales, concurrencia funcional, bases de datos funcionales y mucho m as. Si logro escribir (y en el proceso, aprender) sobre la mitad de estos temas, mi vida estar a completa. Mientras tanto, Google es nuestro amigo.

13.

Tienes algun comentario?

Si tienes alguna pregunta, comentario, o sugerencia, por favor d ejame una nota en coeemug@gmail.com. Me dar a gusto saber tus opiniones.

19

También podría gustarte