En este Tutorial Swift aprenderemos sobre las funciones curried o currificación de funciones. Un tema avanzado del cual se habla bastante poco en la documentación oficial y que personalmente me resulta muy interesante. Vale aclarar que el termino no está relacionado con la cocina y sí con el apellido del matemático norteamericano Haskell Curry.

El concepto que discutiremos hoy nos viene a mostrar como una posible solución ante ciertas necesidades de diseño pues el abogar por funciones que en lugar de aceptar varios argumentos,  implementan solo uno y devuelven otra función que aceptará también un solo argumento. Este proceso continúa hasta que todos los argumentos se agotan y nos quedamos con un único valor de retorno.

Bueno, dudo mucho de que esto que acabo de comentar haya dejado claro lo que intento explicar, así que veamos algunos ejemplos que progresivamente nos van a ir dando una mejor idea acerca de este enfoque.

Métodos de Instancia

Comencemos por explicar un comportamiento del que también se habla bastante poco y es que en Swift los métodos de instancia son funciones curried, veamos un ejemplo:

…en este ejemplo hemos declarado una clase llamada BankAccount la cual cuenta con un método llamado deposit cuya única función radica en sumar el deposito que se está efectuando con el balance actual de una cuenta bancaria. Como podemos observar es una clase y un método de instancia normal y corriente. Luego en la línea 15 creamos una instancia de esta clase, seguimos en la 17 con una llamada que hacemos al método deposit sobre la instancia account que acabamos de crear y pasamos como único argumento el número 100.

Hasta aquí todo bien, lo interesante comienza en la línea 19, en esta creamos una constante que igualamos al método deposit, si nos fijamos bien no estamos haciendo una llamada al mismo, no tiene sus paréntesis finales ni le estamos pasando ningún valor, lo que estamos diciendo al compilador de Swift es que almacene en depositor una referencia a este método.

Hasta aquí todo bien, en el artículo Tutorial Swift – El Tipo de una Función vimos que esto es posible por ende el tipo de la constante depositor debería ser:

…pero cuando presionamos la tecla option y click izquierdo sobre depositor el asistente de Xcode nos muestra lo siguiente:

…es decir que el tipo auto inferido de la constante depositor es una función que toma un solo parámetro en este caso de tipo BankAccount y devuelve una función que a su vez recibe también un solo argumento de tipo Double y que no retorna nada, como ven cumple con lo que explicamos al inicio sobre la definición de una función curried.

Ya conociendo esto pudiéramos agregar la siguiente línea final:

…donde  el primer parámetro es la instancia de tipo BankAccount y los segundos paréntesis corresponden a método deposit que esta llamada retorna, al que agregamos 100 dólares más. También (aunque no tendría sentido añadir una línea más) pudiéramos haber hecho lo siguiente:

…ambas soluciones tiene resultados similares, quizás esta última versión se más legible, pero ya esto lo dejo al análisis de cada cual.

Mi versión final de código sería:

…y la salida en pantalla:

Luego de explicada esta característica y ver el trabajo con clases, pues ahora veamos un ejemplo donde el uso de la currificación de funciones es una de las alternativas con las que contamos.

Currificación de funciones

En ocaciones es necesario trabajar con funciones que no están relacionadas con clases, pues sobre estas también podemos aplicar la currificación. Este ejemplo es bastante popular así que no se asombren por lo original de la elección, cumple con el objetivo de ilustrar y con eso me basta.

Imaginemos que queremos crear una función que acepta un solo parámetro y lo multiplica por 5. Esta función pudiera lucir como la siguiente:

Hasta aquí todo bien, ahora imaginemos que necesitamos una función similar pero ahora que multiplique por 10 en lugar de 5. Tiempo después quizás necesite otra que multiplique por 20 y así obligándolo a crear una función nueva por cada necesidad que tenga. Pero esto, como debes ya de imaginar, puede ser enfrentado con otro enfoque más óptimo, con la currificación de funciones.

Analicemos este nuevo ejemplo:

Hemos definido la función multiplyBy(_:) que acepta un Int como su único parámetro y retorna una función de tipo IntFunction, tipo que declaramos al inicio del ejemplo en pos de mejorar la legibilidad del código ya que en lugar de escribir:

…hemos optado por:

Luce mejor, verdad?

En esta función contamos con otra función anidada de nombre nestedMultiply(_:) y que será la encargada de la multiplicación una vez que cuente con los valores en sus dos operandos. Una de las razones por la que se anida esta función es para tener acceso a la variable a la cual representa la cantidad de unidades por la que multiplicaríamos un número, ese número que representa b y que sería el último para completar la operación.

Creo importante hacer notar que la función puede retornar teniendo el valor de b ya asignado o no, todo depende de como trabajemos con esta. Por ejemplo en la línea 15 del código anterior hacemos una llamada a la función y le pasamos tanto el multiplicador como el número a multiplicar. En la siguiente línea abogamos por otra opción donde creamos una constante llamada multiplyBy5 estableciendo en el nombre que el multiplicador será 5. Ya en este punto es más que evidente la mejora que hemos logrado con este nuevo enfoque, donde solamente pasando un valor en la llamada de la función estamos asignando un multiplicador sin necesidad de crear funciones independientes. En este caso el valor 5 corresponde a la variable a, es decir que la constante multiplyBy5 ahora almacena la función nestedMultiply que ya está establecida con el multiplicador 5 que se encuentra almacenado en la variable a para luego al ejecutar una llamada a esta constante en la línea 19 el valor que pasamos es el relacionado a la variable b y que será multiplicado por 5.

¿En que otro caso pudiéramos usar funciones curried?

Pues donde nos parezca correcto, como siempre digo dependerá mucho del diseño que estemos implementando y si esta sea la solución más optima, limpia y legible.

Para más información y ejemplos podemos dirigirnos a la documentación oficial de Swift (Inglés) en el sitio de desarrolladores de Apple.

Espero que todo cuanto se ha dicho aquí, de una forma u otra le haya servido de aprendizaje, de referencia, que haya valido su preciado tiempo.

Este artículo, al igual que el resto, será revisado con cierta frecuencia en pos de mantener un contenido de calidad y actualizado.

Cualquier sugerencia, ya sea errores a corregir, información o ejemplos a añadir será, más que bienvenida, necesaria!