in Functional Programming

¿Qué es la programación Funcional y porqué es importante?

Si estás relacionado en el mundo de desarrollo de iOS, probablemente te has topado con estos frameworks que Apple ha introducido en el último año:

SwiftUI: que nos proporciona un entorno de programación funcional para construir interfaces de usuario de forma declarativa en iOS y otras plataformas de Apple.

Combine: el nuevo framework de Apple de Programación Reactiva Funcional para el manejo de procesos asíncronos.

Y no podemos olvidar a:

RxSwift: mantenido por la comunidad, un framework que tiene como base “Programación Reactiva Funcional“.

Algo de lo que los desarrolladores iOS no podemos escapar es la parte funcional y si aún no has interactuado con estas herramientas al principio puede ser muy abrumante todos los nuevos conceptos tanto de la parte Funcional como la parte Reactiva 🤯


Es este post, abarco la parte funcional y cómo puedes usarlo en tu día a día cuando utilices Swift, la parte Reactiva la dejaré para otra entrada. Un paso a la vez. 🤓

Tabla de contenidos:

  1. Funciones de Primera Clase (First Order Functions)
  2. Funciones de Orden Superior (Higher Order Functions)
  3. Función Pura (Pure Function)
  4. Estado compartido (Shared State)
  5. Inmutabilidad
  6. Efectos Secundarios (Side Effects)
  7. Qué es la programación Funcional?
  8. De imperativo a Funcional

Swift es un lenguaje principalmente imperativo, pero en su diseño se han introducido conceptos modernos de programación funcional, exploremos algunos conceptos:

  1. Funciones de Primera Clase (First Order Functions)

En Swift podemos usar funciones como Tipos de datos, esto implica que podemos:

  • Asignar funciones a variables
  • Pasar funciones como argumentos
  • Retornar funciones como resultado de otra función
func incrementar(_ element: Int) -> Int {
  return element + 1
}

func potencia(_ element: Int) -> Int {
  return element * element
}

// #1:  Puedo asignar Funciones a Variables
let operacionIncremento = incrementar
let operacionPotencia = potencia
typealias operacion = (Int) -> Int

// #2: Puedo aceptar Funciones como parámetro de Entrada
func operaEnteros(_ element: Int, _ operacion: operacion ) -> Int {
  return operacion(element)
}
typealias operarEnteros = (Int) -> Int

// #3: Puedo retornar una función
func elegirOperacion(element: Int) -> operarEnteros {
  if element < 10 {
    return operacionIncremento
  } else {
    return operacionPotencia
  }
}

  1. Funciones de Orden Superior (Higher Order Functions)

Es una función especial que cumple al menos un de los siguientes requisitos:

  • Recibe una función como argumento o
  • Retorna una función

Este comportamiento ofrece un alto nivel de abstracción, ya que permiten abstraer no sólo valores sino acciones.

Swift ofrece funciones de Orden Superior llámese: map, filter, reduce.

  1. Función Pura (Pure Function)

Es una función que debe cumplir:

  • Dados los mismos argumentos siempre retorna los mismos valores.
  • No tiene efectos secundarios
  • No depende del estado externo, depende única y exclusivamente de los parámetros de entrada.

Puede parecer un concepto tan simple 🤷‍♂️, pero es una de las bases de la programación funcional.

Las Funciones Puras son extremadamente independientes, son fáciles de mover, refactorizar y reorganizar en el código, haciendo que los programas sean más flexibles y adaptables en el futuro.

Función No Pura 🚫

let isLoading = false

// Depende de valores externos a los parámetros

func duplicarElementos(_ array: [Int]) -> [Int] {
  if isLoading {
    return []
  } else {
    return array.map { $0 * 2 }
  }
}

Función Pura ✅

let animals = ["cow", "dog ", "pig", "bird"]

// Función Pura, depende SOLO de los argumentos
func capitalizar(_ animals: [String], _ except: String) -> [String] {
  return animals
    .map {
      if $0 != except {
        return $0.prefix(1).capitalized + $0.dropFirst()
      } else {
        return $0
      }
  }
}

let filters = capitalizar(animals, "pig")
// ["Cow", "Dog ", "pig", "Bird"]

⚠️ Advertencia ⚠️

No toda función por naturaleza puede ser pura.

  • Muchas veces no queda otra que invocar a funciones globales.
  • Tener dependencias implícitas
  • Depender de factores externos
  • I/O

  1. Estado compartido (Shared State) 

Es cualquier variable u objeto, que existe en un ámbito compartido o como propiedad de un objeto que se pasa entre ámbitos.

La programación funcional evita el estado compartido , en su lugar depende de estructuras de datos inmutables y llamadas a funciones puras para derivar nuevos datos existentes.

El gran problema con el estado compartido es que para comprender el funcionamiento de una función debes conocer el historial completo de cada variable compartida que la función utiliza o afecta

Necesitas saber de antemano cómo y cuántas veces ha sido llamado una función.

  1. Inmutabilidad

Un objeto es inmutable cuando no se puede modificar después de su creación

Swift provee de estructuras de datos inmutables por definición. 

Para evitar que una variable mute utilizamos “let”, esto permite al compilador de Swift que optimice el código.

La inmutabilidad garantiza que el código que escribimos no tenga efectos secundarios.

En Swift la mayoría de estructuras que provee son de Tipo Valor (Int, String, Array), esto implica que al asignarlos a una variable su valor es copiado.

Los tipos de valor son muy importante porque simplifican el comportamiento de compilador en la gestión de memoria.

Además no aumentan el valor del ARC, si no que se puede liberar la memoria cuando se elimina su ámbito actual.

  • ¿Entonces es muy costoso utilizar Tipos por Valor?

Por ejemplo copiar un arreglo de 1000 elementos cada vez que se asigne a otra variable.

Pero este no es el caso, ya que el compilador de Swift optimiza estas sentencias y solo realiza la copia en el momento en el que hay una modificación de las variables que comparten el array.

Esta técnica se llama Copy on Write

  1. Efectos Secundarios (Side Effects)

Es cualquier cambio de estado de la aplicación que realiza una función que no sea su valor de retorno:

  • Modificar una variable global o estática
  • Escribir en un archivo
  • Llamadas a red
  • Lanzar un proceso externo
  • Llamar a otras funciones con Efectos secundarios

Es imposible pensar que una aplicación no tenga efectos secundarios, es más construimos apps porque queremos Efectos Secundarios, queremos hacer pagos, persistir data en el dispositivo, usar la red, enviar un email.

Lo que hay que tener en cuenta es que debe ser aislada del resto del software. Si mantiene los efectos secundarios separados del resto de la lógica del programa, el software será mucho más fácil de entender, refactorizar, depurar, probar y mantener.

  1. ¿Entonces qué es Programación Funcional? 🤔

Es el proceso de construir software: 

  • Usando Funciones puras
  • Evitando el estado compartido
  • Evitando la mutación de los datos y 
  • Evitando los efectos secundarios.

El estado de la aplicación fluye a través de funciones puras, el pilar de la programación funcional es transformar un flujo de datos y como resultado retorna otro flujo de datos.

Como resultado el código tiende a ser más conciso, más predecible, por ende más fácil de probar.

  1. De imperativo a Funcional

Debemos recordar que el cambio de imperativo a funcional no es un juego de todo o nada, desde mi punto de vista, un enfoque más reservado sería empezar con algo pequeño, empezar a escribir Funciones Puras y utilizar las Funciones de Orden Superior siempre que tenga sentido (map, filter, reduce) e ir implementando poco a poco todos estos nuevos conceptos en el código que escribimos a diario.

Un inconveniente con el que podrías enfrentarte es así que debido a la memoria muscular, la próxima vez que te sientas a escribir código y te encuentres con problemas que has enfrentado antes, naturalmente utilizarás el mismo enfoque que has usado siempre para resolverlos.

Aplicar el enfoque funcional es un salto mental, pero vale la pena, tu código se volverá más fácil de probar, reutilizar y refactorizar.

Recuerda que el objetivo es crear funciones puras, sencillas y genéricas para luego componer funciones más complejas usando composición.

En el mundo real las aplicaciones que construimos mezclan ampliamente Programación Funcional, Orientada a Objetos o algún otro paradigma, personalmente escribo código funcional donde tiene sentido, tampoco debería forzar las cosas.

Espero que te animes a usar estos conceptos, que traspasan fronteras y son aplicables para distintos lenguajes: Kotlin, JavaScript, Python, Ruby, etc.

Referencias:

https://medium.com/javascript-scene/master-the-javascript-interview-what-is-a-pure-function-d1c076bec976

dotSwift 2018 – Paul Hudson – Elements of Functional Programming

https://medium.com/javascript-scene/composing-software-the-book-f31c77fc3ddc

https://www.swiftbysundell.com/basics/map-flatmap-and-compactmap/

https://www.hackingwithswift.com/example-code/language/what-is-copy-on-write

#LimaJS Programación Funcional por @lupomontero

Write a Comment

Comment