Introducción a la programación funcional con JavaScript

logo

Introducción a la programación funcional

Con este post pretendo dar pie a una serie de articulos sobre progracion funcional en javascript, mostrar sus ventajas frente a otros paradigmas como la programacion imperativa y como podemos sacarle el maximo provecho utilizando javascript como lenguaje funcional.

Pero antes de empezar, que es eso de programacion funcional?

La progracion funcional es simplemente otro enfoque a los problemas comunes del codigo, es otra forma de pensar sobre el codigo utilizando bases matematicas bien definidas y ampliamente probadas. En progracion funcional no necesitamos decirle a la computadora como tiene que hacer las cosas sino que simplemente le tenemos que decir que es lo que queremos obtener, no como obtenerlo. Pongamos un ejemplo:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var users = [
{name: "carlos", age: 21 },
{name: 'marcelo', age: 30},
{name: 'mikaela', age: 25},
{name: 'cecilia', age: 37},
];
var getUsersName = function(users) {
var _names = [];
for (var i=0; i < users.length; i++) {
_names.push(users[i].name);
}
return _names;
}
var names = getUsersName(users);
console.log(names);

Este codigo te suena conocido?, pues es algo muy comun de hacer bajo la mano opresora de la programacion imperativa, lo que hacemos aqui es simplemente obtener la propiedad nombre de una lista de usuarios. Ahora una pregunta ¿Te ha sido facil saber que hace la funcion con solo darle un vistazo?. pues a mi francamente no. Como vemos el objetivo es simple, pero al estar programado de modo imperativo implica que tengamos que decirle paso a paso como obtener el resultado inflando asi el codigo mas de lo necesario. Basicamente lo que estamos diciendole aqui es:

  1. genera una funcion llamada getUsersName que acepte una lista de usuarios como argumento.
  2. declara un array llamado _names que este vacio
  3. itera sobre cada elemento de la lista de usuarios.
  4. por cada usuario obtene la propiedad name y agregala al array _names
  5. retorna el array _name.

¿no te parece demasiado lio para obtener simplemente una propiedad?. Veamos como podemos lograr lo mismo usando la programacion funcional:

1
2
nombres = users.map(function(user) { return user.name });
console.log(nombres);

¿que estamos diciendole aqui?, dada una lista de usuarios, quiero el nombre de cada uno.

Fijemonos en la diferencia. La version imperativa es mas similar a una receta de cocina donde debemos decir en cada paso lo que hay que hacer para lograr el objetivo, en cambio en la version funcional pareciera mucho mas sencillo entender a primera vista lo que hace, de la lista de usuarios, dame la propiedad name.. Parece algo tonto pero tiene mucho ese cambio de expresion, una cosa es decir lo que queremos y otra como lo queremos. Esa forma de programar se conoce como programacion declarativa ¿porque?, pues porque declaramos lo que queremos obtener, no como queremos que haga para obtenerlo.

Al contrario que la progracion imperativa, la programacion funcional tiene fuertes raices matematicas que analizaremos en su momento (no te preocupes que no es nada complicado), la cual dota al codigo de unas cuantas propiedades matematicas utiles.

¿Es cierto que no se usan variables en la programacion funcional?.

Pues no, si se utilizan variables pero no como las entendemos en imperativo, las variables en funcional las podemos entender como constantes que al tomar un valor lo mantiene durante toda la ejecucion. Si analizamos una vez mas la funcion anterior podemos ver como las variables i y name van cambiando su valor a lo largo de la ejecucion.

1
2
3
4
5
6
7
var getUsersName = function(users) {
var _names = [];
for (var i=0; i < users.length; i++) {
_names.push(users[i].name);
}
return _names;
}

Esto en funcional no esta permitido, de echo en lenguajes de programacion funcionales puros no existe el for ni el while ni nada parecido, pero hablaremos de ello mas adelante. Lo interesante es que las variables si las podemos usar pero siempre intentando reducirlas al minimo y nunca pero nunca usarlas de forma global.

Las funciones

En funcional, las funciones deben de ser cortas y hacer una sola tarea (de echo lo ideal seria comenzar cada funcion con un return), la idea detras de esto es la reutilizacion la cual como veremos mas adelante brilla por su presencia en la programacion funcional, ademas se intenta mantener las funciones “puras”, ¿que significa eso de “puras”?, lo explico por encima pero lo veremos en mas detalle en proximos articulos.

Una funcion pura es aquella que dada los mismos argumentos de entrada siempre produce la misma salida, tampoco depende del estado del sistema y no produce efectos colaterales como pueden ser mostrar algo en la consola, o guardar un registro en una base de datos, etc… ¡tranquilo si podemos usar esas funciones pero de otra manera!.

1
2
3
4
5
6
7
8
var _names = [];
var getUsersName = function(users) {
for (var i=0; i < users.length; i++) {
_names.push(users[i].name);
}
return _names;
}

Si miramos nuesta funcion getUsersName (modificada) vemos que es impura porque depende del estado general del sistema, en este caso del array _names definido fuera de la funcion, si necesitaramos poder referenciar ese array, deberemos pasarlo como argumento.

Por ahora eso es todo, este post no pretende explicar ni por encima la programacion funcional, sino que prentende ser lo que es, una introduccion a una serie de futuras publicaciones sobre programacion funcional en javascript que espero sean de tu agrado.

Comentarios

Composicion funcional

Composición funcional en javascript.

La programación funcional dispone de numerosas técnicas bien documentadas para trabajar con funciones, ya hemos visto unas cuantas en artículos previos en este blog, hoy le toca el turno a la composición funcional, una de las herramientas mas poderosas de la programación funcional.

Recapitulando.

Nosotros sabemos bien como generar una función en javascript, por ejemplo:

1
2
3
function saludar(name) {
return "Hola " + name;
}

¿Simple no?, y seguramente todos sabemos como ejecutar esta función:

1
saludar("gustabo"); // ==> Hola gustabo

Si lo admito, esta función es bastante aburrida por si misma, vamos a mostrar ese saludo por la consola:

1
console.log(saludar("gustabo"));

funciona, vamos a saludar a pedrito y ya que estamos (y para no repetir código), vamos a crear una función que nos abstraiga de tener que escribir ese console.log en todos lados. :

1
2
3
4
5
6
7
8
9
10
function saludar(name){
return "Hola " + name;
}
function mostrarSaludo (name) {
console.log(saludar(name));
}
mostrarSaludo("martin");
mostrarSaludo("pepito");

Y obviamente funciona, pero vamos un poco mas allá y mostremos nuestros saludos en un alert:

1
2
3
function alertSaludo (name) {
alert(saludar(name));
}

mmm algo no esta bien, ¡hemos tenido que crear 2 funciones para saludar por distintos medios a una persona!, eso no parece muy re utilizable y optimizado. ¿Porque ha pasado esto?, pues por la forma de componer las funciones que tiene el paradigma imperativo, function1( function2 ( function3 (argumentos...)));

Como bien sabemos, básicamente lo que estamos haciendo es una tubería con nuestros datos (un pipe para los usuarios de unix y derivados), el resultado de aplicar la funcion3 se lo pasamos como argumento de entrada a nuestra funcion2 y el resultado se lo pasamos como argumento de entrada a la funcion1. Esta forma de “combinar funciones” si bien es muy utilizada no es la única ni tampoco la mas eficiente ya que como vemos nos obliga a tener que crear toda una nueva función cada vez que algo cambia como en el ejemplo anterior.

Presentando a compose

Vamos a abstraer esta forma de componer nuestras funciones y vamos a darle un nombre ¿que tal compose?.

1
2
3
4
5
function compose(f,g) {
return function (x) {
return f(g(x));
}
}

Compose se lee de adentro hacia afuera, en este caso x es el argumento y g y f son las funciones que queremos componer juntas. Vamos a probar nuestro nuevo invento y a re definir nuestras amables funciones de saludos.

1
2
3
4
5
6
7
8
var saludarPorConsola = compose(console.log, saludar);
var saludarPorAlert = compose(alert, saludar);
saludarPorConsola("martin");
saludarPorConsola("pepito");
saludarPorAlert("martin");
saludarPorAlert("pepito");

Fantástico! hemos logrado reducir bastante la duplicidad del código y hasta hemos mejorado la legibilidad!. Es mas sencillo y cómodo tanto para generar nuestras funciones, como para leer y saber de un vistazo que es lo que hacen.

Básicamente esto se lee como:

compose(console.log, saludar); Es la composición de saludar con un console.log.

Nosotros solamente tenemos que tomar 2 funciones que nos interese combinar y componerlas juntas para obtener otra función, ¿genial no?. Esto tiene mucho sentido puesto que tomar 2 unidades de un mismo tipo nos debe de devolver una unidad de ese mismo tipo, en este caso una función. Si juntamos 2 ruedas no obtenemos una casa, hay una teoria matematica detras de esto y la veremos en breve. Ej:

1
2
3
4
5
1 + 2 == 3 // ==> number
"Hola " + "pepito" == "Hola pepito" // ==> string
compose(console.log, saludar) == function(x) { return console.log(saludar(x)); } // ==> function

Hagamos un par de ejercicios con nuestra flamante función compose:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
/* ************* Funciones de utilidad ****************/
function head(vector) { return vector[0]; }
function uppercase (str) { return str.toUpperCase(); }
function exclaim (str) { return str + "!"; }
function reverse (lista) { return lista.reduce(function(acc, x) { return x.concat(acc); }); }
/****************************************************************************/
var last = compose(head, reverse);
var showLastName = compose(console.log, last);
var showLastNameExclaim = compose(console.log, compose(exclaim, last));
showLastName(["carlos", "martin", "gustavo"]); // ==> gustavo
showLastNameExclaim (["carlos", "martin", "gustavo"]); // ==> gustavo!
var addExclaim = compose(exclaim, exclaim);
var showLastNameAndAddExclaim = compose(console.log, compose(addExclaim,last));
showLastNameAndAddExclaim(["carlos", "martin", "gustavo"]); // ==> gustabo!!
var upperExclaimLast = compose(uppercase, compose(exclaim ,last));
var showUppername = compose(console.log, upperExclaimLast);
var alertUpperName = compose(alert, upperExclaimLast);

¿Que clase de brujera es esta?

Si si, así me quede yo cuando conocí esta técnica. Como vemos la reutilización de funciones y el principio DRI (don’t repeat yourself) se potencia al máximo al componer las funciones de esta manera. Ya que estamos creando nuevas funciones reutilizando funciones mas pequeñas y abstractas.

La función reverse invierte una lista y la función head toma el primer elemento de una lista, si lo combinamos juntos obtenemos una flamante función last con muy poco código.

Como vemos componer funciones es algo sencillo e intuitivo, aunque hay un par de puntos que aclarar.

  • El orden de composición si importa, en este ejemplo componemos nuestras funciones desde la derecha hacia la izquierda.

  • Las funciones a componer deberían de ser puras.

  • Las funciones a componer deberían de ser suficientemente abstractas como para solucionar una familia de problemas y no un problema en especifico.

Como la función compose viene directamente de las matemáticas, existe una propiedad que deberiamos conocer.

Asociatividad:

compose (f, compose( g, h)) es igual a compose (compose(f,g) , h );

Esto quiere decir que no importa como agrupemos nuestras llamadas a compose ya que el resultado siempre sera el mismo. Osea que si queremos capitalizar una cadena podemos hacer:

1
2
3
4
compose(uppercase, compose(head, reverse));
// o sino
compose(compose(uppercase, head), reverse);

El resultado es el mismo.

Existen librerias como ramda que tienen implementada la funcion compose con curring, lo que nos permite componer varias funciones juntas, con lo cual podriamos hacer:

1
compose(uppercase, head, reverse);

sin importar la cantidad de funciones juntas que deseemos componer (ventajas de la propiedad asociativa).

Pero de donde viene todo esto?

La teoría de categorías

En la teoría de categorías tenemos algo que se llama una categoría, la cual esta definida como una colección de cosas con los siguientes componentes:

  • Una colección de objetos

  • Una colección de morfismos

  • Una forma de componer los morfismos.

Vamos por partes.
una colección de objetos:
Los objetos los podemos ver como tipos de datos, como number, Boolean, String, Object etc etc.. Se suelen ver estos objetos como un conjunto de todos los posibles valores. Por ejemplo uno puede ver un String como un conjunto de todas las combinaciones alfanuméricas posibles, o un Bool como un conjunto de True False o number como un conjunto de todos los valores numéricos enteros posibles etc…, Tratar los tipos como conjuntos es útil porque podemos utilizar la teoría del conjunto con ellos.

Una colección de morfismos:
Los morfismos, no son mas que nuestras funciones puras de todos los días.

Una forma de componer en los morfismos

Esto como te daras cuenta, es nuestra función compose. Compose es asociativa puesto que es una propiedad necesaria para cualquier composición de la teoría categórica.

Comentarios

Funciones de orden superior

logo

Funciónes de orden superior en javascript

La base de la programación funcional.

Las funciónes de orden superior, son funciones capaces de:

  • Tomar una función como parametro
  • Retornar una función.

Una función se dice que es funcional si toma una funcion como argumento.
Las funciónes de orden superior forman la columna vertebral de la programación funcional.

Creando closures.

El dominio de los closures es clave para dominar JavaScript y la programación funcional en este lenguaje. Los closures estan por todas partes en JavaScript, y no se podria hacer mucho de una manera funciónal sin ellas. Veamos un ejemplo:

1
2
3
4
5
6
7
8
var showName = function() {
var name = "FP JavaScript"
return function() {
console.log(name)
}
}()
showName() //==>> FP JavaScript
console.log(name) //==>> Reference Error

Hemos creado y ejecutado inmediatamente una función anonima.

1
2
3
function() {
}()

y asignado el valor de retorno a showName. showName es asignado a la función.

1
2
3
function() {
console.log(name)
}

Tambien hemos creado un nombre de variable en el mismo scope que la función anterior. La variable es visible dentro de la función, por eso la función puede imprimir su valor.

Fuera de la función que se ejecuto inmediatamente, la variable name es undefined. Obtendremos un error al tratar de acceder a ella. Sin embargo, podemos imprimir el nombre, llamando a showName. A pesar de que la función que ha creado la variable nombre a sido ejecutada y retornada, name sigue siendo accesible desde dentro de showName.

En efecto, hemos creado un closure con una variable name para la función showName. Podemos hacer lo que queramos con showName. Pasarlo a otra función, o lo que sea que deseemos. podemos contar con nuestro closure fielmente.

Para los que vienen de lenguajes como C o c++, un closure es básicamente un puntero, que la función lleva consigo. Este puntero apunta a una tabla de todas las variables que fueron creadas en ese scope.

Un closure se crea cada vez que una función retorna otra función definida dentro de ella.

la creación de closures debe ser algo natural en un programador JavaScript. De no ser asi, deberia seguir practicando closures, ya que vamos a hacer una gran cantidad de closures en este articulo y en posteriores. Usted debe ser capaz de reconocerlos facilmente, puesto que no siempre los mencionaremos expicitamente.

Fabrica de funciónes

Considere el siguiente codigo:

1
2
3
4
5
6
7
8
9
10
function add(x) {
return function(y) {
return x + y
}
}
var add2 = add(2)
var add3 = add(3)
add2(4) //==>> 6
add3(6) //==>> 9

La función add toma una simple variable x y retorna una función, que a su vez toma una variable y y retorna la suma de x e y.

add es una fabrica de funciónes. Dado un valor, se crea una función que acepta un argumento, y añade el valor de su argumento a un valor previamente almacenado en su clousure.

¿Entonces, porque no hemos escrito simplemente add(x,y)? Esto seguramente seria mas simple. Escribir funciones como “fabrica de funciones” nos permite componer fácilmente nuevas funciones. Veremos mas sobre este tema más adelante, cuando hablemos sobre la composición funcional.

Vamos a ver algunas de las funciones comunes que se escriben usando el patron function factory.

get

1
2
3
4
5
function get(prop) {
return function(obj) {
return obj[prop]
}
}

Lo interesante de esta función, es que no “obtiene” por asi decirlo. Esto simplemente retorna otra función.

1
var getName = get(“name”)

getName se define como una función retornada por get. Pero antes de retornar nada, get crea un closure con la variable prop (de property) para getName. Y la propiedad se establece en nombre.

1
2
3
4
var book = {
name: “Functional JavaScript”
}
getName(book) //==>> Functional JavaScript

Podemos usar get con arrays tambien.

1
get(1)([1, 2, 3]) //==>> 2

Esta es la forma en la que consiguen trabajar los lenguajes funcionales. Esto es muy util cuando componemos esto con otras funciónes. Tambien es util si necesitamos acceder a la misma propiedad en un conjunto de objetos. Veremos todo esto en acción mas adelante.

map

Vamos a reescribir la función de arrays JavaScript map para que trabaje de manera mas funcional.

1
2
3
4
5
6
7
function map(fn) {
return function(arr) {
return Array.prototype.map.call(arr, fn)
}
}
map(function(x) {return x * x}) ([1, 2, 3]) //==>> [ 1, 4, 9 ]

Note que el orden de los argumentos han sido cambiados. La función viene primero y luego el array. Tambien hemos utilizado Array.prototype.map.call en lugar de simplemente haber llamado a arr.map. Esto es asi para que podamos utilizar nuestra función map con arrays al igual que con objetos, argumentos y el DOMNodeList.

Digamos que queremos conseguir todos los emails desde una lista como esta en el array:

1
2
3
4
5
var people = [ {name:”John”, email:”john@example.com”},
{name:”Bill”, email:”bill@example.com”} ]
map(get(‘email’)) (people)
//===>> [ ‘john@example.com’, ‘bill@example.com’ ]

Usted es capaz de ver la ventaja de escribir get de esta forma. Podemos pasar esto como argumento a otra función. Hemos compuesto get con map. A continuacion vamos a ver algunas ventajas de escribir map de esta manera.

pluck

El patron map(get()) que vimos anteriormente, es tan comun que tenemos una función para esto llada pluck.

1
2
3
4
5
6
function pluck(prop) {
return map(get(prop))
}
pluck(‘email’)(people)
//===>> [ ‘john@example.com’, ‘bill@example.com’ ]

pluck es una composición entre las funciones map y get(prop). la composición trabaja de derecha a izquierda.

map retorna una función que requiere un array. Pero tambien ha establecido que su función requerida sera get(prop) en su closure.

forEach

1
2
3
4
5
function forEach(fn) {
return function(arr) {
Array.prototype.forEach.call(arr, fn)
}
}

Esto funciona igual que nuestra función map anterior. excepto que simplemente itera sobre un array, y no retorna ningun valor. Esto tambien funcióna con un DOMNodeList.

un patron común que nos encontramos cuando desarrollamos del lado del cliente es la iteración a trabes de un NodeList retornada por document.querySelectorAll. Para mejorar esto podemos escribir una función de orden superior generica para que itere a trabes de un NodeList dado un selector.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<!DOCTYPE html>
<html><body>
<div>Ocultame</div>
<div>Ocultame tambien.</div>
<script>
function forEach(fn) {
return function(arr) {
Array.prototype.forEach.call(arr, fn)
}
}
var displayNone = forEach(function(elem) {
elem.style.display = “none”
})
displayNone(document.querySelectorAll(“div”))
</script>
</body></html>

take

take toma un numero n y retorna una función, a la que debe pasar un array, para obtener los primeros n elementos del array.

1
2
3
4
5
function take(n) {
return function(arr) {
return arr.slice(0, n)
}
}

flip

flip toma una función de 2 o mas argumentos y retorna una función con el primero y segundo argumentos cambiados.

1
2
3
4
5
6
function flip(fn) {
return function(first, second) {
var rest = [].slice.call(arguments, 2)
return fn.apply(null, [second, first].concat(rest))
}
}

flip es especialmente util, cuando se necesita aplicar parcialmente una función dada y ordenar sus argumentos en el orden deseado.

memoize

La memorización es el proceso en el que una función cachea sus resultados. y retorna esos resultados desde la cache, si la función fue llamada previamente con los mismos argumentos.

1
2
3
4
5
6
function memoize(fn) {
var cache = {}
return function(arg) {
return (arg in cache) ? cache[arg] : (cache[arg] = fn(arg))
}
}

Esto es memoize para una función que toma un unico argumento. Esto retorna una función con un objeto cache en su closure. Cada vez que la función es llamada, checkea la cache para el argumento. Si lo encuentra, entonces retorna el valor correspondiente. En cambio si no lo encuentra, establece la cache con el argumento y el resultado, y retorna el resultado.

memoize es realmente muy poderoso para optimizar calculos largos, o operaciones recursivas caras.

La serie de fibonacci, es un ejemplo muy usado para demostrar la función memoize. Debido a que el computo involucrado crece exponencialmente cuanto mayor sea el numero a calcular en la serie.

Cada numero en la serie de fibonacci es la suma de los 2 numeros previos ej: eg. 1,1,2,3,5,8,13,21 .. etc. Y podemos escribir esto como:

1
2
3
function fib(n) {
return n < 2 ? 1 : fib(n — 1) + fib(n — 2)
}

Y si corremos algunas pruebas, obtenemos…:

1
2
3
4
5
6
7
8
var fibmemo = memoize(fib)
var start = new Date()
fibmemo(20)
console.log(new Date() — start) //==>> 11 ms en mi maquina
start = new Date()
fibmemo(20)
console.log(new Date() — start) //==>> 0 ms

Observe que la ejecución de fibomemo(20), la segunda vez tomo solo o ms, ya que solo devuelve el valor almacenado en la cache.

Tenemos que modificar memioze para manejar las funciones con multiples argumentos. Por fortuna esto es bastante sencillo.

1
2
3
4
5
6
7
8
function memoize(fn) {
var cache = {}
return function() {
var args = Array.prototype.slice.call(arguments)
return (args in cache) ? cache[args] :
(cache[args] = fn.apply(null, args))
}
}

Primero convertimos los argumentos pasados a un array en args. Entonces args es un array y es usado en el contexto de una clave de objeto (hash), esto sera coaccionado dentro de un string. Segundo, ahora podemos aplicar la matriz a la función .

once

oncee crea una función que solo puede ejecutarse una unica vez. Y las invocaciones posteriores, siempre retornaran el primer resultado.

1
2
3
4
5
6
7
function once(fn) {
var cache = {}
return function() {
return (“once” in cache) ? cache[“once”] :
(cache[“once”] = fn.apply(null, arguments))
}
}

Espero sinceramente, que el articulo haya sido de utilidad, para aquellas personas que no conocian el poder de este tipo de funciónes. Saludos :)

Comentarios

Functores paso a paso en JavaScript

Completa introducción a los functores

una explicación paso a paso sobre como funcionan estos poderosos contenedores.

Supongamos que tenemos un valor comun y corriente:

1
var valor = 2;

2

Y deseamos utilizarlo en una función que le sume 1 a ese valor.

1
2
3
var valor = 2;
function addOne(value) { return value +1; }
addOne(valor);

addOne(value) => 3

Como era de esperarse, funciona sin problemas. ¿que pasaria si en vez de pasarle un numero entero algun programador malvado le pasara como argumento un numero entre ' '?.

1
2
3
var valor = 2;
function addOne(value) { return value +1; }
addOne('2');

addOne('2') => '21'

mmm seguramente no era eso lo que deseabamos. ¿Porque paso esto?, bueno de echo como javascript no es un lenguaje fuertemente tipado, una variable puede ser de cualquier tipo, un numero, un string, un objeto, un array, etc…., al igual que el valor que le pasemos como parametro.

Parece que necesitamos algun tipo de validación, para que no nos pasen cualquier cosa como parametro, vamos a implementar algo cutre que nos salve un poco el cuello.

1
2
3
4
5
6
7
8
9
10
11
var valor = 2;
function addOne(value) {
return typeof(value) === "number"
? value + 1
: parseInt(value) +1;
}
var result = addOne(valor); // ==> 3
result = addOne('2'); // ==> 3
result = addOne([5]); // ==> 6

addOne('2') ==> 3

Funciona, tanto para strings como para arrays (puesto que un string no es mas que un array de caracteres, es algo logico). Aunque esta implementación funcione, aun deja mucho que desear puesto que en realidad tiene un par de fallas por depender de variables globales. ¿que pasaria si alguien cambiara el tipo de la variable global valor?, que tal si desearamos concatenar una serie de llamadas a la función? ej:

1
2
3
4
var result = addOne(valor); // ==> 3
result += addOne('2'); // ==> 6
result += addOne('a'); // ==> NaN
result += addOne(2); // ==> NaN

Lo ideal seria que obviara el tipo de dato que desconoce 'a' (en este caso) y continuara con el calculo, pero eso no es posible debido a que la funcion addOne no tiene una forma de almacenar su valor e ir incrementando el resultado, tambien estaria bueno que esa variable global valor tuviera cierta proteccion durante nuestro calculo, asi evitariamos que alguna función pueda modificar su valor mientras estamos realizando nuestros calculos en base a ella.

Tambien hay otro problema y es que el tipo de dato number no sabe como trabajar con valores que no sean de su mismo tipo, al igual que los strings , las listas, los Object, etc…

¿Porque entonces no nos inventamos un tipo de dato que no tenga estos problemas?, pues bien, veamos que nos hace falta:

  1. Que sea agnostico al tipo de dato, osea que pueda trabajar con string, numeros, objetos, etc…
  2. Que nuestros datos esten totalmente protegidos.
  3. Una manera de aplicarle funciones a nuestro tipo de dato.
  4. Una manera de “encadenar funciones”, sobre nuestro tipo de dato.

Si logramos todo eso tendriamos….

Nuestro primer functor

Vamos a crear nuestro primer functor, por ahora no le daremos muchas caracteristicas para no complicar el codigo, pero poco a poco lo iremos mejorando…

1
2
3
4
5
6
7
8
9
10
11
12
13
var Functor = function(value) {
this.__value = value;
}
Functor.of = function(value) {
return new Functor(value);
}
Functor.prototype.map = function(fn) {
return Functor.of( fn (this.__value) );
}
Functor.of(2).map(addOne).map(addOne).map(console.log);

primer_functor

¡Impresionante!, ¿que es eso de la caja fuerte?. Cuando creamos un functor, en realidad lo que estamos haciendo es encerrar continuamente el valor dentro de un contexto. Miremos la linea 6 y veremos que cada vez que llamamos al metodo of pasandole el valor como argumento, lo que hacemos es “encerrarlo” dentro de una nueva instancia del functor.

1
2
3
Functor.of = function(value) {
return new Functor(value);
}

Esto nos permite mantener nuestros valores protegidos de amenazas externas como puede ser una llamada asincrona que se ejecuto fuera de tiempo, cambios por medio de funciones a nuestro valor, errores de tipos de datos, etc…

funcion_rota

Esto falla, porque la función addOneno sabe como desenvolver el functor para extraer el valor.

Los functores son muy utiles cuando tenemos un valor y necesitamos aplicarle un conjunto de transformaciónes para luego hacer algo con él.

Como hemos visto, ya por el solo echo de ser un functor, podemos encadenarle funciones a nuestro valor, sin necesidad de implementar codigo especifico para ese proposito. Tambien nos ha brindado una manera de apicarle funciónes a nuestros valores, de un modo totalmente seguro, a trabes del metodo map. Y ni hablar de la proteción de nuestros valores!!.

Ahora ¿realmente es agnostico a los tipos de datos?.
¿Puedo usarlo tanto con un numero, como con un string o incluo con un objeto si quisiera?.

Pues veamos un ejemplo y de paso introduciremos levemente ramda una utilisima libreria que nos brinda unas cuantas funciónes para facilitarnos la vida con la programación funcional en JavaScript.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var _ = require("ramda");
var Functor = function(value) {
this.__value = value;
}
Functor.of = function(value) {
return new Functor(value);
}
Functor.prototype.map = function(fn) {
return Functor.of(fn(this.__value) );
}
Functor.of("Los functores son geniales!!!")
.map(_.replace("geniales","copados"))
.map(_.toUpper)
.map(_.split(" "))
.map(_.last)
.map(console.log);

Genial, probemos ahora con una lista de objetos a ver que pasa:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
var _ = require("ramda");
var Personas = [
{nombre: "carlos", edad:20, sexo:"M"},
{nombre: "gisela", edad:26, sexo:"F"},
{nombre: "dario", edad:30, sexo:"M"},
{nombre: "cecilia", edad:35, sexo:"F"},
{nombre: "ambar", edad:7, sexo:"F"},
{nombre: "oscar", edad:57, sexo:"M"},
{nombre: "facundo", edad:37, sexo:"M"}
];
Functor.of(Personas)
.map(_.filter((persona) => persona.edad <=8 )) //Filtra todo el array de objetos por edad.
.map(_.head) //Obtiene la primera coincidencia
.map(_.prop("nombre")) //Obtiene el nombre de la persona
.map(_.concat("Salistes ganando ")) //Le concatena una frase.
.map(_.split(" ")) //transforma el string en un array
.map(_.reverse) //invierte los valores del array
.map(_.join(" ")) //los vuelve a concatenar
.map(console.log) //y ya aburrido de jugar,
//lo muestra por la consola

Como vemos son bastante poderosos y nos permiten agrupar una serie de transformaciones sobre los valores de una forma bastante sencilla. Tambien te habras dado cuenta de que realmente es agnostico de los datos, ya que en el ultimo ejemplo, comenzamos con una lista de objetos y terminamos con undefined (un console.log) retorna eso.

Pues bien, ya para ir cerrando este primer articulo vamos a implementar una manera de controlar los errores.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
var Maybe = function(x) {
this.__value = x;
};
Maybe.of = function(x) {
return new Maybe(x);
};
Maybe.prototype.isNothing = function() {
return (this.__value === null || this.__value === undefined);
};
Maybe.prototype.map = function(f) {
return this.isNothing() ? Maybe.of(null) : Maybe.of(f(this.__value));
};
//Cuenta la cantidad de iniciales en el nombre
Maybe.of("Mariano Martinez")
.map(_.match(/m/ig))
.map(_.length)
.map(console.log);
//Intenta extraer una propiedad no existente en el objeto
//y al no encontrarla simplemente corta el proceso sin generar errores.
Maybe.of({name: 'carlos'})
.map(_.prop('edad'))
.map(_.add(10))
.map(console.log);
//El mismo codigo que arriba pero esta vez
//si encuentra la propiedad buscada, por lo tanto la muestra en pantalla.
Maybe.of({name: 'carlos', edad: 20})
.map(_.prop('edad'))
.map(_.add(10))
.map(console.log);

Impresionante no?. Todo esto es posible porque existe una fuerte teoria matematica detras de los functores (de echo, todo lo que se usa en la programacion funcional tiene una fuerte teoria matematica que lo respalda). Vamos a analizarla:

Ley de la identidad.

map(id) == id - es decir, si intentamos mapear id sobre los elementos de algún contenedor, nos debe de dar lo mismo a que si no hubiésemos hecho nada (sin efectos secundarios).

Ley de composición

compose(map(f), map(g)) === map(compose(f, g)); - dice que, mapear (f) y mapear(g) y luego componerlas juntas, es lo mismo que mapear la composicion de las 2 funciones.

No te preocupes si todavia no te queda demasiado claro el tema de las leyes, poco a poco a los largo de los articulos que vienen lo vamos a ir tocando hasta que te lo sepas de memoria casi sin darte cuenta.

Resumiendo:

Un functor entonces, no es mas que un tipo de dato, que permite aplicar funciones a un valor envuelto.

Palabras finales

Existen muchas cosas que se pueden hacer con functores, recien hemos rozado la punta del iceberg. Existen tambien otro tipo de functores llamados aplicativos que francamente no tienen mucho uso pero que a su tiempo hablaremos sobre ellos ya que son la base para entender las monadas.

¿Que sigue?, en proximos articulos hablaremos acerca de la composicion funcional, una tecnica que te enamorara a primera vista. Pero mientras… ¡ha practicar con los functores!! :)

Comentarios

Funtores en Javascript

Introduccion a los functores en JavaScript

Considere la funcion a continuación:

1
2
3
function plus1(value) {
return value + 1
}

Es simplemente una funcion que toma un entero, le adiere 1 y lo retorna. Del mismo modo podriamos tener otra funcion llamada plus2. Vamos a utilizar estas funciones mas adelante.

1
2
3
function plus2(value) {
return value + 2
}

Y ya que estamos, tambien podriamos escribir una funcion generalizada que nos permita usar cualquiera de estas funciones cuando sea necesario:

1
2
3
4
5
function F(value, fn) {
return fn(value)
}
F(1, plus1) ==>> 2

Esta función trabajara correctamente siempre y cuando el valor pasado sea un
numero entero, probemos un array:

1
F([1, 2, 3], plus1) ==>> '1,2,31'

Auch. Tomamos una matriz de enteros, le añadimos un entero y regresamos una cadena!. No solo hizo las cosas mal ya que terminamos con un string habiendo comenzado con un array, sino que nuestro programa tambien a destrozado la estructura de entrada!.

Queremos que F haga “lo correcto” y lo correcto es “mantener la estructura” a trabés de la operación.

Entonces, ¿que es lo que entendemos por “mantener la estructura”?. Nuestra
función debe de “desempaquetar” el array dado y obtener sus elementos para, a continuación, llamar a la función dada con cada elemento del array, luego envolver los valores devueltos en un nuevo array y retornarlo.

Afortunadamente javascript ya tiene esa funcion y se llama map:

1
[1, 2, 3].map(plus1) ==>> [2, 3, 4]

Y map es un functor!

Un functor es una funcion que dado un valor y una función hace lo correcto.

Siendo un poquito mas especifico..

Un functor es una función que dado un valor envuelto (como un array o un objeto por ejemplo) y una función, desenvuelve los valores para llegar a su valor interno, llama a la funcion dada con el valor interno y envuelve los valores devueltos en una nueva estructura para luego retornar esa estructura.

Una cosa a tener en cuenta es que, dependiendo del “tipo” del valor, la
desenvoltura puede dar lugar a un valor o a un conjunto de valores.

Tambien la estructura devuelta no tiene porque ser del mismo tipo que el valor
original. En el caso de map, tanto el valor como el valor devuelto tienen la
misma estructura (un array). La estructura devuelta puede ser de cualquier tipo, siempre y cuando se pueda llegar a los elementos individuales.

Asi que, si habia una funcion que toma un array y retorna un valor de tipo Object con todos los indices del array como llaves, y los valores correspondientes, eso tambien sera un functor.

En el caso de javascript, filter es un functor dado que retorna un array (un valor envuelto en un contexto). Por otro lado, forEach no es un functor porque devuelve undefined. Es decir, forEach no mantiene la estructura.

Los functores provienen de la teoria de categorias de las matematicas. Donde los functores son definidos como “homomorfismos” entre categorias. Vamos a ver que significa eso:

  • homo: igual
  • morfismos: funciónes que mantienen la estructura.
  • category: tipo de dato

Segun la teoria, la funcion F es un functor cuando para 2 funciones comunes
componibles f y g:

1
F(f . g) = F(f) . F(g)

donde el . indica la composición. Los functores deben de preservar la composición.

Asique dada esta ecuación, podemos probar si una funcion dada es de echo un
functor o no lo es.

Functor Array.

Vimos que map es un functor que actua sobre el tipo de dato array. Vamos a
demostrar que la función de javascript Array.map es un functor.

1
2
3
function compose(f, g) {
return function(x) {return f(g(x))}
}

La composición de funciones se trata de llamar a un conjunto de funciones, llamando a la funcion siguiente con los resultados de la funcion anterior.

Tenga en cuenta que nuestra función compose funciona de derecha a izquierda, osea g se llama primero y luego f.

1
2
3
[1, 2, 3].map(compose(plus1, plus2)) ==>> [ 4, 5, 6 ]
[1, 2, 3].map(plus2).map(plus1) ==>> [ 4, 5, 6 ]

Por lo visto sip, map es de echo un functor.

Vamos a probar algunos functores. Podemos escribir functores para valores de
cualquier tipo, siempre y cuando se pueda desenvolver el valor y retornar la
estructura.

String functor.

Entonces… ¿podemos escribir un functor de tipo string?, ¿se puede desenvolver un string?. En realidad si se puede, si se piensa en una cadena como un array de caracteres.

Esto se trata realmente acerca de como se mire el valor. Tambien sabemos que los caracteres tienen codigos que son numeros enteros, entonces si ejecutamos plus1 en cada charcode envolviendolos de nuevo en un
string y retornandolas…

1
2
3
4
5
6
7
8
9
function stringFunctor(value, fn) {
var chars = value.split("");
return chars.map ( function(char) {
return String.fromCharCode( fn( char.charCodeAt(0) ) );
})
.join(""); //Nuevamente volvemos a "empaquetar el valor"
}
stringFunctor("ABCD", plus1) ==>> "BCDE"

Usted podra comenzar a ver como son de impresionantes los functores,
en realidad se puede escribir un parser usando el functor string como base.

Functor funcion.

Como sabemos, en javascript las funciones son ciudadanos de primera clase. Esto significa que podemos tratar a las funciones como a cualquier otro valor.
Entonces, ¿Podemos escribir un functor para un valor de tipo function?,
deberiamos de ser capaces de hacerlo, pero ¿como desenvolvemos una funcion?.

Se puede desenvolver una funcion llamandola y consiguiendo su valor de retorno, pero enseguida nos encontraremos con un problema, y es que para llamar a una funcion necesitamos sus argumentos. Recordemos que un functor solo tiene la funcion de entrada como el valor.

Podemos solventar esto haciendo que el functor devuelva una nueva funcion. Esta funcion sera llamada con los argumentos y que a su vez, llamara a la funcion de valor con los argumentos, para luego llamar a la función del functor original con el valor devuelto.

1
2
3
4
5
6
7
8
9
10
11
function functionFunctor(value, fn) {
return function(initial) {
return function() {
return fn(value(initial))
}
}
}
var init = functionFunctor(function(x) {return x * x}, plus1)
var final = init(2)
final() ==> 5

Nuestro functor funcion realmente no hace mucho, por decir poco. Pero hay un par de cosas para tomar nota aqui. Nada sucede hasta que se llame a final. Cada cosa esta en un estado de animacion suspendida hasta que final sea llamado. El functor funcion (tambien llamado aplicativo) constituye la base para la materia funcional mas impresionante como el mantenimiento del estado, llamadas de continuación e incuso promesas. Usted puede escribir sus propios functores funcion para hacer todas estas cosas.

Functor Maybe

1
2
3
function mayBe(value, fn) {
return value === null || value === undefined ? value : fn(value)
}

Si, esto es un functor valido.

1
2
3
4
MayBe(undefined, compose(plus1, plus2)) ==>> undefined
mayBe(mayBe(undefined, plus2), plus1) ==>> undefined
mayBe(1, compose(plus1, plus2)) ==>> 4
mayBe(mayBe(1, plus2), plus1) ==>> 4

Asique Maybe pasa nuestra prueba de functor. No hay necesidad de envolver o
desenvolver aqui. Simplemente retorna Nothing para nothing. Maybe se usa como una funcion de corto circuito, que sirve como un sustituto de codigo
para cosas como:

1
2
3
4
5
if (result === null) {
return null
} else {
doSomething(result)
}

Funcion identidad

1
2
3
function id(x) {
return x
}

A la función anterior se la conoce como la funcion identidad. Es simplemente una funcion que retorna el valor que se le pasa commo argumento (bastante simple no?).
Se llama asi ya que es la identidad en la composicion de funciones en las matematicas.

Hemos aprendido anteriormente que los functores deben preservar la composicion. Sin embargo algo que no hemos mencionado aqui es que los functores tambien deben de preservar la identidad, es decir:

1
F(value, id) = value

Vamos a probar esto para map.

1
[1, 2, 3].map(id) ==>> [ 1, 2, 3 ]

Signatura de tipo.

El tipo de firma de una función es su argumento o argumentos de ser mas de uno y el valor de retorno.

Por lo que el tipo de firma de la funcion plus1 es:

1
f: int -> int

El tipo de signatura de la función map depende del tipo de signatura de la
función pasada como argumento.Entonces si map es llamado con plus1 entonces este es su tipo de signatura:

1
map: [int] -> [int]

Sin embargo el tipo de firma de la funcion dada no tiene porque ser como la
anterior. Tranquilamente podriamos tener una funcion como:

1
f: int -> string

En el que el tipo de signatura de map seria:

1
map: [int] -> [string]

La unica reestricción es que el cambio de tipo no afecte a la componibilidad del functor. Asique en general una firma de tipo functor puede.

1
F: a -> b

En otras palabras, map puede tomar un array de enteros y retornar un array de cadenas y seguir siendo un functor valido.

Las monadas son un caso especial de los functores cuya firma es:

1
M: A -> A

Pero veremos mas acerca de las monadas en proximos articulos, por hoy creo que tenemos suficiente para entretenernos un buen rato.

Comentarios

Funciones parciales y curry en JavaScript

Curry, o aplicación parcial, es una de las tecnicas de programación funcional que mas confusión puede causar entre las personas que estan acostumbradas a programar en JavaScript de modo tradicional (imperativo). Sin embargo, cuando se aplica correctamente puede llegar a hacer de su codigo una verdadera obra de arte, y minimizar la duplicidad de codigo, haciendo que sea mas legible y reutilizable.

Mas legible y mas flexible.

Una de las ventajas que se promocionan entre los que usamos JavaScript como lenguaje funcional, es que es mas corto, mas estricto y que va directo al punto en el menor numero de lineas posibles, y con menos repetición. A veces esto puede ir a expensas de la legibilidad; hasta que se haya familiarizado con la forma en la que funciona la programacion funciónal, el codigo escrito de esta manera puede llegar a ser mas dificil de leer y entender en un principio.

Si ya ha encontrado el termino “Curry” en libros o en articulos anteriormente, pero nunca supo bien lo que significaba, sepa que es perdonado por pensar en el “curry” como algo exotico, una tecnica “picante” de la cual usted no tiene porque preocuparse. Pero curry es en realidad un concepto muy simple pero poderoso, y aborda unos problemas que nos son muy familiares cuando tratamos con los argumentos de las funciones, mientras que habre una gran gama de opciones muy flexibles para el desarrollador.

¿Pero que es curring?

En pocas palabras, curring es una manera de contruir funciónes que permite la aplicación parcial de argumentos en la llamada a la función. Lo que esto significa es que usted puede pasar todos los argumentos que una funcion esta esperando y obtener el resultado, o pasar un subconjunto de esos argumentos y obtener una copia de la función que esta esperando para el resto de los argumentos. Es realmente asi de simple.

El curry es algo elemental en lenguajes como Haskell y Scala, los cuales se construyen en torno a conceptos funcionales. JavaScript en cambio tiene capacidades funcionales, pero la currificación no se construye por defecto en este lenguaje (Al menos no en las versiones actuales). Pero como ya sabemos algunos trucos funcionales, podemos hacer que curring funcione para nosotros tambien en JavaScript.

Para que nos demos una idea de como esto podria funcionar, vamos a crear nuestra primera función curry en javascript, utilizando una sintaxis familiar para construir la funcionalidad currificada que necesitamos. Como ejemplo, imaginemos una simple función que saluda a alguien por su nombre. Todos sabemos como crear una simple función saludar que toma un nombre y un saludo, y muestra el nombre y el saludo en la consola:

1
2
3
4
var greet = function(greeting, name) {
console.log(greeting + ", " + name);
};
greet("Hello", "Heidi"); //"Hello, Heidi"

Esta función requiere que tanto el nombre como el saludo se pasen como argumentos para poder funcionar correctamente. Pero podriamos reescribir esta funcion utilizando una currificación sencilla anidada, de modo que la función básica solo requiera un saludo, y retorne otra función que tome como argumento el nombre de la persona a la que queremos saludar.

Nuestro primer curry.

1
2
3
4
5
var greetCurried = function(greeting) {
return function(name) {
console.log(greeting + ", " + name);
};
};

Este pequeño cambio en la forma de escribir la función, nos permite crear una nueva función para cualquier tipo de saludo, y pasar a esa nueva función, el nombre de la persona a quien queremos saludar:

1
2
3
var greetHello = greetCurried("Hello");
greetHello("Heidi"); //"Hello, Heidi"
greetHello("Eddie"); //"Hello, Eddie"

También podemos llamar a nuestra función currificada directamente, simplemente pasando cada uno de los parámetros en un conjunto separado de parentesís, uno detras del otro:

1
greetCurried("Hi there")("Howard"); //"Hi there, Howard"

¿Por qué no probar esto en nuestro navegador?
Ejemplo en JsBin

Currifica todas las cosas!

Lo interesante es, ahora que hemos aprendido a modificar nuestras funciónes para utilizar este enfoque con los argumentos, que podemos hacer esto con tantos argumentos como deseemos:

1
2
3
4
5
6
7
8
9
var greetDeeplyCurried = function(greeting) {
return function(separator) {
return function(emphasis) {
return function(name) {
console.log(greeting + separator + name + emphasis);
};
};
};
};

Tenemos la misma flexibilidad, con 4 argumentos como antes con 2. No importa que tan lejos llegue la anidación, podemos crear nuevas funciónes personalizadas para saludar a tantas personas como queramos en tantas formas como deseemos:

1
2
3
var greetAwkwardly = greetDeeplyCurried("Hello")("...")("?");
greetAwkwardly("Heidi"); //"Hello...Heidi?"
greetAwkwardly("Eddie"); //"Hello...Eddie?"

Lo que es más, podemos pasar tantos parametros como deseemos al crear variaciónes personalizadas en nuestra funcion currificada original, tambien podemos crear nuevas funciónes que son capaces de tomar el numero de parametros restantes, cada argumento pasado por separado en su propio conjunto de parentesis:

1
2
3
var sayHello = greetDeeplyCurried("Hello")(", ");
sayHello(".")("Heidi"); //"Hello, Heidi."
sayHello(".")("Eddie"); //"Hello, Eddie."

Y podemos definir variaciónes subordinadas asi de facil:

1
2
3
var askHello = sayHello("?");
askHello("Heidi"); //"Hello, Heidi?"
askHello("Eddie"); //"Hello, Eddie?"

Ejemplo en JsBin

Currificando funciónes tradicionales.

Se puede ver lo poderoso que es este enfoque, especialmente cuando usted necesita crear una gran cantidad de funciones personalizadas muy detalladas. El unico problema es la syntaxis. A medida que se construyen estas funciones currificadas, usted necesita mantener un gran numero de retorno de funciónes anidadas, y llamarlas con nuevas funciones que requieren multiples sets de parentesis, conteniendo cada uno su propio argumento aislado. Esto puede causar problemas.

Para hacer frente a este problema, un enfoque es crear una función curry rapidamente, y que tome el nombre de una función existente que haya sido escrita sin todos esos horrendos retornos anidados. Una función curry necesitaria obtener todos los argumentos de la funcion que desea currificar, y usarlos para devolver una función curry de la función original.

1
2
3
4
5
6
7
8
var curryIt = function(uncurried) {
var parameters = Array.prototype.slice.call(arguments, 1);
return function() {
return uncurried.apply(this, parameters.concat(
Array.prototype.slice.call(arguments, 0)
));
};
};

Para utilizar esto, le pasamos el nombre de una función que toma cualquier numero de argumentos, junto con los argumentos que deseamos rellenar previamente. Lo que retornara una función que espera el numero de argumentos restantes:

1
2
3
4
5
6
var greeter = function(greeting, separator, emphasis, name) {
console.log(greeting + separator + name + emphasis);
};
var greetHello = curryIt(greeter, "Hello", ", ", ".");
greetHello("Heidi"); //"Hello, Heidi."
greetHello("Eddie"); //"Hello, Eddie."

Y al igual que antes, no estamos limitados en el numero de argumentos que podemos utlizar cuando construimos funciónes derivadas de nuestra función curry original:

1
2
var greetGoodbye = curryIt(greeter, "Goodbye", ", ");
greetGoodbye(".", "Joe"); //"Goodbye, Joe."

Ejemplo en JsBin

Tomando en serio la currificación

Nuestra pequeña función de currificación, no puede manejar todos los casos, tales como parámetros faltantes u opcionales, pero hace un trabajo razonable, siempre y cuando nos mantengamos estrictos sobre la sintaxis para el paso de los argumentos.

Algunas librerias funcionales para JavaScript como ramda tienen funciónes currificadas mas flexibles que son capaces de romper los parametros requeridos para una función, y permiten pasarlas en forma individual o en grupos para crear variaciones personalizadas de la función currificada. Si desea usar ampliamente curring, este es probablemente el mejor camino a seguir.

Independientemente de como elija agregar la currificacion en su codigo, ya sea que desee utilizar parentesis anidados o si prefiere incluir una función curry mas robusta, ramda viene con una convención de momenclatura coherente para sus funciónes curry que ayuda a que el codigo sea mas legible. Cada variacion derivada de una función currificada debe de tener un nombre que deje en claro como se comporta, y que argumentos esta esperando.

Orden de los argumentos.

Una cosa que es importante tener en cuenta a la hora de diseñar una función a la cual vamos a querer currificar, es el orden de los argumentos. Aqui hay una ley casi inexorable que es poner siempre los datos como ultimo parametro de la función. Imaginemos por ejemplo que tenemos que generar una gran cantidad de funciónes para el trabajo con strings, en este ejemplo utilizaremos ramda para apoyarnos en su función curry:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var curry = require("ramda").curry;
var match = curry(function(what, str) {
return str.match(what);
});
var replace = curry(function(what, replacement, str) {
return str.replace(what, replacement);
});
var filter = curry(function(f, ary) {
return ary.filter(f);
});
var map = curry(function(f, ary) {
return ary.map(f);
});

Si nos fijamos, hemos posicionado de forma estrategica los parametros en las funciónes, siempre el ultimo argumento es el dato sobre el cual vamos a trabajar. Veamos como podemos usar estas funciones currificadas:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
match(/\s+/g, "hello world");
// [ ' ' ]
match(/\s+/g)("hello world");
// [ ' ' ]
var hasSpaces = match(/\s+/g);
// function(x) { return x.match(/\s+/g) }
hasSpaces("hello world");
// [ ' ' ]
hasSpaces("spaceless");
// null
filter(hasSpaces, ["tori_spelling", "tori amos"]);
// ["tori amos"]
var findSpaces = filter(hasSpaces);
// function(xs) { return xs.filter(function(x) { return x.match(/\s+/g) }) }
findSpaces(["tori_spelling", "tori amos"]);
// ["tori amos"]
var noVowels = replace(/[aeiou]/ig);
// function(replacement, x) { return x.replace(/[aeiou]/ig, replacement) }
var censored = noVowels("*");
// function(x) { return x.replace(/[aeiou]/ig, "*") }
censored("Chocolate Rain");
// 'Ch*c*l*t* R**n'

Si nos damos cuenta, de 4 funciónes currificadas que teniamos hemos generado una gran cantidad de otras utiles funciónes casi de la nada, aprobechando mucho mejor el codigo y sin tener que duplicar codigo.

Conclusión

La currificación es una tecnica increiblemente util de la programacion funcional que podemos usar facilmente en JavaScript. Permite generar una libreria de pequeñas y utiles funciónes faciles de configurar que se comportan de manera consistente. Son rapidas de usar, y pueden entenderse facilmente al leer su codigo. Aderir la tecnica de currificación a su dia a dia, fomentara el uso de las funciones aplicadas parcialemente a trabes de su codigo, evitando mucha repetición, y puede ayudar a conseguir mejores habitos sobre el nombramiento de las funciones y a hacer frente a los argumentos de las funciones.

Comentarios

Funciones puras en javascript y sus beneficios

haciendo puro a JavaScript

Una vez que su sitio web o aplicación va mas alla de un par de numeros de lineas, se hace inevitable que puedan contener errores de algun tipo. Esto no es especifico de JavaScript pero si es compartido por casi todos los lenguajes- es muy complicado, si no imposible, poder descartar completamente la chance de que nuestro codigo no tenga bugs de ningún tipo. Sin embargo, esto no quiere decir que no podamos tomar precauciónes programando de una manera que disminuya nuestra vulnerabilidad a los errores.

Funciónes puras e impuras.

Una función pura se define como una función que no depende de variables externas a su ambito. Esto es un poco duro de entender al comienzo, por lo que vamos a sumergirnos en un poco de codigo para un ejemplo mas practico.

Tomemos esta función que calcula si el mouse esta en el lado izquierdo de una pagina, y muestra por consola usando console.log si es true (esta al lado izquierdo) o false (no esta en el lado izquierdo). En realidad su función seguramente seria mas compleja y un poco mas trabajada, pero este ejemplo hace un gran trabajo para la demostración:

1
2
3
4
5
6
7
function mouseOnLeftSide(mouseX) {
return mouseX < window.innerWidth / 2;
}
document.onmousemove = function(e) {
console.log(mouseOnLeftSide(e.pageX));
};

mouseOnLeftSide() toma la coordenada x y comprueba si es menos de la mitad del ancho de la ventana- lo que la colocaria en el lado izquierdo. Sin embargo, mouseOnLeftSide() no es una función pura. Lo sabemos porque dentro del cuerpo de la función hace referencia a un valor que no se le paso de forma explicita:

1
return mouseX < window.innerWidth / 2;

Se le da a la función mouseX, pero no window.innerWidth. Esto significa que la función esta pudiendo acceder a los datos que no se le dio, y por lo tanto no es pura.

El problema con las funciónes impuras.

Seguramente usted se estara preguntando porque esto es un problema- esta pieza de codigo trabaja bien y hace el trabajo que se espera de ella. Imaginese que usted obtenga un informe de error de un usuario de que cuando la ventana esta a menos de 500px de ancho la función es incorrecta. Como probaria esto? Tiene dos opciones:

  • Se puede probar manualmente mediante el navegador, cargando la pagina y redimencionandola y moviendo el mouse hasta que haya encontrado el problema.

  • Podria escribir algunas pruebas unitarias (Rebecca Murphey’s Writing Testable JavaScript es una gran introducción) no solo para rastrear los errores, sino para asegurarse de que nunca vuelvan a suceder.

Interesados en tener una prueba en el lugar para evitar este error recurrente, elegimos la segunda opción y escribimos el test. Ahora nos enfrentamos a un nuevo problema, porque: ¿como podeos configurar nuestra prueba correctamente?. Sabemos que necesitamos para configurar nuestra prueba con la anchura de la ventana esablecida a menos de 500px, pero ahora?. La función se basa en window.innerWidth, y asegurarse de que eso pasa solamente en un valor concreto va a ser un dolor.

Beneficios de las funciónes puras.

La prueba mas simple

Con esa cuestión de como probar nuestra función en mente, imaginemos que nos decidimos a reescribir nuestra función de esta manera:

1
2
3
4
5
6
7
function mouseOnLeftSide(mouseX, windowWidth) {
return mouseX < windowWidth / 2;
}
document.onmousemove = function(e) {
console.log(mouseOnLeftSide(e.pageX, window.innerWidth));
};

La diferencia clave aqui es que mouseOnLeftSide() ahora toma 2 argumentos: La posicion X del mouse y el ancho de la ventana. Esto significa que mouseOnLeftSide() es ahora una función pura; todos los datos que necesita para funcionar les son pasados explicitamente a trabes de sus parametros de entrada, y nunca tiene que llegar a acceder a los datos fuera de su ambito.

En terminos de funcionalidad, esto es identico al ejemplo anterior, pero hemos mejorado drasticamente su capacidad de mantenimiento y su capacidad de prueba. Ahora no tenemos que usar un mock, o hacer un fake alrededor de window.innerWidth para poder probarlo, ahora simplemente podemos llamar a mouseOnLeftSide() con los argumentos exactos que necesitamos:

1
mouseOnLeftSide(5, 499) // nos aseguramos que funciona con un ancho < 500px

Auto documentada.

Ademas de ser mas facil para testear, las funciónes puras tienen otras caracteristicas que hace que valga la pena usarlas siempre que sea posible. Por su propia naturaleza, las funciónes puras son auto-documentadas. Si usted sabe que una función no llega a salir de scope para obtener los datos, usted sabe que los unicos datos que puede tocar son los pasados como argumentos. Considere la siguiente declaración de función:

1
function mouseOnLeftSide(mouseX, windowWidth)

Usted sabe con solo ver la declaración, que esta función solo ocupa 2 piezas de datos, y si los argumentos estan bien nombrados, debe de quedar claro que son con solo ver la declaración. Todos tenemos que lidiar con el dolor de tener que volver a tocar codigo que yace sin tocar durante 6 meses o mas tiempo, y ser capaces de familiarizare con el codigo rapidamente es una habilidad clave.

Evitar variables globales en el codigo.

El problema de las variables globales esta bien documentado en JavaScript. El lenguaje hace que sea trivial almacenar datos de forma global, donde todas las fnuciones pueden acceder a ellos. Esto es una fuente comun de errores, tambien, cualquier cosa podria haber cambiado el valor de una variable global, y por lo tanto las funciones pueden comportarse de manera diferente a lo esperado sin que nos percatemos de ello.

Una propiedad adicional de las funciónes puras es la transparencia referencial. Este termino que parece bastante complejo, tiene un significado muy simple: dadas las mismas entradas, la salida de la función es siempre la misma. Volviendo a mouseOnLeftSide echemos un vistazo a la definicion de la primera función que teniamos:

1
2
3
function mouseOnLeftSide(mouseX) {
return mouseX < window.innerWidth / 2;
}

Esta función no es referencialmente transparente. Por ejemplo podriamos llamarla con 5 como entrada varias veces, redimencionar la ventana entre llamadas, y el resultado seria diferente cada vez. Este es un ejemplo un poco artificial, pero las funciones que devuelven valores diferentes incluso cuando sus entradas son las mismas siempre resultan mas dificiles de trabajar. El razonamiento acerca de ellas es mas dificil puesto que no se puede garantizar su comportamiento. Por el mismo motivo, probar este tipo de funciones es mucho mas complicado porque no tenemos un control total sobre los datos con los cuales la función opera.

Por otra parte, nuestra función mejorada mouseOnLeftSide es referencialmente transparenteporque todos sus datos llegan a trabes de sus parametros y nunca llega fuera de su scope u ambito.

1
2
3
function mouseOnLeftSide(mouseX, windowWidth) {
return mouseX < windowWidth / 2;
}

Se obtiene transparencia referencial de forma gratuita cuando seguimos la regla de declarar todos sus datos como argumentos, y al hacer esto, se elimina toda una clase de bugs alrededor de los efectos secundarios y las funciones que actuan de forma inesperada. Si usted tiene el control total sobre los datos, se puede cazar y replicar los errores de forma mucho mas rapida y fiable sin necesidad de tener que cambiar toda la loteria de variables globales que pueden llegar a interferir.

La eleccion de las funciónes que deben ser puras.

Lamentablemente es imposible tener funciones puras consistentemente- siempre habra un momento que usted necesitara buscar los datos fuera del ambito de las funciones, el ejemplo mas común de esto es cuando tenemos que acceder al DOM para agarrar un elemento para interactuar con él. Es un echo de que en JavaScript vamos a tener que hacer eso. Y usted no tiene porque sentirse mal por llegar fuera del ambito de su función. En su lugar, debe considerar como reestructurar el codigo de manera que las funciónes impuras se puedan aislar. Impedirles tener amplios efectos a trabes de su codigo fuente, y tratar de usar funciones puras siempre que sea apropiado.

Vamos a echar un vistazo al codigo de abajo, que toma un elemento de DOM y cambia su color de fondo a rojo:

1
2
3
4
5
6
7
function changeElementToRed() {
var foo = document.getElementById('foo');
foo.style.backgroundColor = "red";
}
changeElementToRed();

Hay 2 problemas con este pedazo de codigo, pero solventable al hacer la transición a una función pura:

  • Esta función no es reutilizable en lo absoluto, ya que esta directamente vinculada a un elemento especifico de DOM, si quisieramos volver a utilizarla para cambiar otro elemento, no podremos hacerlo.

  • Esta función es dificil de probar debido a que no es pura. Para testearla, tendriamos que crear un elemento con un ID especifico en lugar de cualquier elemento generico.

Teniendo en cuenta los 2 puntos anteriores, me gustaria volver a reescribir esta funcion para:

1
2
3
4
5
6
7
8
9
10
function changeElementToRed(elem) {
elem.style.backgroundColor = "red";
}
function changeFooToRed() {
var foo = document.getElementById('foo');
changeElementToRed(foo);
}
changeFooToRed();

Ahora hemos cambiado changeElementToRed() para no estar atado a un elemento especifico del DOM y que sea mas generica. Al mismo tiempo, hemos echo que sea pura, dado que nos trae todos los beneficios discutidos anteriorente.

Es importante señalar, sin embargo, que todavia tengo algo de codigo impuro-changeFooToRed() es impuro. Nunca se puede evitar del todo esto. Pero esto es acerca de detectar la oportunidad de volver una funcion pura para incrementar la legibilidad, reusabilidad, y testeabilidad. Manteniendo los lugares donde operan las funciones impuras al mimimo y creando funciones mas puras y reutilizables como sea posible, se ahorrara una gran cantidad de dolor en un futuro y su codigo mejorara considerablemente.

Conclusión

“Funciónes puras”, “transparencia referencial”, “efectos secundarios”, son terminos comunmente asociados con los lenguajes de programacion funcionales puros, pero eso no significa que no podemos tomar esos principios y asociarlo a nuestros programas en JavaScript tambien. Al ser consciente de estos principios y aplicarlos con prudencia el código puede beneficiarse de ellos y obtener más fiabilidad, auto-documentacion que hace que sea mas facil de trabajar y se rompe con menos frecuencia. Les animo a tener todo esto en cuenta la proxima vez que esteos escribiendo nuevo codigo JavaScript o incluso cuando volvamos a visitar algun codigo ya existente. Puede tomar un poco de tiempo acostumbrarse a estas ideas, pero pronto se encontrara aplicandolas casi sin darse cuenta, y el resto de los desarrolladores e incluso usted mismo se lo agradeceran.

Comentarios

Desestructuracíon y recursividad en Javascript 2015

Arrays literales

Los arrays en javascript son representaciones nativas de las listas. Las listas son importantes porque representan colecciones ordenadas de cosas, y la ordenacion de esas colecciones es una abstraccion fundamental para dar sentido a la realidad.

javascript tiene una sintaxis literal para crear arrays. Los corchetes [ ]. Podemos crear una lista vacia:

1
2
[]
//-> []

Tambien podemos crear un array con uno o mas elementos colocandolos entre los corchetes ([ ]) y separandolos por comas. El espacio en blanco es opcional aunque se recomienda por legibilidad:

1
2
3
4
[1]
//-> [1]
[2,3,4]
//-> [2,3,4]

Cualquier expresion funcionara:

1
2
3
4
5
[ 2,
3,
2 + 2
]
//=> [2,3,4]

Incluidas expresiones que denoten otros arrays:

1
[[[[[]]]]]

Esto es un array con un elemento que es un array de arrays con un elemento que es un array con un elemento que es un array con un elemento vacio. Aunque esto parece algo que nunca jamas nadie construiria, muchos estudiantes han trabajado con casi exactamente lo mismo cuando se exploran diversas maneras para la construccion de la aritmetica de la teooria de categorias.

Cualquier expresion puede hacerlo:

1
2
3
4
const wrap = (something) => [something];
wrap("lunch")
//=> ["lunch"]

Los arrays literales son expresiones, y los arrays son tipos por referencia. Podemos ver que cada vez que se evalua un array literal, obtenemos un nuevo array distinto, incluso si contiene los mismos elementos exactos.

1
2
3
4
5
6
7
8
9
10
[] === []
//=> false
[2 + 2] === [2 + 2]
//=> false
const array_of_one = () => [1];
array_of_one() === array_of_one()
//=> false

Desestructurando arrays.

La desestructuración es una caracteristica que se remonta a Common Lisp, si no antes. Ya vimos como construir una matriz usando un array literal [, expresiones, , y ]. Aqui hay un ejemplo de un array literal que usa un nombre:

1
const wrap = (something) => [something];

vamos a ampliar esto para que use un bloque y un nombre adicional:

1
2
3
4
5
6
7
8
const wrap = (something) => {
const wrapped = [something];
return wrapped;
}
wrap("package")
//=> ["package"]

La linea const wrapped = [something]; es interesante. En el lado izquierdo tenemos un nombre que sera vinculado, y en el lado derecho un array literal. Un template para construir un array, muy parecido a una cadena cuasi-literal.

En Javascript, nosotros podemos invertir la declaracion y colocar el template en la izquierda y el valor en la derecha.

1
2
3
4
5
6
7
8
const unwrap = (wrapped) => {
const [something] = wrapped;
return something;
}
unwrap(["present"])
//=> "present"

La declaración const [something] = wrapped; desestructura el array representado por wrapped, une el valor de su unico elemento al nombre somehing. Podemos hacer lo mismo con mas de un elemento.

1
2
3
4
5
6
7
8
const surname = (name) => {
const [first, last] = name;
return last;
}
surname(["Reginald", "Braithwaite"])
//=> "Braithwaite"

Podriamos lograr lo mismo con (name) => name[1], pero la desestructuracion es un codigo que se asemeja a los datos que consume, un estilo valioso de programación.

La desestructuracion tambien se puede añidar:

1
2
3
4
5
6
7
8
const description = (nameAndOccupation) => {
const [[first, last], occupation] = nameAndOccupation;
return `${first} is a ${occupation}`;
}
description([["Reginald", "Braithwaite"], "programmer"])
//=> "Reginald is a programmer"

Recolección

Ha veces tenemos que extraer arrays desde arrays. Este es el patron mas comun:
La extraccion de la cabeza y la recoleccion de todo el resto del array excepto la cabeza:

1
2
3
4
5
6
const [car, ...cdr] = [1, 2, 3, 4, 5];
car
//=> 1
cdr
//=> [2, 3, 4, 5]

car y cdr sor terminos arcanos que se remontan a una implementacion de Lisp que corria en las computadoras IBM 704. Algunos otros lenguajes llaman a esto first y butFirst, o head y tail. Nosotros vamos a usar una convención comun y llamar a las variables que recogemos como rest, pero refiriendonos al operador como recolector.

Por desgracia, la notacion no nos provee de una capacidad universal de pattern-maching. Por ejemplo, no podemos escribir:

1
2
3
4
const [...butLast, last] = [1, 2, 3, 4, 5];
//=> ERROR
const [first, ..., last] = [1, 2, 3, 4, 5];
//=> ERROR

Tambien es importante tener un cuenta que la notacion , puede estar al principio. Por ejemplo en el caso de un constructor como:

1
const date = new Date(...[2015, 1, 1]);

Ahora, cuando introdujimos desestructuración, vimos que es kind-of-sort-of el reverso de arrays literales. Entonces si

1
const wrapped = [something];

Entonces:

1
const [unwrapped] = something;

¿Cual es la inversa de la recoleccion?, Lo sabemos ahora:

1
const [car, ...cdr] = [1, 2, 3, 4, 5];

Vamos a probar esto:

1
2
3
4
const oneTwoThree = ["one", "two", "three"];
["zero", ...oneTwoThree]
//=> ["zero","one","two","three"]

¡Funciona! nosotros podemos usar ... para colocar los elementos de una matriz dentro de otra matriz. Decimos que el uso de ... para desestructurar esta reuniendo, y usandolo con un literal para insertar elementos es llamado “extension”.

Desestructurando paramentros.

Considere la forma de pasar los argumentos a los parametros de una función.

1
2
3
foo()
bar("smaug")
baz(1, 2, 3)

Esto es muy parecido a un array literal. Y considere como ligamos valores a nombres de los parametros.

1
2
3
const foo = () => ...
const bar = (name) => ...
const baz = (a, b, c) => ...

Esto luce igual que la desestructuración. Y actua igual que la desestructuración. Solo hay una diferencia: no hemos probado la recolección. Vamos a hacer eso:

1
2
3
4
5
6
7
8
9
const numbers = (...nums) => nums;
numbers(1, 2, 3, 4, 5)
//=> [1,2,3,4,5]
const headAndTail = (head, ...tail) => [head, tail];
headAndTail(1, 2, 3, 4, 5)
//=> [1,[2,3,4,5]]

La recolección trabaja con parametros!. Esto es muy util de echo, y vamos a ver más de ella en un momento.

Auto-Similaridad

Vimos que la idea básica de poner un array junto con una expresion de array literal era la inversa o contraria de desarmarlo con una asignación desestructurada.

Vamos a ser un poco mas especiicos. Algunas estructuras de datos, como las listas, pueden ser obviamente vistas como una colección de elementos. Algunas estan vacias, otras tendran tres elementos, otras cuarenta y dos, algunas contienen numeros, otras contienen cadenas, algunas una mexcla de elementos, existen todo tipo de listas.

Pero tambien podemos definir una lista ecribiendo algunas reglas para la construccion de las mismas. Una de las mas sencillas y de mas larga data en las ciencias de la computación, es decir que una lista es:

  1. Vacia, o;
  2. Consta de un elemento concatenado con una lista.

Convirtamos nuestras reglas en arrays literales. La primer regla es simple: [] es una lista. ¿Y que hay hacerca de la segunda regla?. Podemos expresar esto usando una extensión. Dado un elemento e y una lista list, [e, ...list] es la lista. Podemos verificar esto de forma manual mediante la creacion de una lista.

1
2
3
4
5
6
7
8
9
10
11
[]
//=> []
["baz", ...[]]
//=> ["baz"]
["bar", ...["baz"]]
//=> ["bar","baz"]
["foo", ...["bar", "baz"]]
//=> ["foo","bar","baz"]

Gracias al paralelismo entre arrays literales + las extensiones con desestructuración + rest, tambien podemos usar las mismas reglas para descomponer listas.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
const [first, ...rest] = [];
first
//=> undefined
rest
//=> []:
const [first, ...rest] = ["foo"];
first
//=> "foo"
rest
//=> []
const [first, ...rest] = ["foo", "bar"];
first
//=> "foo"
rest
//=> ["bar"]
const [first, ...rest] = ["foo", "bar", "baz"];
first
//=> "foo"
rest
//=> ["bar","baz"]

Para el proposito de esta exploración, vamos a suponer lo siguiente:

1
2
3
4
5
6
7
8
9
10
const isEmpty = ([first, ...rest]) => first === undefined;
isEmpty([])
//=> true
isEmpty([0])
//=> false
isEmpty([[]])
//=> false

Armados con nuestra definición de una lista vacia y con lo que ya hemos aprendido, podemos construir una gran cantidad de funciones que operan con arrays. Sabemos que podemos obtener la longitud de un array mediante Array.length. Pero como un ejercicio, ¿como podemos escribir una funcion length usando solo lo que ya tenemos?

Primero, especificamos lo que llamamos un caso base terminal. ¿Cual es la longitud de un array vacio? 0. Asi que vamos a empezar nuestra función con la observación de que si un array esta vacio, la longitud es 0:

1
2
3
4
const length = ([first, ...rest]) =>
first === undefined
? 0
: // ???

Ahora necesitamos algo para cuando el array no este vacio. Si un array no esta vacio y lo dividimos en dos partes, first y rest, la longitud del array va a ser: length(first) + length(rest). Pues bien, la longitud de first es 1, debido a que hay solo un elemento en la parte delantera. Pero no sabemos la longitud de rest. Si solo hubiese una función a la cual llamar… ¡Lo tengo! length!.

1
2
3
4
const length = ([first, ...rest]) =>
first === undefined
? 0
: 1 + length(rest);

Probemos esto!.

1
2
3
4
5
6
7
8
length([])
//=> 0
length(["foo"])
//=> 1
length(["foo", "bar", "baz"])
//=> 3

Nuestra función length es recursiva, osea que se llama a si misma. Esto tiene mucho sentido porque nuestra definición de una lista es recursivo, y si una lista es auto-similar, es natural para crear un algoritmo que sea tambien auto-similar.

recursión lineal.

La “recursión” algunas veces parece un truco de una fiesta geek. Hay incluso una broma acerca de esto.

When promising students are trying to choose between pure mathematics and applied engineering, they are given a two-part aptitude test. In the first part, they are led to a laboratory bench and told to follow the instructions printed on the card. They find a bunsen burner, a sparker, a tap, an empty beaker, a stand, and a card with the instructions “boil water.”

Of course, all the students know what to do: They fill the beaker with water, place the stand on the burner and the beaker on the stand, then they turn the burner on and use the sparker to ignite the flame. After a bit the water boils, and they turn off the burner and are lead to a second bench.

Once again, there is a card that reads, “boil water.” But this time, the beaker is on the stand over the burner, as left behind by the previous student. The engineers light the burner immediately. Whereas the mathematicians take the beaker off the stand and empty it, thus reducing the situation to a problem they have already solved.

Hay mas en las soluciones recursivas que una simple funcion que se invoca a si misma.
Los algoritmos recursivos siguen la estrategia “divide y venceras” para solventar los problemas:

  1. Divide los problemas en problemas mas pequeños.
  2. Si un problema tiene una solucion mas pequeña, entonces se resuelve el problema mas pequeño.
  3. Si un problema pequeño no es facilmente solucionable, entonces “divide el problema en otros mas pequeños y conquistalo”.
  4. Cuando se han resuelto todos los problemas pequeños, entonces componerlos juntos en una gran solucion.

Los grandes elementos de “divide y venceras” son un metodo para descomponer un problema en problemas mas pequeños y manejables, una prueba para el problema mas pequeño posible, y un medio para poner las piezas juntas.

Nuestras soluciones son un poco mas simples en las que en realidad no rompemos un problema en multiples piezas, rompemos una pieza del problema cuando puede o no ser solucionable, y la resolvemos antes de pegarla en una solucion para el resto del problema.

Un muy buen algoritmo recursivo es aquel que es paralelo a la naturaleza recursiva de los datos que estan siendo manipulados. Esta forma simple de “divide y venceras” es comunmente llamada “recursion lineal”. Es muy util y facil de entender, y es paralelo a la definicion lineal self-similar que hicimos para las listas. Tomemos otro ejemplo. Hay veces que queremos aplanar un array, esto es, un array de arrays que necesita ser convertido en una serie de elementos que no son matrices.

Ya sabemos como dividir las matrices en trozos mas pequeños. ¿Como podemos decidir si un problema tiene o no una solucion mas pequeña?. Necesitamos una prueba para el caso base. Afortunadamente, hay algo a lo largo de estas lineas proporcionado para nosotros.

1
2
3
4
5
Array.isArray("foo")
//=> false
Array.isArray(["foo"])
//=> true

El caso base sera que intentar aplanar un array vacio producira un array vacio. El siguiente caso base es que si un elemento no es un array pues que no se aplane, y lo pondremos junto con el resto de los elementos en la solución directamente. Mientras que si un elemento si es un array, lo vamos a aplanar y lo pondremos junto con el resto de nuestra solución.

Asi que de un primer vistazo, nuestra funcion flatten puede verse asi:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const flatten = ([first, ...rest]) => {
if (first === undefined) {
return [];
}
else if (!Array.isArray(first)) {
return [first, ...flatten(rest)];
}
else {
return [...flatten(first), ...flatten(rest)];
}
}
flatten(["foo", [3, 4, []]])
//=> ["foo",3,4]

Una vez más, la solucion muestra directamente los elementos importantes: Dividir el problema en subproblemas mas pequeños, detectar cuales son los casos base (cuando se considerara que la funcion debe finalizar), resolver los casos base antes que nada y componer la solucion a partir de los casos base antes resueltos.

Mapear (Mapping).

Otro problema común es aplicar una función a cada elemento de un array. Javascript cuenta con una funcion interna para eso, pero vamos a escribir nuestra propia especificacion para ilustrar el uso de la recursion lineal.

Si quisieramos elevar al cuadrado cada numero en una lista, podriamos escribir:

1
2
3
4
5
6
7
8
const squareAll = ([first, ...rest]) =>
first === undefined
? []
: [first * first, ...squareAll(rest)];
squareAll([1, 2, 3, 4, 5])
//=> [1,4,9,16,25]

Y si quisieramos saber que elementos de un array son true o false podriamos escribir:

1
2
3
4
5
6
7
const truthyAll = ([first, ...rest]) =>
first === undefined
? []
: [!!first, ...truthyAll(rest)];
truthyAll([null, true, 25, false, "foo"])
//=> [false,true,true,false,true]

Este caso especifico de recursividad lineal es llamado “maping” (o mapeo), y no es necesario escribir constantemente el mismo patron una y otra vez. Las funciones en javascript pueden tener funciones como argumentos, asi que vamos a “extraer” lo que hay que hacer para cada elemento en una funcion aparte, y separarlo de la actividad tomando un array aparte, haciendo lo que tenga que hacer, y juntando los elementos modificados en otro array.

Dada la signatura:

1
const mapWith = (fn, array) => // ...

Podriamos escribir esto usando un operador ternario. Incluso en esta pequeña funcion, podemos identificar facilmente el caso base, la pieza que se rompe, y recomponer la solución.

1
2
3
4
5
6
7
8
9
10
const mapWith = (fn, [first, ...rest]) =>
first === undefined
? []
: [fn(first), ...mapWith(fn, rest)];
mapWith((x) => x * x, [1, 2, 3, 4, 5])
//=> [1,4,9,16,25]
mapWith((x) => !!x, [null, true, 25, false, "foo"])
//=> [false,true,true,false,true]

Pliegues (Folding)

Con la excepción del ejemplo de length que hicimos al comienzo, todos nuestros ejemplos hasta ahora implican la reconstruccion de una solución usando extensiones. Pero ellos no la necesitan. Una función para calcular la suma de los cuadrados de una lista de números podría tener este aspecto:

1
2
3
4
5
6
7
const sumSquares = ([first, ...rest]) =>
first === undefined
? 0
: first * first + sumSquares(rest);
sumSquares([1, 2, 3, 4, 5])
//=> 55

Aqui hay dos diferencias entre sumSquares y nuestros mapas anteriores.

  1. Dado un caso base de una lista vacia, retornamos un 0 en lugar de una lista vacia, y;
  2. Concatenamos el cuadrado de cada elemento al resultado de aplicar sumSquares al resto de los elementos.

Vamos a reescribir mapWith para que podamos utilizar esto para sumar cuadrados.

1
2
3
4
const foldWith = (fn, terminalValue, [first, ...rest]) =>
first === undefined
? terminalValue
: fn(first, foldWith(fn, terminalValue, rest));

y ahora que tenemos una funcion que hace un poco mas que nuestra funcion map.

1
2
3
foldWith((number, rest) =>
number * number + rest, 0, [1, 2, 3, 4, 5])
//=> 55

Nuestra función foldWith es una generalización de nuestra función mapWith. Podemos representar un mapa como un pliegue, solo tenemos que suministrar el array y recompilar el codigo.

1
2
3
4
5
const squareAll = (array) =>
foldWith((first, rest) => [first * first, ...rest], [], array);
squareAll([1, 2, 3, 4, 5])
//=> [1,4,9,16,25]

Y si nos gusta, podemos escribir mapWith usando foldWith:

1
2
3
4
5
6
const mapWith = (fn, array) =>
foldWith((first, rest) => [fn(first), ...rest], [], array);
const squareAll = (array) => mapWith((x) => x * x, array);
squareAll([1, 2, 3, 4, 5])
//=> [1,4,9,16,25]

Y para volver a nuestro primer ejemplo, nuestra version de length, podemos reescribirlo como un pliegue:

1
2
3
4
5
const length = (array) =>
foldWith((first, rest) => 1 + rest, 0, array);
length([1, 2, 3, 4, 5])
//=> 5

¿Que significa todo esto?

Algunas estructuras de datos, como las listas, pueden ser definidas como “auto similares”, cuando se trabaja con estructuras de datos auto-similares, un algoritmo recursivo es paralelo a la auto similitud de los datos.

La recursividad lineal es un componente basíco de los algoritmos. Estas formas basícas son paralelas a las estructuras de datos de forma lineal como la construcción de listas: Esto ayuda a que sea comprensible. Sus casos especiales de mapeos y folding son especialmente utiles y se pueden utlizar para construir otras funciones. Y finalmente, mientras que folding es un caso especial de recursion lineal, mapping es un caso especial de folding.

Y por ultimo pero no menos importante, la desestructuración, extensión, y la recoleccion hacen que todo esto sea muy natural de expresar en JavaScript ES-6.

Comentarios

PointFree en javascript

logo

Javascript es realmente muy flexible. Tiene caracteristicas funcionales como de orientacion a objetos y permite la programacion en una gran cantidad de estilos diferentes.
En este post vamos a ver lo que se llama el estilo de programación PointFree y vamos a ir a trabes de algunos escenarios comunes para demostrar sus beneficios.

Un paso atras.

En primer lugar permitanme mostrarles la forma imperativa estandar para extraer informacion de una matriz. El bucle for.

1
2
3
4
5
6
7
8
9
10
// Estilo imperativo
var getAdminEmails = function(users) {
var emails = [];
for (var i = 0; i < users.length; i++) {
if (users[i].role === 'admin') {
emails.push(users[i].email);
}
}
return emails;
}

¿Qué hace esta función? Vamos a analizarla. Tengo un array de emails vacío. Luego recorro todos los usuarios y verifico si rol de un usuario es igual a “admin”, y si es asi añadimos el correo electronico del usuario al array y retornamos el array. ¿bastante familiar no?.

Aunque acabo de describir el codigo de la funcion, todavia no esta muy claro lo que hace. Una manera mucho mas clara de decir lo que haria seria asi:
devuelve todos los mensajes de correo electronico de los usuarios con rol de administrador

Vamos a reescribir esto de una manera un poco mas funcional, utilizando las funciones propias de Javascript como filter y map. (Estoy usando la syntaxis ECS6, tienes que amar estas funciones de flecha) xd.

1
2
3
4
var getAdminEmails = users =>
users
.filter(u => u.role === 'admin')
.map(u => u.email);

El analisis de esta funcion es mucho mas facil.

  1. Se obtienen solamente los usuarios con el rol del admin.
  2. luego se reciben solamente los mensajes de correo electronico.

El primer punto fuerte de este estilo de programacion es que el codigo de la funcion se hacerca mucho mas a la descripcion de lo que hace la funcion . Esto hace que sea mucho mas facil entenderla y razonar sobre ella, y al ver una nueva pieza de codigo se comprende rapidamente lo que se supone que debe hacer dicha funcion sin la necesidad de entrar en detalle sobre como lo hace.

Los filtros y los mapas nos sirven para iterar sobre los elementos de un array, al igual que el bucle for, pero tienen propositos especificos: regresan subconjuntos y transformaciones de elementos , respectivamente.
Por el contrario, cualquier cosa puede suceder dentro de un bucle for. Eso significa que cada vez que te encuentres con un codigo for se tendra que investigar un poco mas para saber si lo que se desea obtener es un subconjunto o no, si se transformara un elemento , si se agregaran los valores , o cualquier combinacion de estos resultados.

Cuando se utilizan filtros y mapas, uno tiene una comprension mas rapida sobre la funcion, y se puede profundizar en la comprension poco a poco a medida que lo necesite. Lo se getAdminEmails retorna un array, ya que tiene filter y map. En una segunda mirada uno ve que primero filtra a los usuarios y luego los transforma.
Ahora uno puede investigar solamente filter y entender que elementos van a ser transformados y comprender que sucede durante la transformacion en si.

Componibilidad

Lo que me parece muy interesante sobre este codigo en la version funcional, es que pude entender facilmente pieza por pieza lo que hace en las diferentes capas.
Voy a tratar de hacerlo aun mas claro ahora.

Vamos a comenzar por tratar de escribir el codigo lo mas cerca posible a lo que hace la funcion. Queremos transformar una lista filtrada, asique vamos a empezar por la composicion de estas 2 piezas.

1
2
3
var getAdminEmails = compose(
getTheEmailsOf,
onlyTheAdminRoleUsers);

compose es una funcion que toma 2 funciones como argumento, y ejecuta una despues de la otra.

su definicion mas simple es asi:

1
2
3
4
5
6
7
8
var compose = (f, g) => x => f(g(x));
//o en ECS5
var compose = function(f,g) {
return function(x) {
return f(g(x));
}
}

Ahora vamos a ver las 2 funciones que van a estar compuestas.

getTheEmailsOf es una funcion que transforma una lista retornando la propiedad email. “Transformar una lista” es lo que hace map y obtener una propiedad de un objeto suena como una tarea muy comun, asique vamos a implementar map desde un punto de vista un poco diferente y prop:

1
2
3
4
5
6
7
var prop = p => x => x[p];
var map = f => list =>
list.map(f);
// Esto da lugar a:
var getTheEmailsOf = map(prop('email'));

onlyTheAdminRoleUsers es un filtro. La prueba para decidir que elementos seran retornados implica probar si una propiedad es igual a algun valor.

1
2
3
4
5
6
7
8
var propEq = v => p => obj =>
prop(p)(obj) === v;
var filter = f => list =>
list.filter(f);
var onlyTheAdminRoleUsers =
filter(propEq('admin')('role'));

Y la version definitiva de la funcion:

1
2
var getAdminEmails = compose(
map( prop('email')),filter(propEq('admin')('role')));

Esto es lo que se llama programacion PointFree o programacion tacita.
Lo que mas sorprende de esta version es que esta enteramente compuesta de otras mas pequeñas genericas y reutilizable funciones. Estas funciones no solamente le ayudaran con sus proximas funciones, sino que le ayudara a comprender mas rapido cualquier funcion.
Despues de saber lo que hace prop, se hace mas facil de entender prop(“algo”) que obj => obj.algo ademas de que no es necesario entrar en el estress de tener que elegir un nombre para la variable temporal obj.

Nota: La mayoria de estas funciones mas pequeñas son genericas y lo suficientemente utiles para utilizarlas en casi todos los proyectos. Ramda es una libreria que tiene exactamente eso: un monton de funciones muy pequeñas y genericas.

Otro ejemplo.

Para ir terminando vamos a ver otra situacion bastante comun.

1
2
3
var calculateTotalPromotions = cart =>
getPromotions(cart.products[0])
.then(calculateTotal);

Este codigo lo podemos entender rapidamente debido a la familiaridad. Pero el flujo del codigo es por todo el lugar. Aqui lo que se hace es: Calcular el total de las promociones del primer producto del carrito de compras.

Mira el orden de esta ultima frase, y compara el orden del codigo anterior con la siguiente funcion:

1
2
3
4
5
6
// composeP es una funcion compose que acepta promesas
var calculateTotalPromotions = composeP(
calculateTotal,
getPromotions,
head, // head retorna el primer elemento.
prop('products'));

Leer el codigo y entender lo que va a ocurrir es mucho mas sencillo! :)

Conclusion

PointFree programming tiene que ver con la modularizacion de funciones a trabes de la composicion. El uso de funciones mas pequeñas, genericas, bien definidas y bien probadas para construir las funciones que necesitamos. Dentro de compose o composeP, una funcion es llamada, luego la otra, y esto es lo que pasa. No importa si la funcion tiene 17 lineas o 3. Esto funciona siempre de la misma manera, y la complejidad del codigo no se incrementa.

Tampoco tenemos porque preocuparnos acerca de las variables temporales, lo que hace que sea mucho mas facil de entender el codigo y mas dificil introducir errores. Ademas, es mas facil de entender y poner a prueba las partes mas pequeñas del codigo, lo que hace que sea mas fiable.

Por supuesto que no es una bala de plata, y existen una gran cantidad de veces que uno termina escribiendo una funcion que no es totalmente pointfree. Me parece que su principal debilidad se produce cuando la funcion tiene mas de un parametro; muy a menudo se llega a tener un codigo no pointfree. Pero, dicho esto, mi experiencia personal es que la parte pointfree del codigo es mucho mas robusta y los errores son identificados facilmente durante la falla de desarrollo. Los errores casi nunca se encuentran en codigo pointfree!!.

Comentarios

Secretos del texto en vim.

Objetos de texto en Vim (como el maestro yoda lo usa).

[texto en vim

En este articulo voy a mostrarte como manejar el texto en vim mas fácilmente.

Una de las ventajas que vim nos pone a disposicion es el modo en que ve el contenido del archivo que estamos editando.
Si bien para nosotros “el texto es texto”, para vim no es tan asi.

Vim entiende y trata el texto no como una simple cadena de caracteres como la mayoria de los editores, sino que lo “ve” como objetos.
Esto nos provee de varias ventajas frente a otros editores de texto. ¿Porque? pues porque al saber de que tipo de texto se trata vim puede manejarlo de diferentes maneras.

Imaginate que en tu editor de textos tenes que moverte entre parrafos enteros solamente, ¿que tan facil te resulta hacer eso?. Si tienes que usar el mouse ya pierdes tiempo, y si tienes que pulsar varias teclas tambien… En otro caso ¡Seguro que estas usando vim! :)

En este articulo no pretendo poner la tipica lista de super comandos de vim con lo que haces un monton de cosas, sino enseñarte que todas esas combinaciones no tenes porque aprenderlas de memoria ya que (aunque no lo parezca) tienen un sentido y un significado que no es para nada dificil de aprender.

Lo primero que necesitamos aprender es ver como vim identifica el texto para que podamos movernos mas facilmente sobre el.

1
2
3
palabra = word en ingles
fin = end en ingles
atras = back.. (adivina en que idioma..)
1
2
sentencia = sentence Usa '(' o ')' para navegar entre sentencias
parrafo = paragraph Usa '{' o '}' para navegar entre parrafos

Ok ok, eso ya lo se geniecillo… ¿y que con eso?, pues que vim usa las siglas
de esas palabras como comandos… :P

Osea que si deseas moverte entre palabras, lo unico que tenes que hacer es presionar en
modo comandos la letra w (de word), si deseas ir una palabra hacia atras
presiona b (de back ), si deseas ir al final de una palabra simplemente presiona
e (de end). Aja! ¿y que pasa con las sentencias? pues para las sentencias y
parrafos vim tambien usa las siglas (pero de otra manera).

Simplemente quedate con el concepto de que te podes mover mas facilmente usando ( para las
sentencias y { para los parrafos por ahora… :)

Y de que me sirve saber todo esto?. Ya lo vas a ver no te impacientes…

Resulta que vim tambien usa siglas para referirse a los comandos mas usados,
siguiendo con las tablitas estas serian:

1
2
3
4
5
borrar = d -> delete en ingles
copiar = y -> yank en ingles
cambiar = c -> change en ingles
buscar = f -> find en ingles
visual = v -> visual ¿impensable verdad?

mmm ya estas captando el concepto verdad?

Siguiendo con el ejemplo…

1
2
3
4
5
6
7
8
9
d para borrar
c para cambiar.
y para copiar.
f para buscar una letra en la linea.
v para seleccionar.

Existen mas ordenes que no las listo aca para no salirnos demasiado de tema,
pero que son de mucha utilidad conocer.

Si presionas alguno de las ordenes anteriores vas a notar que no pasa nada
(excepto en la de seleccionar “ v “ y en la de buscar una letra “ f “).

Esto pasa porque estas ordenes esperan que pulsemos una tecla de movimiento las
cuales pueden ser las que vimos arriba ( w b e s p ).

Por ejemplo si quisieramos borrar una palabra tendriamos que pulsar dw o cw si
deseamos cambiarla…

Pero aun hay mas asique no te apresures joven padawan…

baby
Los objetos de texto en vim tienen 2 modos de trabajar.

1
2
i = inner
a = a o un o una etc...

Estos 2 ¿nuevos? operadores le dicen a vim el ambito donde queremos realizar la operacion.

inner significa interior

a tiene varios significados pero para el caso significa incluyendo.

Osea que si tenemos esta linea.

>”Una linea de texto”<
y tecleamos la orden da” dentro de la linea se eliminaria todo el texto (junto con las comillas).
En caso contrario si tecleamos di” se eliminaria solamente el texto dentro de
las comillas pero no las comillas, quedando asi:
>< Comando ( da” )

>””< Comando ( di” )
De esta manera podemos formar comandos y recordarlos muy facilmente como vemos:

1
2
3
4
5
6
diw se traduce como delete inner word (eliminar la palabra interior.
daw se traduce como delete a word (eliminar una palabra)
dis se traduce como delete inner sentence (eliminar una sentencia pero dejando los espacios alrededor )
das se traduce como delete a sentence (eliminar una sentencia)
dip se traduce como delete inner paragraph (eliminar un parrafo excluyendo el espacio previo)
dap se traduce como delete a paragraph (eliminar un parrafo)

Ok ok pero si eso es todo te mato ¡quiero mas!! :)

mas.jpeg

1
dib se traduce como delete inner block '(' ')' ()' (eliminar todo el bloque incluyendo los parentesis

Estas ordenes pueden hacerse tambien de forma explicita, por ejemplo:

1
2
3
4
5
6
7
8
di" Borraria todo lo que este dentro de """ pero excluiria las comillas
da" Borraria todo lo que este dentro de """ incluyendo las comillas
di[ Borraria todo lo que este dentro de [] pero excluiria los corchetes
da[ Borraria todo lo que este dentro de [] incluyendo los corchetes.
di{ Borraria todo lo que este dentro de {} incluyendo los corchetes.
da{ Borraria todo lo que este dentro de {} pero excluiria los corchetes
di( Borraria todo lo que este dentro de () incluyendo los corchetes.
da( Borraria todo lo que este dentro de () pero excluiria los corchetes

¿y si deseo borrar una etiqueta html o xml como hago?

simple, supongamos la siguiente etiqueta:

1
<div id="contenedor"><article> Mi articulo </article></div>

Si nos posicionamos en alguna parte de la declaracion del div y ejecutamos dit
vim se va a dar cuenta de que lo que queremos borrar es todo lo que esta dentro
de del div dejandolo asi:

1
<div id="contenedor"></div>

Por el contrario, si nos posicionamos en alguna parte de la declaracion del
article y ejecutamos dit vim va a borrar todo el contenido del articulo
dejandolo asi:

1
<div id="contenedor"><article></article></div>

¿genial no?

1
2
dit Borra todo el contenido que este dentro del tag
dat Borra todo el tag

mmmm y solo puedo borrar texto en vim? bahh…

Por supuesto que no solamente podes borrar texto en vim ¿de que serviria si fuera solo eso?

Con los comandos que aprendimos recien podemos borrar facilmente objetos de
texto, ahora si remplazamos la orden de eliminar d por la c de change la cosa
tomaria todavia mas color ya que no necesitariamos estar presionando i en cada
momento para pasar al modo insercion, y adivina que.. ¡Todo lo que vimos para
borrar se aplica para cambiar!!

Si desearamos cambiar una sentencia seria:

1
2
3
4
cis change inner sentence
cas change a sentence
cip change inner paragraph
cap change a paragraph

etc… y como te imaginaras todo el resto es igual.. :)

mmm hasta ahora me venis convenciendo… ¿pero que mas puede hacer con el texto vim?

Bueno, las ordenes de movimiento son siempre las mismas como te habras dado cuenta, (espero sino…)

w = word para moverse entre palabras.

b = back para ir al comienzo de una palabra.

e = end para ir al final de una palabra.

f = find para ir a la posicion del caracter que presionemos

s = sentence para referirse a la sentencia completa.

( para moverse desde donde estamos al comienzo de la sentencia.

) para moverse desde donde estamos al final de la sentencia.

{ para moverse al comienzo del parrafo

} para moverse al final del parrafo.

Y que pasaria si usaramos estos comandos de movimiento para seleccionar el texto en vim?, ¿funcionaria? ¡pues claro!

viw y vaw seleccionan una palabra (incluyendo o no los espacios segun sea i o a )

vip y vap seleccionan un parrafo completo.

vis y vas seleccionan una sentencia completa.

vi( y va( seleccionan una sentencia a partir de la posicion actual hasta el comienzo.

vi) y va) seleccionan una sentencia a partir de la posicion actual hasta el final.

vi{ y va{ seleccionan un parrafo a partir de la posicion actual hasta el comienzo.

vi} y va} seleccionan un parrafo a partir de la posicion actual hasta el final.

En el modo de seleccion, existe un operador que sirve para decirle a vim hacia
adonde orientar el cursor de seleccion, este operador es la o y con pulsarla
simplemente, veremos como vim cambia la orientacion del cursor para facilitarnos
seleccionar texto (muy util). :)

Y como te imaginaras, para copiar es el mismo rollo que con las demas
operaciones, asique te lo dejo a tu investigacion… :)

texto en vimCon este articulo pretendo aclararte un poco las dudas acerca de los objetos de
texto en Vim y mostrarte la manera sencilla de recordarlos. Como sabras quedaron muchos comandos (no tan relevantes para el tema) en el tintero, asique te aconsejo investigarlos y tenerlos siempre a mano para
asi poder editar texto en vim de una manera mas eficiente y rapida, aca te dejo algunos enlaces de interes que te pueden interesar sobre le tema,

Un pequeño manual sobre vim echo por la gente de hackxcrack muy recomendado

Un articulo sobre algunos comandos de vim (tambien recomendable)

Un tutorial interactivo para aprender vim (muy muy bueno)

Una guia de como instalar plugins facilmente en vim

Un tutorial de EasyMotion (para moverse fácilmente por el texto)

 

espero que no te hayas aburrido mucho y que lo hayas disfrutado leyendo tanto como yo
haciendolo. ¡Vimvenido a la fuerza! :)

Comentarios