Programación funcional

La programación funcional es un paradigma de programación declarativa basado en el uso de funciones matemáticas, en contraste con la programación imperativa, que enfatiza los cambios de estado mediante la mutación de variables.

Swift puede ser usado como lenguaje funcional, y esto hace que nos facilite en muchas ocasiones la legibilidad. No se si se puede considerar esto syntactic sugar, pero para mi al menos lo es.

El ejemplo más claro es a la hora de hacer operaciones con arrays (o cualquier conjunto). En muchas ocasiones tenemos que escribir mucho código para hacer operaciones sencillas en un array.

Si queremos que en un array compuesto de valores numéricos transformemos cada valor por su cuadrado, habría que hacerlo así.

En swift tenemos un método llamado map que nos ayuda a realizar operaciones sobre cada posición de un array, de la siguiente manera.

Este método, ya sencillo de por sí, aún se le puede aplicar un poco más de azúcar de esta forma.

Si el array hubiera tenido valores nulos y quisiéramos que el resultante los elimines tenemos otro método llamado flatMap que hace este trabajo, de la siguiente manera.

Otra tarea muy frecuente con arrays es obtener un array filtrado a partir de otro. Se haría de la siguiente forma.

Pero si usamos el método filter lo podríamos hacer en una sola línea.

¿Y si queremos obtener el sumatorio de ese array de valores? Pues tendríamos que hacer esto.

Aunque quedaría mucho mejor así.

Y por último una de las tareas más recurrentes, ordenar un array. Hay muchas formas y métodos, pero en esta ocasión pondré directamente la opción más rápida y elegante, usando el método sorted.

Todas estas operaciones sobre arrays también se pueden aplicar, como hemos indicado antes, a otros conjuntos como Dictionaries o Sets, y a Optionals.

Sin darnos cuenta hemos usado dos conceptos muy comunes en la programación funcional, vamos a explicarlos un poquito. Me voy a basar en un artículo de Aditya Bhargava, donde a través de unos dibujos explica unos conceptos básicos de la programación funcional, basándose en Haskell.

¿Cuál es el problema?

Tenemos un simple valor

Y una función para aplicárselo a ese valor

No hay ningún problema, todo perfecto, pero … ¿y si el valor no es tan simple y está dentro de un contexto?

En este caso puede ser o nuestro valor anterior o no. En swift esto sería nuestros famosos Optionals.

Por tanto, dependiendo del contexto podemos obtener un valor u otro y, por tanto, tenemos un problema.

¿Cómo arreglamos esto? Pues haciendo uso de map, que haciendo su magia nos permite operar con este tipo de objetos.

Entonces, ¿qué son los functors?

Functors no es más que un tipo que es implementado por la función map. En los casos que hemos visto, un array, dictionary y Optional serían functors. Un map aplica una función a un elemento encapsulado (functor) y devuelve otro valor encapsulado (otro functor), como podemos ver en la siguiente imagen.

En la siguiente imagen podemos ver de una forma muy gráfica como funciona cuando podemos extraer un valor correcto de su contexto.

Y cuando no es evaluable por la función lo que tenemos en nuestra “caja”.

A parte del ejemplo que hemos visto antes haciendo un map de un array, aquí podemos ver como hacer la misma operación a un Optional.

Vamos a exponer ahora el siguiente caso. Seguimos teniendo un objeto con contexto.

Pero en esta ocasión tenemos una función que también está con un contexto.

¿Cómo solucionamos esto? Existe en programación funcional la función apply, y a los tipos que la soportan se les llama Applicatives. Funcionaría de la siguiente manera.

Como swift no es una lenguaje funcional puro, no tiene implementada esta función, pero lo podemos solucionar de esta forma para los Optionals.

Vamos a ver el último caso. Seguimos teniendo un objeto con su contexto, pero ahora tenemos una función que devuelve otro objeto encapsulado, en este caso una función que devuelve la mitad de un número solo en el caso de ser múltiplo de dos.

Todo va bien cuando el valor que se le pasa a la función es solo eso, un valor.

Pero en cuanto que tiene contexto no es tan fácil que encaje.

En este caso usaríamos la función flatMap, y como en ocasiones anteriores, la definición de monad sería igual de sencilla, y no es más que un tipo que es implementado por flatMap.

Resumiendo como funciona, si la función nos devuelve nil para un valor tendríamos

Si a la función le pasamos un valor nulo, obtenemos el mismo resultado.

Y cuando devolvemos el valor esperado, lo obtenemos en su contexto y podemos aplicarle la función de nuevo.

Como podemos observar, un Array, un Dictionary o un Optional son tanto functors como monads en swift.

Continuando el ejemplo anterior, aquí podemos ver como aplicar un flatmap a un Optional.

Para terminar, podemos ver un ejemplo práctico de como aplicar todo esto a un caso real, y lo vamos a hacer con la inyección de dependencia. Para ello vamos a añadir un nuevo concepto llamado Reader Monad, que es más fácil explicarlo con un ejemplo.

Como vemos, creamos una clase donde le pasamos la función a aplicar en apply, map y flatMap en el init, con lo que nos proporcionaría inyectarle esa dependencia.

Imaginemos que tenemos una base de datos de usuarios y queremos desarrollar una función que actualice un usuario. A la hora de testearlo tenemos varios entornos, y nos gustaría de una forma fácil cambiar de entorno inyectándoselo al update. Aquí tendríamos el resultado.

Todo limpio y legible, como nos gusta.

La programación funcional da para mucho más, lo veremos en futuros artículos, aquí t beis un repositorio con todo el código que hemos visto aquí.

A la hora de escribir este artículo me he basado en mucho trabajo de otros, y aquí os dejo sus enlaces para que sigáis profundizando.