An Introduction to FP Through Lambda Calculus (JS)

Índice

Código en JavaScript del libro An Introduction to Functional Programming Through Lambda Calculus. Posiblemente (casi seguro al 100%) solamente los ejemplos pero no los ejercicios.

Introducción

Función Identidad

const id = x => x;

console.log(id(1) == 1);
console.log(id(id)(1) == 1);

Auto-aplicación

// Ya definido / `session` not supported for node.js
const id = x => x;

// Nuevo
const selfAp = s => s(s);

console.log(selfAp(id)(1) == 1)

La autoaplicación s(id) es igual a la aplicación anterior de id en id. Si expandimos:

s(id) *> (s => s(s))(id) *> s:id => s(s) *> id(id) *> (x => x)(id) *> x:id => x *> id

Con s:id quiero decir que s se va a sustituir por id.

Aplicación de una función

// Ya definido
const id     = x => x;
const selfAp = s => s(s);

// Nuevo
const apply = f => x => f(x);

console.log(apply(id)(1)    == 1);
console.log(apply(selfAp(id))(2) == 2);

Función de orden superior que recibe función, y devuelve otra función. Esta función de salida se encarga de aplicar la primera función que pasamos como argumento inicial al parámetro que se le pase.

Selección de elementos y Tuplas

// Ya definido
const id     = x => x;
const selfAp = s => s(s);
const apply  = f => x => f(x);

// Nuevo
const first    = x => y => x;
const second   = x => y => y;
const makePair = x => y => f => f(x)(y)

console.log(first(1)(2) == 1);
console.log(second(1)(2) == 2);
console.log(makePair(1)(2)(first) == 1);
console.log(makePair(1)(2)(second) == 2);

const makeTriplet   = x => y => z => f => f(x)(y)(z);
const tripletFirst  = x => y => z => x;
const tripletSecond = x => y => z => y;
const tripletThird  = x => y => z => z;

console.log(tripletFirst(1)(2)(3)  == 1);
console.log(tripletSecond(1)(2)(3) == 2);
console.log(tripletThird(1)(2)(3)  == 3);
console.log(makeTriplet(1)(2)(3)(tripletFirst)  == 1);
console.log(makeTriplet(1)(2)(3)(tripletSecond) == 2);
console.log(makeTriplet(1)(2)(3)(tripletThird)  == 3);

Condicionales, booleanos y números

Condicional

A partir del ternario es muy fácil definir el condicional

const cond = x => y => c => c(x)(y)

Booleanos

  // Ya definido
const id       = x => x;
const selfAp   = s => s(s);
const apply    = f => x => f(x);
const first    = x => y => x;
const second   = x => y => y;
const makePair = x => y => f => f(x)(y);

// Nuevo
const True  = first;
const False = second;

console.log(True(1)(2))
console.log(False(1)(2))

Operadores Lógicos

Not

Not nos permite elegir entre un par de valores False/True.

X ? False : True

Como espera un booleano, si es verdadero devuelve False, si recibe falso devuelve True. Con esto es muy fácil darse cuenta que simplemente necesitamos seleccionar el segundo cuando pasemos falso, y el primero cuando verdadero. Como True ~ first y False ~ second, entonces lo tenemos hecho.

// Ya definido
const cond = x => y => c => c(x)(y);
// Aquí expando en vez de hacer alias para mejor salida de la ejecución del código.
const True  = x => y => x;
const False = x => y => y;

// Nuevo
// x es un booleano, en ts sería
// const not = (x: bool) => x(second)(first)
// const not = x => x(second)(first)
const not = x => x(False)(True);

console.log('----');
console.log(not(True));
console.log(not(False));

And/Or

Para and, si la condición es verdadero devolvemos el segundo, y si es falsa podemos devolver o bien la misma condición (por ser falsa) o mejor falso directamente por si la evaluación fuese perezosa y no hubiese cacheado de resultados.

// Ya definido
const cond  = x => y => c => c(x)(y);
const True  = x => y => x;
const False = x => y => y;

// Nuevo
const and = x => y => x(y)(False)
const or  = x => y => x(True)(y)

console.log('----')
console.log(and(True)(False))
console.log(and(True)(True))
console.log(and(False)(True))
console.log(and(False)(False))
console.log(or(True)(False))
console.log(or(True)(True))
console.log(or(False)(True))
console.log(or(False)(False))

La traducción es directa desde los equivalentes en ternario, x ? y : false, x ? true : y.

Números

Se define el cero, y a partir de ahí el resto como sucesores del cero.

// Ya definido
const id    = x => x;
const first = x => y => x;
const second = x => y => y;
const True  = first;
const False = second;

// Nuevo
// básicos
const zero   = id;
const succ   = n => s => s(False)(n);
const isZero = n => n(first); // first

// derivados
const one = succ(zero);
const two = succ(one);

console.log('----');
console.log(isZero(zero)); // first ~ True
console.log(isZero(one));  // second ~ False
console.log(isZero(two));  // second ~ False