Programación funcional en JavaScript

Closures

Las clousures son funciones internas que acceden a variables locales de las funciones externas en las que se encuentran definidas.

Los clousures tienen algunas ventajas como: encapsulación, ocultación de datos, reutilización de código, etc.

function funcionExterna(par1) {
    var miVariableLocal = 'un valor';

    var miFuncionInterna = function () {
        return par1 - miVariableLocal; // Situación que genera el closure
    }

    return miFuncionInterna;
}

Al invocar a la función externa funcionExterna se genera el closure miFuncionInterna y este guarda la referencia de las variables en el momento de su creación. De esta manera, cada closure que se crea recuerda los valores de las variables con que se creó.

function crearSuma(x){
    // clousure
    function suma(y){
        return x + y;
    }
    return suma
}
var sumar5 = crearSuma(5);
var sumar10 = crearSuma(10);
console.log(sumar5(15));
// output: 20
console.log(sumar10(15));
// output: 25

Programación tácita o de punto libre (point free)

La programación tácita es un paradigma de programación donde nos olvidamos de los argumentos de una función. Es una técnica usada en la composición de funciones.

const suma = (x, y, cb) => {
    cb(x + y);
}

const imprimirResultado = (resultado) => {
    console.log(resultado);
}

suma(2, 3, (resultado) => imprimirResultado(resultado));

En el ejemplo anterior estamos creando una función anónima para llamar a la función de imprimirResultado. Esto es innecesario y podemos simplificarlo con la programación tácita quedando así:

const suma = (x, y, cb) => {
    cb(x + y);
}

const imprimirResultado = (resultado) => {
    console.log(resultado);
}

suma(2, 3, imprimirResultado);

Currying

Es la técnica que transforma las funciones en unarias; funciones que solo reciben un argumento.

Con Curry podemos llamar a una función con menos parámetros de los esperados. Esta a su vez devolverá una función que espera los parámetros restantes y finalmente devolverá un resultado.

👉  Actualizar automáticamente funciones "import" en Google Sheets

En el siguiente código, la función resta requiere dos argumentos.

const resta = (a, b) => a - b;

const resultado = resta(10, 4);
console.log(resultado);
// output: 6

En el supuesto que de inicio no tuviéramos disponibles los dos valores para los argumentos de resta, no podríamos llamar a la función. Aquí es donde entra Currying transformando la función para que solo tenga un parámetro.

const resta = a => b => a - b;

const resta10 = resta(10);
const resultado = resta10(4)
console.log(resultado);
// output: 6

Composición de funciones

Cuando tenemos un problema complejo conviene dividirlo en pequeños problemas que sean sencillos de resolver para luego combinar estas pequeñas soluciones.

La composición de funciones se basa en esta idea, crear funciones simples para construir funciones complejas mediante combinación: el resultado de cada una de estas funciones simples será la entrada de otra función.

Para combinar las funciones crearemos otra función que se encargará de ello. También podemos optar por utilizar librerías como Ramda.

Ventajas de usar composición de funciones:

  • Descomponer un problema complejo en partes más simples
  • Escribir código más elegante y mantenible
  • Facilitar el testing
En la composición de funciones la lectura es de derecha a izquierda o de adentro hacia afuera.

En el siguiente ejemplo aplicaremos sobre un array de objetos una serie de funciones mediante composición para obtener un texto personalizado con el jugador mejor pagado. Otra función, compose , será la encargada de aplicar la composición de funciones.

const players = [
    { age: 20, name: 'João Félix', surname: 'Sequeira', salary: 100 },
    { age: 28, name: 'Leroy', surname: 'Sané', salary: 100 },
    { age: 28, name: 'Neymar', surname: 'da Silva Santos', salary: 160 },
    { age: 28, name: 'Luis', surname: 'Suárez', salary: 40 },
    { age: 28, name: 'Zlatan', surname: 'Ibrahimovic', salary: 3.5 },
    { age: 28, name: 'Lionel', surname: 'Messi', salary: 140 }
];

// función que realizará la composición de funciones
const compose = (...fns) => x => fns.reduceRight((y, f) => f(y), x);

const orderBySalaryDesc = xs => xs.sort((a, b) => b.salary - a.salary);
const first = xs => xs[0];
const format = x => `${x.name} ${x.surname} gana ${x.salary}€ al año`;

const bestPaid = compose(
    format,
    first,
    orderBySalaryDesc
);

console.log(bestPaid(players));
// output: Neymar da Silva Santos gana 160€ al año

👇Tu comentario