Programación funcional, un enfoque diferente a los problemas de siempre

Programación funcional, un enfoque diferente a los problemas de siempre
Facebook Twitter Flipboard E-mail

Cuando pensamos en programación, excepto algún que otro gurú que se pasea por la vida viendo las letritas verdes de Matrix, nos suelen venir a la mente ideas como variables, ifs, objetos, funciones, procedimientos, todos esos conceptos presentes en la mayoría de los típicos lenguajes imperativos, que componen la vasta mayoría del software existente.

Es bueno, sin embargo, no perder de vista que todo esto son abstracciones más o menos arbitrarias y que puede haber otros enfoques de cómo escribir archivos de texto llenos de código formal que puedan convertirse luego en unos y ceros para alimentar a nuestro computador. Existen muy variados paradigmas de programación, entre los que se encuentra el que nos ocupa: la conocida como programación funcional, que incorpora algunas ideas muy interesantes para todo programador.

¿Qué es la programación funcional?

Antes de nada, quiero aclarar que durante el artículo hablaré basándome en un lenguaje funcional en concreto: Haskell. En otros lenguajes puede haber implementaciones ligeramente diferentes, al igual que C++ y Python implementan la POO de manera distinta, pero la idea de fondo será siempre la misma.

Los lenguajes habituales se basan, en mayor o menor medida, en realizar una serie de pasos, en definir lo que las cosas hacen; esto se conoce como programación imperativa. En programación funcional, definiremos más bien lo que las cosas son.

El propio nombre nos da una pista de la característica fundamental de este paradigma: se basa fuertemente en un concepto de función algo diferente al que estamos acostumbrados, más cercano al matemático que su contraparte en los lenguajes imperativos. No hay variables, por lo que estas funciones sólo tratarán con sus valores de entrada y con constantes predefinidas; no tienen más posibilidad de acción.

Pongamos un ejemplo. La función factorial, imperativamente, se definiría como «para calcular el factorial, coge un valor de entrada, réstale uno, asigna este valor a x, calcula el factorial de x, asígnalo a y, y devuelve x por y». Órdenes, órdenes. En cambio, funcionalmente se definiría como «el factorial de n es n por el factorial de n-1». Es una mera diferencia de abstracción, pero los lenguajes funcionales te fuerzan a pensar en ese alto nivel y no tiene sentido hablar de órdenes, igual que en Python no tiene sentido hablar de registros, porque están situados a un nivel inferior.

Básicamente, un programa funcional es aquel que sólo está compuesto de funciones que se limitan a hacer corresponder cada elemento que se le pase como argumento con un valor.

Algunas características típicas

Como consecuencia de todo esto, en los programas funcionales se encuentran algunas características:

qsort [] = []
qsort (x:xs) =  
  qsort (filter (< x) xs) ++
  [x] ++ 
  qsort (filter (>= x) xs)
Implementación de Quicksort en Haskell. Es bonita, ¿eh? Pues las hay más elegantes.
  • Transparencia referencial. Al no haber variables, no hay efectos laterales, es decir, al ejecutar la función (aunque ya hemos visto que el concepto de ejecutar no existe como tal) no cambiará nada fuera del entorno de ésta. Tampoco dependerá para nada de lo que haya en ese entorno. Se dice que una función tiene transparencia referencial si, para un valor de entrada, produce siempre la misma salida. En programación funcional esto es siempre así por definición. Esto permite la…

  • Evaluación perezosa (Lazy evaluation). Para una mente acostumbrada a lo imperativo esto cuesta de entender, así que atención. Al no tener que seguir una serie de órdenes, una tras otra, algunos los lenguajes funcionales sólo evalúan lo que les es requerido en cada momento. Esto permite cosas como el código Haskell take 5 [1..], que significa «coger los cinco primeros números de una lista infinita de números empezando por el 1». En un lenguaje imperativo, esto llevaría a un bucle infinito mientras se genera la lista. En Haskell, sólo se genera lo que se necesita; en este caso, los cinco primeros números. Esto puede redundar en una ejecución más eficiente y un código más claro y cercano al matemático.

  • Altísima abstracción. Los lenguajes funcionales implementan muchísimos mecanismos de abstracción que te hacen la vida muy, muy feliz. Un brutal sistema de tipos de datos, polimorfismo en muchos niveles, y un montón de nuevos conceptos como funciones de orden superior, functores, mónadas… Las inevitables operaciones que no son puramente funcionales, como la entrada/salida o los números aleatorios, quedan envueltos en abstracciones que resuelven la papeleta con elegancia.

  • Elegancia, legibilidad, flexibilidad. Por lo general, cuanto más abstracto es un lenguaje, más se potencian estas características, por lo que los programas funcionales suelen ser más claros, más concisos y más bellos que sus contrapartidas en lenguajes imperativos (incluso en preciosidades como Python o Ruby). El código más artístico que he visto nunca, con diferencia, está en Haskell. Además, la ausencia de entorno y de estado permite que, si estás leyendo una función, sólo tengas que retener en tu cabeza esa función, sin preocuparte de más. Esto facilita además…

  • Facilidad para las pruebas y la depuración. Gracias a la transparencia referencial, hacer unit testing en lenguajes imperativos es trivial. Además, el alto nivel hace que los errores lo tengan difícil para esconderse por el código, por lo que suelen salir programas muy fiables.

¿Y por qué no los usamos todos siempre?

No todo es tan bonito, claro. En la práctica, existen algunos inconvenientes a tener en cuenta.

max([H|T]) -> max2(T, H).
max2([], Max) -> Max;
max2([H|T], Max) 
  when H > Max -> max2(T, H);
max2([_|T], Max) -> max2(T, Max).
Máximo de una lista, en Erlang.
  • Mayor dificultad inicial. Aunque sean muy fáciles de entender y mantener, suele ser más difícil escribir un programa funcionalmente, sobre todo para mentes acostumbradas a lo imperativo. La gran variedad de conceptos complejos a tener en cuenta hace difícil dominar el lenguaje y producir buen código. La ausencia de variables de estado, aunque nos facilite la vida a posteriori, hace que tengas que comerte más la cabeza al sentarte a picar líneas y planificar muy bien lo que vas a hacer, y esto en entornos de producción puede no ser lo más conveniente. De hecho, esto despierta hasta algunos odios...

  • Falta de recursos. Al estar tan poco extendidos, faltan la gran cantidad de recursos (librerías, frameworks, etc.) existentes para otros lenguajes; aunque últimamente, que se vuelven a poner relativamente de moda, cada vez salen más soluciones, como este binding de Cocoa para Haskell.

Por dónde seguir

Sólo hemos visto una torpe introducción a la punta del iceberg. Si eres un sanamente inquieto programador, te habrá picado el gusanillo de probar todo esto tan raro. Afortunadamente, la comunidad de Haskell parece más preocupada por documentar el lenguaje que de escribir software, por lo que recursos de aprendizaje hay de sobra y recomiendo empezar por este lenguaje.

  • Try Haskell, una amena introducción interactiva online que te dejará con ganas de más.

  • Learn you a Haskell, uno de los mejores tutoriales de todos los tiempos, extremadamente divertido.

  • En Haskell.org tienes infinidad de documentación, incluido el fantástico buscador Hoogle, y una comunidad muy dispuesta a ayudar.

(defn fib
  ([] (concat [0 1] (fib 0 1)))
  ([a b] (lazy-cons (+ a b) 
    (fib b (+ a b)))))
Secuencia infinita Fibonacci en Clojure. Para usar con lazy evaluation, claro.

Si te manejas con Java, puede que te interese más Clojure, que utiliza su máquina virtual; recientemente en .NET se ha incorporado el lenguaje F#.

En cualquier caso, es muy interesante echarle un ojo a este mundo. Aunque no llegues a utilizarlo en la práctica, te abrirá la mente y hará que mejores tu técnica y lo que escribas en tu horrible e impuro lenguaje imperativo favorito tenga más estilo (como reza la Décima Ley de Greenspun). Animo encarecidamente a ponerse con ello.

Comentarios cerrados
Inicio