
Conozcamos qué condiciones debe tener un lenguaje para decir que soporta programación funcional y cuáles son las condiciones fundamentales para hablar de esta.
La unidad fundamental de trabajo en programación funcional con Swift es el closure. Conozcamos sus características, cómo se crea y trabaja con él.
El estado es la capacidad de un dato de mutar o no. La programación funcional, en su propia definición, apuesta por datos sin estado para permitir un flujo declarativo. Aprendamos el por qué.
La programación imperativa es la más habitual si somos desarrolladores en entornos Apple con Swift, pero vamos a ver en qué se diferencia de la declarativa y por qué esta última es un paradigma más eficiente.
Una de las condiciones que todo lenguaje que se considere funcional debe implementar. Funciones que reciben o devuelven otras funciones. Vamos a entender este concepto.
Los constructores de resultados o funciones para crear lenguajes específicos de dominio, es la última incorporación en Swift. Aprendamos cómo Apple desarrolló en su núcleo a SwiftUI.
Para que un lenguaje se considere funcional, tiene que permitir que las funciones sean ciudadanos de primera clase: que sean tipos de datos. Vamos a ver cómo lo hace Swift.
Si una función es un tipo de dato, también podrá ser enviada o devuelta por otra función como parámetro o como resultado. Veamos algunos ejemplos de cómo implementarlo esto y su utilidad.
El closure es la unidad de trabajo en programación funcional en Swift. La función anónima. Veamos cómo funciona y cómo convertir una función en un closure en unos pocos sencillos pasos.
Los closures usan la inferencia como herramienta fundamental. Podemos reducir, por lo tanto, su especificación a la mínima expresión y que, aún así, sigan teniendo sentido. Vamos a explorar un ejemplo práctico y entender cómo se aplica la inferencia en Swift y la utilidad de usar los closures de cierre cuando estos son el o los últimos parámetros de una llamada.
Una función es pura cuando siempre devuelve el mismo resultado al recibir los mismos parámetros de entrada: no tiene dependencias externas. La forma más óptima de trabajo con las funciones que nos aseguran su reutilización.
Funciones dentro de funciones. Anidándolas como elementos privados. Descubramos nuevas formas de estructuras nuestro código.
Si podemos tener funciones dentro de funciones, el siguiente paso es devolver funciones como resultado de una función. Con ello conseguimos un comportamiento dinámico muy cercano a las funciones matemáticas y sus distintos estados de representación.
Funciones que se llaman a sí mismas para obtener un resultado final. Un paradigma prohibido por muchos pero que en Swift tiene mucho sentido para conseguir operaciones que se retroalimentan de su propio resultado y acercarnos más a la formulación matemática.
Los árboles binarios son una forma muy optimizada de buscar contenidos y de organizar información. Veamos cómo implementarlos en Swift a través de enumeraciones recursivas y buscar en ellos, también explorándolos recursivamente en cada una de sus hojas o nodos.
¿Qué sucede si un closure que es recibido como parámetro en una función, no ejecuta dicha función si no que la guarda para después o la incluye en otro flujo? Decimos que escapa. Un concepto fundamental para entender cómo los closures capturan la semántica del contexto donde se ejecutan y cómo gestionar la memoria para evitar fugas.
Usamos recursividad y un caché de resultados para hacer que procesos, de por sí muy lentos, consigan una velocidad increíble que nos permita trabajar con algoritmos funcionales muy complejos en su desarrollo y sus llamadas a varios niveles.
La función de orden más alto que nos permite transformar cada elemento de una colección en base a un algoritmo funcional que le pasemos y que será aplicado a cada uno.
La función de orden más alto que nos permite filtrar los elementos de una colección en base a una comparación que devolverá verdadero o falso en cada elemento evaluado.
Reduzcamos todos los elementos de una colección a solo uno, mediante un algoritmo de agregación desde un resultado de inicio.
¿Cumplen todos los elementos de una colección con una condición específica? Sepamos si sí, o si no.
El map, pero preparado para trabajar con datos opcionales. Con un algoritmo aplicado a cada elemento, devolveremos opcionales y solo aquellos que no sean nil se incluirán (ya enlazados) en el resultado final.
¿Hay al menos un elemento en la colección que cumpla con la condición que pasemos en nuestro closure a esta función de orden más alto?
Obtengamos segmentaciones de los elementos de una colección, a partir que estos cumplan o dejan de cumplir una colección. Una forma de obtener los primeros o últimos elementos sobre nuestro closure entregado.
Recuperemos el primer o último elemento de una colección que cumpla un criterio concreto que le pasemos en el closure correspondiente.
¿Cuál es el último índice o el primero, dentro de la colección, para un elemento que cumpla una condición específica?
“Aplanemos” una colección transformándola. La forma de convertir matrices o colecciones de más de una dimensión a un resultado de dimensión única, transformando también sus resultados si lo necesitamos.
Recorramos como una enumeración convencional una colección, realizando una misma operación en base al algoritmo sumistrado, para cada elemento.
Veamos cómo ordenar los elementos de una colección en base a cumplir o no una condición, usando el método de la burbuja.
Borrado selectivo en base a una condición entregada en un closure. Algo extremadamente útil.
¿Cómo conectar los resultados de una función de orden más alto a otra? Consigamos realizar operaciones complejas concatenando un resultado a otro.
Los genéricos nos permiten crear una única función o estructura, para poder recibir cualquier tipo de dato como un “comodín”. Una forma de reutilización del lenguaje que nos lleva a otro nivel. Vamos a ver cómo funcionan en detalle.
Vamos a crear una llamada de URLSession, genérica, que nos sirva para cualquier tipo de llamada de cualquier tipo de contenido que queramos construir y recuperar. Todo con closures genéricos como parámetros de entrada.
En ocasiones, no todos los tipos de datos son susceptibles de ser usados para cualquier tipo de evaluación u operación. A través de los protocolos podemos asegurar que los genéricos aceptan determinados tipos para su correcto funcionamiento.
¿Componer funciones a través de operador para sumarlas como si fueran una única función? ¿Que la salida de una sea la entrada de la otra de forma automática? Puede hacerse de forma simple.
¿Cómo podemos integrar memorización de funciones automática y genérica para que sirva para cualquier algoritmo recursivo? Lo vemos en esta lección que lleva la programación a su máximo.
Los empaquetadores de propiedades son los elementos más versátiles y potentes que Swift ha agregado en los últimos año: propiedades que actúan como instancias de clases auto-instanciadas y que derivan la mutabilidad fuera de los structs.
Creamos nuestra propia estructura DSL y vemos cómo funcionan los constructores de resultados o de funciones, para ir un poco más allá en la forma de representar las llamadas.
Podemos incluir flujos de código como condicionales, bucles o sentencias case. Todo dentro de nuestro DSL para llevar sus posibilidades más allá.
La programación funcional es uno de los pilares del lenguaje Swift, un paradigma presente en muchos lenguajes, que en ocasiones no es entendido en toda su profundidad. De hecho, es posible que ya hayas usado algunos aspectos de la programación funcional sin saber que lo estabas haciendo, como cuando usas bloques de código o closures.
Con la programación funcional, conseguirás llevar tu código a otro nivel y resolver problemas con flujos que hasta ahora no conocías, mucho más allá de los simples bucles o condicionales que tanto usamos. Imagina: usar funciones que devuelven funciones o que reciben funciones, tratar a las colecciones de una manera práctica, potente e intuitiva con las funciones de orden más alto o dominar la programación genérica para crear tus propias APIs que te permiten reutilizar tu trabajo de la forma más eficiente.
Todo eso y mucho más, lo conseguirás en este curso que te abrirá las puertas de la programación funcional y todos sus conceptos más elementales, haciendo fácil lo difícil y, sobre todo, desde un punto de vista práctico para que no solo aprendas, también que sepas dónde y cómo aplicar lo aprendido.
Aprenderás todos los conceptos teóricos, a aplicarlos en la práctica, qué son las funciones puras, composición de funciones, operadores personalizados, memorización en funciones, árboles binarios, recursión, funciones anidadas o parcializadas... aprende todas y cada una de las funciones de orden más alto para colecciones como map, filter, reduce, compactMap o incluso las más específicas de Swift como prefix, firstIndex y sobre todo, a cómo usarlos en conjunto para obtener soluciones más eficientes. Cómo son los genéricos, cómo funcionan, como se aplican, condicionan o se ponen en práctica para maximizar la reutilización del código. Por último, cómo funcionan los constructores de resultados, empaquetadores de propiedades y con ello crear tu implementación DSL al más puro estilo SwiftUI y construir nuevas formas de representar tu código.
No esperes más y aprende el pilar fundamental de Swift y la base de toda su progresión en los últimos años. Un paso imprescindible tras aprender el lenguaje en "Aprendiendo Swift 5.4", antes de ponerte a desarrollar apps con UIKit o SwiftUI.