Capítulo 01: ¿Qué estamos haciendo exactamente?
Presentaciones
¡Hola! Soy el profesor Franklin Frisby. Un placer conocerte. Pasaremos un tiempo juntos mientras te enseño programación funcional. Basta sobre mí, hablemos de ti. Espero que conozcas JavaScript, tengas nociones de programación orientada a objetos y te consideres un programador práctico. No necesitas doctorado en entomología, solo saber detectar y corregir errores.
No asumo que tengas conocimientos previos de programación funcional (ya sabemos lo peligroso de asumir). Sí espero que hayas enfrentado problemas con estados mutables, efectos secundarios descontrolados y diseño sin principios. Presentaciones hechas, comencemos.
Este capítulo busca mostrarte la esencia de la programación funcional. Para entender los siguientes capítulos, debemos comprender qué hace funcional a un programa. De lo contrario escribiremos código sin rumbo. Necesitamos objetivos claros y principios que nos guíen en la tormenta.
Existen principios generales en programación - acrónimos que nos orientan: DRY (no repetir código), YAGNI (no implementar lo innecesario), bajo acoplamiento/alta cohesión, principio de mínima sorpresa, responsabilidad única, etc.
No enumeraré todas las guías que conozco... Lo importante es que aplican en programación funcional, aunque son secundarias a nuestra meta final. Antes de profundizar, quiero que sientas nuestra intención al programar: nuestro paraíso funcional.
Un breve encuentro
Comencemos con un ejemplo cuestionable: una aplicación de gaviotas. Cuando las bandadas se unen crecen, y al reproducirse aumentan según el número de parejas. Este no es buen código orientado a objetos, sino una demostración de los peligros del enfoque basado en mutación:
class Flock {
constructor(n) {
this.seagulls = n;
}
conjoin(other) {
this.seagulls += other.seagulls;
return this;
}
breed(other) {
this.seagulls = this.seagulls * other.seagulls;
return this;
}
}
const flockA = new Flock(4);
const flockB = new Flock(2);
const flockC = new Flock(0);
const result = flockA
.conjoin(flockC)
.breed(flockB)
.conjoin(flockA.breed(flockB))
.seagulls;
// 32¿Quién crearía tal aberración? ¡Es imposible seguir el estado mutable interno! Y el resultado está incorrecto: debería ser 16, pero flockA fue alterado permanentemente. ¡Esto es anarquía informática! ¡Aritmética salvaje!
Si no entiendes este programa, es normal. El punto es que el estado mutable es difícil de seguir, incluso en ejemplos pequeños.
Intentemos un enfoque funcional:
const conjoin = (flockX, flockY) => flockX + flockY;
const breed = (flockX, flockY) => flockX * flockY;
const flockA = 4;
const flockB = 2;
const flockC = 0;
const result =
conjoin(breed(flockB, conjoin(flockA, flockC)), breed(flockA, flockB));
// 16Ahora el resultado es correcto con menos código. El anidamiento de funciones es algo confuso (lo corregiremos en el Capítulo 5). Al analizar más profundamente, veremos que usamos suma (conjoin) y multiplicación (breed).
Estas funciones solo tienen nombres especiales. Redefinámoslas como multiply y add para revelar su esencia:
const add = (x, y) => x + y;
const multiply = (x, y) => x * y;
const flockA = 4;
const flockB = 2;
const flockC = 0;
const result =
add(multiply(flockB, add(flockA, flockC)), multiply(flockA, flockB));
// 16Así descubrimos los principios matemáticos ancestrales:
// associative
add(add(x, y), z) === add(x, add(y, z));
// commutative
add(x, y) === add(y, x);
// identity
add(x, 0) === x;
// distributive
multiply(x, add(y,z)) === add(multiply(x, y), multiply(x, z));Sí, las propiedades matemáticas básicas son útiles. Tranquilo si no las recuerdas, muchos las aprendimos hace tiempo. Usémoslas para simplificar nuestro programa:
// Original line
add(multiply(flockB, add(flockA, flockC)), multiply(flockA, flockB));
// Apply the identity property to remove the extra add
// (add(flockA, flockC) == flockA)
add(multiply(flockB, flockA), multiply(flockA, flockB));
// Apply distributive property to achieve our result
multiply(flockB, add(flockA, flockA));¡Excelente! No necesitamos código personalizado excepto la función principal. Incluimos add y multiply por completitud, pero existen en bibliotecas estándar.
Podrías pensar "qué ejemplo tan artificial" o "los programas reales son más complejos". Elegí esto porque todos conocemos sumas y multiplicaciones, demostrando cómo las matemáticas nos ayudan.
No te desanimes. En este libro usaremos teoría de categorías, teoría de conjuntos y cálculo lambda con ejemplos reales tan elegantes como el de las gaviotas. No necesitas ser matemático, será natural como usar cualquier API común.
Sorprendentemente, podemos crear aplicaciones completas con este enfoque funcional. Programas con propiedades sólidas, concisos y comprensibles. La anarquía es útil para criminales, pero en este libro seguiremos las leyes matemáticas.
Usaremos teorías donde todo encaja armoniosamente. Representaremos problemas específicos con componentes genéricos y composables, aprovechando sus propiedades. Requiere más disciplina que la programación imperativa, pero las recompensas te sorprenderán.
Hemos vislumbrado nuestra estrella polar funcional, pero debemos entender algunos conceptos clave antes de continuar.