DEV Community

Heiker
Heiker

Posted on Edited on Originally published at vonheikemen.github.io

Transductores en javascript

ÂŋQuÃĐ pasaría si pudiÃĐramos extraer la idea detrÃĄs de operaciones como map y filter y aplicarlas a otro tipo de colecciones mÃĄs allÃĄ de los arreglos? ÂŋY si les digo que puedo implementar filter una sola vez y reusar ese mismo cÃģdigo en diferentes tipos de colecciones? Esa es la premisa de los transductores. Hoy vamos a aprender quÃĐ son, cÃģmo funcionan y cÃģmo se usan.

Requerimientos

Antes de comenzar hay un par de cosas que necesitan saber:

TambiÃĐn es recomendado que tengan familiaridad con los siguientes conceptos:

  • Funciones de primera clase
  • Funciones de orden superior
  • Cierres (closures)

Y si no estÃĄn al tanto de quÃĐ significa todo eso, no se preocupen. SÃģlo deben saber que en javascript podemos tratar a las funciones como cualquier otro tipo de dato.

Comencemos.

ÂŋQuÃĐ son los transductores?

La palabra transductor tiene una larga historia. Si buscan su definiciÃģn se van a encontrar con algo como esto:

Un transductor es un dispositivo capaz de transformar o convertir una determinada manifestaciÃģn de energía de entrada, en otra diferente de salida...
-- Wikipedia

Definitivamente no estamos hablando de dispositivos físicos en este artículo. Pero sí se acerca a lo que queremos, el objetivo principal de un transductor (en nuestro contexto) serÃĄ procesar los datos de una colecciÃģn y potencialmente convertir esa colecciÃģn de un tipo de dato a otro.

Para nuestros propÃģsitos una definiciÃģn mÃĄs cercana a lo que queremos sería esta:

Transformaciones algorítmicas combinables.

Ya sÃĐ, no parece que esa tampoco ayude mucho. Bueno, la idea aquí es bÃĄsicamente combinar procesos de una manera declarativa, y tambiÃĐn que sea reusable en diferentes estructuras. Eso es todo. Pero claro es mÃĄs fÃĄcil decirlo que hacerlo.

ÂŋCÃģmo logramos todo eso?

Buena pregunta. Esto serÃĄ todo un viaje, mejor empecemos con pasos pequeÃąos. Primero preguntemos...

ÂŋPor quÃĐ?

Usemos un ejemplo para responder eso. Imaginemos un escenario comÚn. Digamos que tenemos un arreglo y queremos filtrarlo. ÂŋCÃģmo lo hacemos? Usamos el mÃĐtodo .filter.

const is_even = number => number % 2 === 0;
const data = [1, 2, 3];

data.filter(is_even);
// Array [ 2 ]
ðŸ“Ē āđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™: vonheikemen/transductores en javascript kc - āļ›āļĢāļ°āđ€āļ”āđ‡āļ™āļĢāđ‰āļ­āļ™ ðŸ“Ē āđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™: vonheikemen/transductores en javascript kc - āļ›āļĢāļ°āđ€āļ”āđ‡āļ™āļĢāđ‰āļ­āļ™

Todo se ve bien. Ahora nos llega otro requerimiento, tenemos que transformar los valores que pasan la prueba de la funciÃģn is_even. No hay problema porque podemos usar .map.

const is_even = number => number % 2 === 0;
const add_message = number => `The number is: ${number}`;

const data = [1, 2, 3];

data.filter(is_even).map(add_message);
// Array [ "The number is: 2" ]
ðŸ“Ē āđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™: vonheikemen/transductores en javascript kc - āļ›āļĢāļ°āđ€āļ”āđ‡āļ™āļĢāđ‰āļ­āļ™ ðŸ“Ē āđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™: vonheikemen/transductores en javascript kc - āļ›āļĢāļ°āđ€āļ”āđ‡āļ™āļĢāđ‰āļ­āļ™

Genial. Todo funciona bien hasta que un día, por razones que no vamos discutir, nos vemos obligados a convertir data en un Set. DespuÃĐs de hacer el cambio nos topamos con este mensaje.

Uncaught TypeError: data.filter is not a function
ðŸ“Ē āđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™: vonheikemen/transductores en javascript kc - āļ›āļĢāļ°āđ€āļ”āđ‡āļ™āļĢāđ‰āļ­āļ™ ðŸ“Ē āđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™: vonheikemen/transductores en javascript kc - āļ›āļĢāļ°āđ€āļ”āđ‡āļ™āļĢāđ‰āļ­āļ™

ÂŋCÃģmo podemos resolver esto? Una forma sería usar el ciclo for..of.

const is_even = number => number % 2 === 0;
const add_message = number => `The number is: ${number}`;

const data = new Set([1, 2, 3]);
const filtered = new Set();

for(let number of data) {
  if(is_even(number)) {
    filtered.add(add_message(number));
  }
}

filtered;
// Set [ "The number is: 2" ]
ðŸ“Ē āđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™: vonheikemen/transductores en javascript kc - āļ›āļĢāļ°āđ€āļ”āđ‡āļ™āļĢāđ‰āļ­āļ™ ðŸ“Ē āđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™: vonheikemen/transductores en javascript kc - āļ›āļĢāļ°āđ€āļ”āđ‡āļ™āļĢāđ‰āļ­āļ™

La buena noticia es que esto funciona con cualquier estructura que implemente el protocolo de iteraciÃģn. La mala noticia es que para agregar otra "operaciÃģn" tenemos que modificar el cÃģdigo de nuestro for.

ÂŋPor quÃĐ modificar el cÃģdigo sería un problema?

Hagamos una comparaciÃģn. Digamos que tenemos nuestro ciclo en su lugar.

for(let number of data) {

}
ðŸ“Ē āđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™: vonheikemen/transductores en javascript kc - āļ›āļĢāļ°āđ€āļ”āđ‡āļ™āļĢāđ‰āļ­āļ™ ðŸ“Ē āđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™: vonheikemen/transductores en javascript kc - āļ›āļĢāļ°āđ€āļ”āđ‡āļ™āļĢāđ‰āļ­āļ™

ÂŋQuÃĐ hacemos cuando queremos filtrar? Agregamos cÃģdigo dentro del bloque.

  for(let number of data) {
+   if(is_even(number)) {
+     filtered.add(number);
+   }
  }
ðŸ“Ē āđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™: vonheikemen/transductores en javascript kc - āļ›āļĢāļ°āđ€āļ”āđ‡āļ™āļĢāđ‰āļ­āļ™ ðŸ“Ē āđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™: vonheikemen/transductores en javascript kc - āļ›āļĢāļ°āđ€āļ”āđ‡āļ™āļĢāđ‰āļ­āļ™

ÂŋQuÃĐ hacemos cuando queremos transformar? Agregamos cÃģdigo dentro del bloque.

  for(let number of data) {
    if(is_even(number)) {
-     filtered.add(number);
+     filtered.add(add_message(number));
    }
  }
ðŸ“Ē āđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™: vonheikemen/transductores en javascript kc - āļ›āļĢāļ°āđ€āļ”āđ‡āļ™āļĢāđ‰āļ­āļ™ ðŸ“Ē āđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™: vonheikemen/transductores en javascript kc - āļ›āļĢāļ°āđ€āļ”āđ‡āļ™āļĢāđ‰āļ­āļ™

Eso va ocurrir cada vez que queramos agregar alguna funcionalidad a nuestro ciclo. ÂŋAlguna vez han escuchado la frase "abierto para extensiÃģn, cerrado para modificaciÃģn"? Es bÃĄsicamente lo que quiero ilustrar aquí. Para extender el ciclo for necesitamos modificarlo, no es que sea una terrible idea, es sÃģlo que hay una forma mÃĄs "elegante" de lograr nuestro objetivo.

Revisemos nuevamente nuestra primera versiÃģn, la que tenía data como un Array. ÂŋQuÃĐ hacemos cuando necesitamos filtrar? Agregamos una funciÃģn.

data.filter(is_even);
ðŸ“Ē āđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™: vonheikemen/transductores en javascript kc - āļ›āļĢāļ°āđ€āļ”āđ‡āļ™āļĢāđ‰āļ­āļ™ ðŸ“Ē āđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™: vonheikemen/transductores en javascript kc - āļ›āļĢāļ°āđ€āļ”āđ‡āļ™āļĢāđ‰āļ­āļ™

ÂŋQuÃĐ hacemos cuando queremos transformar? Agregamos una funciÃģn.

- data.filter(is_even);
+ data.filter(is_even).map(add_message);
ðŸ“Ē āđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™: vonheikemen/transductores en javascript kc - āļ›āļĢāļ°āđ€āļ”āđ‡āļ™āļĢāđ‰āļ­āļ™ ðŸ“Ē āđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™: vonheikemen/transductores en javascript kc - āļ›āļĢāļ°āđ€āļ”āđ‡āļ™āļĢāđ‰āļ­āļ™

ÂŋVen a donde quiero llegar? No voy a decir que es mejor, sÃģlo digamos que es mÃĄs "expresivo". En este caso, para extender nuestro proceso lo que hacemos es combinar funciones.

Pero no todo es color de rosas. Ya nos topamos con un problema: no todas las colecciones implementan estos mÃĐtodos. Y otro problema que podríamos enfrentar tiene que ver con el desempeÃąo, porque cada mÃĐtodo es el equivalente a un ciclo for. Así que tal vez no sea una buena idea hacer una larga cadena de filters y maps.

Aquí es donde entran los transductores, con ellos podemos construir una cadena de operaciones de una manera declarativa y eficiente. Aunque no serÃĄn tan rÃĄpidos como un ciclo for, puede ser una manera de aumentar el desempeÃąo cuando tienen una larga cadena de operaciones actuando sobre una colecciÃģn con muchos (muchos) elementos.

Otra cosa en la que destacan sobre los mÃĐtodos tradicionales en el prototipo Array es que podemos reusar la misma operaciÃģn en distintas estructuras. Podemos por ejemplo implementar filter como un transductor una vez y reusamos ese mismo cÃģdigo para los arreglos, Sets, generadores u otro tipo de colecciones. Suena genial, Âŋcierto?

ÂŋCÃģmo funcionan?

La magia detrÃĄs de los transductores se encuentra dentro de un concepto que mencionÃĐ en la secciÃģn de requerimientos: reducer. Especificamente reducers de orden superior. (Se los advertí).

Tomen un momento para digerir esta frase: "reducer de orden superior."

ÂŋEstÃĄn listos?

Por ahora podemos pensar en un transductor como una funciÃģn que puede recibir un reducer como argumento y retorna otro reducer. Resulta que (con un poco de magia) podemos combinar reducers usando composiciÃģn de funciones. Esta propiedad es la que nos va a permitir armar cadenas de operaciones como en nuestro ejemplo anterior, donde llamamos al mÃĐtodo .filter y luego .map. Pero hay una gran diferencia, la manera en la que se combinan de hecho debería ser algo así.

compose(filter(is_even), map(add_message));
ðŸ“Ē āđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™: vonheikemen/transductores en javascript kc - āļ›āļĢāļ°āđ€āļ”āđ‡āļ™āļĢāđ‰āļ­āļ™ ðŸ“Ē āđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™: vonheikemen/transductores en javascript kc - āļ›āļĢāļ°āđ€āļ”āđ‡āļ™āļĢāđ‰āļ­āļ™

Antes de que pregunten, la magia no ocurre en compose. Esa funciÃģn es bastante genÃĐrica. Lo Único que hace es pasar el resultado de una funciÃģn a la otra. Podemos implementarla nosotros mismos.

function compose(...fns) {
  const apply = (arg, fn) => fn(arg);
  return (initial) => fns.reduceRight(apply, initial);
}
ðŸ“Ē āđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™: vonheikemen/transductores en javascript kc - āļ›āļĢāļ°āđ€āļ”āđ‡āļ™āļĢāđ‰āļ­āļ™ ðŸ“Ē āđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™: vonheikemen/transductores en javascript kc - āļ›āļĢāļ°āđ€āļ”āđ‡āļ™āļĢāđ‰āļ­āļ™

Ahora, cuando combinamos varios transductores con compose lo que obtenemos es otro transductor. Pero ese no es el fin de la historia, como ya mencionÃĐ un transductor nos devuelve un reducer, ÂŋY quÃĐ funciÃģn conocen ustedes que necesite un reducer? Por supuesto, nuestro amigo reduce. reduce serÃĄ como el "protocolo" que usaremos para recorrer los valores de la colecciÃģn y hacer algo con ellos.

Creo que ya es suficiente de teorías, vamos a la prÃĄctica. Para empezar vamos a crear un transductor con la misma funcionalidad de filter.

Creando un transductor

Paso 1: Reunir los argumentos necesarios

Primero lo primero, creamos una funciÃģn y obtenemos todo lo que necesitamos con los parÃĄmetros. ÂŋQuÃĐ necesitamos en este caso? Una funciÃģn que debería retornar true o false, un predicado.

function filter(predicate) {

}
ðŸ“Ē āđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™: vonheikemen/transductores en javascript kc - āļ›āļĢāļ°āđ€āļ”āđ‡āļ™āļĢāđ‰āļ­āļ™ ðŸ“Ē āđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™: vonheikemen/transductores en javascript kc - āļ›āļĢāļ°āđ€āļ”āđ‡āļ™āļĢāđ‰āļ­āļ™

Un buen comienzo pero no es suficiente. Sabemos que en algÚn momento vamos a combinar este transductor con otro. Lo que necesitamos ahora es aceptar otro reducer, que vendría siendo el siguiente "paso" en la composiciÃģn. Vamos a agregar eso.

function filter(predicate, next) {

}
ðŸ“Ē āđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™: vonheikemen/transductores en javascript kc - āļ›āļĢāļ°āđ€āļ”āđ‡āļ™āļĢāđ‰āļ­āļ™ ðŸ“Ē āđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™: vonheikemen/transductores en javascript kc - āļ›āļĢāļ°āđ€āļ”āđ‡āļ™āļĢāđ‰āļ­āļ™

Si aÚn no estÃĄ claro, recordemos nuestro ejemplo anterior.

compose(filter(is_even), map(add_message));
ðŸ“Ē āđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™: vonheikemen/transductores en javascript kc - āļ›āļĢāļ°āđ€āļ”āđ‡āļ™āļĢāđ‰āļ­āļ™ ðŸ“Ē āđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™: vonheikemen/transductores en javascript kc - āļ›āļĢāļ°āđ€āļ”āđ‡āļ™āļĢāđ‰āļ­āļ™

Aquí lo que va a pasar es que map(add_message) nos devolverÃĄ un reducer. Ese reducer se convertirÃĄ en nuestro parÃĄmetro next.

Ya sÃĐ lo que piensan, sÃģlo estoy enviando el argumento is_even. ÂŋCÃģmo vamos a obtener next? Vamos a lidiar con eso despuÃĐs. Sigamos.

Paso 2: Retornar un reducer

En la prÃĄctica un reducer no es mÃĄs que una funciÃģn binaria. SÃģlo necesitamos retornar eso.

function filter(predicate, next) {
  return function reducer(state, value) {
    // ???
  };
}
ðŸ“Ē āđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™: vonheikemen/transductores en javascript kc - āļ›āļĢāļ°āđ€āļ”āđ‡āļ™āļĢāđ‰āļ­āļ™ ðŸ“Ē āđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™: vonheikemen/transductores en javascript kc - āļ›āļĢāļ°āđ€āļ”āđ‡āļ™āļĢāđ‰āļ­āļ™

Paso 3: Implementa el resto

Bien, ya (casi) terminamos con la estructura del transductor. Lo que viene ahora es la lÃģgica que queremos implementar. En este caso, lo que queremos hacer es replicar el comportamiento de Array.filter.

function filter(predicate, next) {
  return function reducer(state, value) {
    if(predicate(value)) {
      return next(state, value);
    }

    return state;
  };
}
ðŸ“Ē āđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™: vonheikemen/transductores en javascript kc - āļ›āļĢāļ°āđ€āļ”āđ‡āļ™āļĢāđ‰āļ­āļ™ ðŸ“Ē āđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™: vonheikemen/transductores en javascript kc - āļ›āļĢāļ°āđ€āļ”āđ‡āļ™āļĢāđ‰āļ­āļ™

Aquí tomamos el predicado, lo evaluamos, y decidimos si vamos seguir con el siguiente paso o no hacemos nada.

Paso 4: AplicaciÃģn parcial

Aquí viene la magia. Sabemos cÃģmo queremos usar filter pero justo ahora no va a funcionar. Necesitamos que filter sea lo suficientemente inteligente para saber cuando tiene que ejecutarse, ÂŋCuÃĄndo es eso? Cuando tenga todos sus argumentos.

function filter(predicate, next) {
  if(arguments.length === 1) {
    return (_next) => filter(predicate, _next);
  }

  return function reducer(state, value) {
    if(predicate(value)) {
      return next(state, value);
    }

    return state;
  };
}
ðŸ“Ē āđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™: vonheikemen/transductores en javascript kc - āļ›āļĢāļ°āđ€āļ”āđ‡āļ™āļĢāđ‰āļ­āļ™ ðŸ“Ē āđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™: vonheikemen/transductores en javascript kc - āļ›āļĢāļ°āđ€āļ”āđ‡āļ™āļĢāđ‰āļ­āļ™

Esta es sÃģlo una forma de lograr la aplicaciÃģn parcial. No tiene que ser de esta manera.

Usando un transductor

Ya tenemos algo que en teoría debería funcionar. Ahora necesitamos una funciÃģn reduce. Por suerte para nosotros el prototipo Array tiene una que podemos usar. Empecemos usando un solo transductor.

const is_even = number => number % 2 === 0;

const data = [1, 2, 3];

const combine = (state, value) => (state.push(value), state);

data.reduce(filter(is_even, combine), []);
// Array [ 2 ]
ðŸ“Ē āđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™: vonheikemen/transductores en javascript kc - āļ›āļĢāļ°āđ€āļ”āđ‡āļ™āļĢāđ‰āļ­āļ™ ðŸ“Ē āđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™: vonheikemen/transductores en javascript kc - āļ›āļĢāļ°āđ€āļ”āđ‡āļ™āļĢāđ‰āļ­āļ™

ÂĄGenial, de verdad funciona! Ahora vamos a expandir el conjunto de datos. Digamos que ahora data tendrÃĄ nÚmeros negativos, pero tampoco queremos esos, vamos a crear otro filtro que deje pasar sÃģlo los nÚmeros positivos. Aquí es donde la composiciÃģn entra en escena.

const is_even = number => number % 2 === 0;
const is_positive = number => number > 0;

const data = [-2, -1, 0, 1, 2, 3];

const combine = (state, value) => (state.push(value), state);

const transducer = compose(filter(is_positive), filter(is_even));

data.reduce(transducer(combine), []);
// Array [ 2 ]
ðŸ“Ē āđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™: vonheikemen/transductores en javascript kc - āļ›āļĢāļ°āđ€āļ”āđ‡āļ™āļĢāđ‰āļ­āļ™ ðŸ“Ē āđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™: vonheikemen/transductores en javascript kc - āļ›āļĢāļ°āđ€āļ”āđ‡āļ™āļĢāđ‰āļ­āļ™

ÂŋVieron? Obtuvimos el mismo resultado. Ahora hagamos algo mejor, vamos a aÃąadir otra "operaciÃģn."

function map(transform, next) {
  if(arguments.length === 1) {
    return (_next) => map(transform, _next);
  }

  return function reducer(state, value) {
    return next(state, transform(value));
  };
}
ðŸ“Ē āđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™: vonheikemen/transductores en javascript kc - āļ›āļĢāļ°āđ€āļ”āđ‡āļ™āļĢāđ‰āļ­āļ™ ðŸ“Ē āđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™: vonheikemen/transductores en javascript kc - āļ›āļĢāļ°āđ€āļ”āđ‡āļ™āļĢāđ‰āļ­āļ™

El comportamiento es el mismo que esperarían de Array.map. Aquí el valor es transformado antes de ir al siguiente paso. Ahora vamos a incorporarlo en el ejemplo.

const data = [-2, -1, 0, 1, 2, 3];

const transducer = compose(
  filter(is_positive),
  filter(is_even),
  map(add_message)
);

data.reduce(transducer(combine), []);
// Array [ "The number is: 2" ]
ðŸ“Ē āđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™: vonheikemen/transductores en javascript kc - āļ›āļĢāļ°āđ€āļ”āđ‡āļ™āļĢāđ‰āļ­āļ™ ðŸ“Ē āđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™: vonheikemen/transductores en javascript kc - āļ›āļĢāļ°āđ€āļ”āđ‡āļ™āļĢāđ‰āļ­āļ™

Esto es bueno, muy bueno. Hay un detalle que necesitamos atender, la compatibilidad. Les mencionÃĐ que los transductores deberían funcionar con otros tipos de colecciones aparte de Array, pero aquí usamos Array.reduce. El asunto es que para completar el panorama tenemos que controlar la funciÃģn reduce, así que haremos una.

Ya que javascript nos ofrece el protocolo de iteraciÃģn, vamos a usar eso para ahorrarnos muchas molestias en nuestro propio reduce, con esto haremos que nuestros transductores sean compatibles con mÃĄs tipos de colecciones.

function reduce(reducer, initial, collection) {
  let state = initial;

  for(let value of collection) {
    state = reducer(state, value);
  }

  return state;
}
ðŸ“Ē āđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™: vonheikemen/transductores en javascript kc - āļ›āļĢāļ°āđ€āļ”āđ‡āļ™āļĢāđ‰āļ­āļ™ ðŸ“Ē āđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™: vonheikemen/transductores en javascript kc - āļ›āļĢāļ°āđ€āļ”āđ‡āļ™āļĢāđ‰āļ­āļ™

Para probar esto cambiaremos nuestro ejemplo, data pasarÃĄ de ser un arreglo a un Set. Cambiaremos la funciÃģn combine, para que ahora estÃĐ al tanto de cÃģmo armar un Set. TambiÃĐn cambiaremos nuestro valor inicial en reduce a un Set. Lo demÃĄs seguirÃĄ igual.

const data = new Set([-2, -1, 0, 1, 2, 3]);

const combine = (state, value) => state.add(value);

const transducer = compose(
  filter(is_positive),
  filter(is_even),
  map(add_message)
);

reduce(transducer(combine), new Set(), data);
// Set [ "The number is: 2" ]
ðŸ“Ē āđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™: vonheikemen/transductores en javascript kc - āļ›āļĢāļ°āđ€āļ”āđ‡āļ™āļĢāđ‰āļ­āļ™ ðŸ“Ē āđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™: vonheikemen/transductores en javascript kc - āļ›āļĢāļ°āđ€āļ”āđ‡āļ™āļĢāđ‰āļ­āļ™

Noten que el resultado no tiene porque ser un Set, podemos transformar data a un Array si eso deseamos. Para cambiar de un tipo de colecciÃģn a otro, sÃģlo tenemos que intercambiar el valor inicial en reduce y cambiar la funciÃģn combine.

Todo funciona bien pero hay una cosa mÃĄs que podemos hacer para crear una "experiencia" mÃĄs agradable. Hagamos una funciÃģn auxiliar, transduce, para que se encargue de algunos detalles por nosotros.

function transduce(combine, initial, transducer, collection) {
  return reduce(transducer(combine), initial, collection);
}
ðŸ“Ē āđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™: vonheikemen/transductores en javascript kc - āļ›āļĢāļ°āđ€āļ”āđ‡āļ™āļĢāđ‰āļ­āļ™ ðŸ“Ē āđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™: vonheikemen/transductores en javascript kc - āļ›āļĢāļ°āđ€āļ”āđ‡āļ™āļĢāđ‰āļ­āļ™

No parece una gran mejora pero esto nos permite aumentar nuestro control sobre reduce, ahora podríamos tener varias implementaciones para diferentes estructuras y decidir cual queremos usar basados en el tipo de dato de collection. Pero por el momento sÃģlo usaremos la funciÃģn reduce que creamos anteriormente.

Ahora lo que haremos serÃĄ encargarnos de algunos detalles antes de tiempo. Crearemos funciones que tengan la misma funcionalidad de combine, para acumular los valores finales y asociamos eso con el valor inicial correcto.

function curry(arity, fn, ...rest) {
  if (arity <= rest.length) {
    return fn(...rest);
  }

  return curry.bind(null, arity, fn, ...rest);
}

const Into = {
  array: curry(2, function(transducer, collection) {
    const combine = (state, value) => (state.push(value), state);
    return transduce(combine, [], transducer, collection);
  }),
  string: curry(2, function(transducer, collection) {
    const combine = (state, value) => state.concat(value);
    return transduce(combine, "", transducer, collection)
  }),
  set: curry(2, function(transducer, collection) {
    const combine = (state, value) => state.add(value);
    return transduce(combine, new Set(), transducer, collection);
  }),
};
ðŸ“Ē āđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™: vonheikemen/transductores en javascript kc - āļ›āļĢāļ°āđ€āļ”āđ‡āļ™āļĢāđ‰āļ­āļ™ ðŸ“Ē āđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™: vonheikemen/transductores en javascript kc - āļ›āļĢāļ°āđ€āļ”āđ‡āļ™āļĢāđ‰āļ­āļ™

Ahora podemos usar aplicaciÃģn parcial en los argumentos. En esta ocasiÃģn logramos ese efecto con la funciÃģn curry. Vamos a probar.

const data = [-2, -1, 0, 1, 2, 3];

const transducer = compose(
  filter(is_positive),
  filter(is_even),
  map(add_message)
);

Into.array(transducer, data);
// Array [ "The number is: 2" ]
ðŸ“Ē āđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™: vonheikemen/transductores en javascript kc - āļ›āļĢāļ°āđ€āļ”āđ‡āļ™āļĢāđ‰āļ­āļ™ ðŸ“Ē āđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™: vonheikemen/transductores en javascript kc - āļ›āļĢāļ°āđ€āļ”āđ‡āļ™āļĢāđ‰āļ­āļ™

TambiÃĐn podemos hacer esto.

const some_process = Into.array(compose(
  filter(is_positive),
  filter(is_even),
  map(add_message)
));

some_process(data);
// Array [ "The number is: 2" ]
ðŸ“Ē āđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™: vonheikemen/transductores en javascript kc - āļ›āļĢāļ°āđ€āļ”āđ‡āļ™āļĢāđ‰āļ­āļ™ ðŸ“Ē āđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™: vonheikemen/transductores en javascript kc - āļ›āļĢāļ°āđ€āļ”āđ‡āļ™āļĢāđ‰āļ­āļ™

Pueden visualizar todo el cÃģdigo de este ejemplo aquí

Ahora poseemos "operaciones" reusables. No tuvimos que implementar un filter especial para el Array y otra para el Set. En este ejemplo no parece gran cosa, pero imagínense tener un arsenal de operaciones como RxJS, y poder usarlas en diferentes estructuras. Lo Único que deben hacer es una funciÃģn reduce. AdemÃĄs, la manera en la que combinamos estas operaciones nos invita a resolver nuestros problemas con una funciÃģn a la vez.

Hay una cosa mÃĄs que deben saber.

Esta no es su forma final

Hasta ahora he estado presentando los transductores como funciones que retornan un reducer, pero sÃģlo era para ilustrar su funcionamiento. El problema es que nuestros transductores son limitados. Hay un par de cosas que nuestra implementaciÃģn no soporta:

  • Mecanismo de inicializaciÃģn: Una manera de que un transductor pueda producir el valor inicial para el proceso.

  • InterrupciÃģn temprana: Un transductor debe ser capaz de interrumpir todo el proceso y devolver el resultado que se ha procesado hasta el momento. Algo así como el break de un ciclo for.

  • Una funciÃģn "final": BÃĄsicamente proveer un mecanismo para ejecutar una funciÃģn al final del proceso. Esto podría ser Útil para ejecutar procesos de "limpieza".

Es por cosas como esas que muchos artículos que hablan sobre transductores recomiendan encarecidamente que usen una librería.

Librerías que tienen soporte para transductores solo conozco:

Siguiendo el protocolo

Ya sabemos cÃģmo funcionan los transductores a grandes rasgos, ahora vamos a descubrir cÃģmo implementar uno de la manera correcta. Para esto vamos a seguir el protocolo establecido en la librería transducers-js.

Las reglas dicen que un transductor debe ser un objeto con la siguiente forma.

const transducer = {
  '@@transducer/init': function() {
    return /* ???? */;
  },
  '@@transducer/result': function(state) {
    return state;
  },
  '@@transducer/step': function(state, value) {
    // ???
  }
};
ðŸ“Ē āđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™: vonheikemen/transductores en javascript kc - āļ›āļĢāļ°āđ€āļ”āđ‡āļ™āļĢāđ‰āļ­āļ™ ðŸ“Ē āđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™: vonheikemen/transductores en javascript kc - āļ›āļĢāļ°āđ€āļ”āđ‡āļ™āļĢāđ‰āļ­āļ™
  • @@transducer/init: SerÃĄ la funciÃģn que nos da la oportunidad de retornar un valor inicial si por alguna razÃģn necesitamos uno. El comportamiento "por defecto" es delegar sus funciones al siguiente transductor de la composiciÃģn, con suerte alguno tendrÃĄ que devolver algo Útil.

  • @@transducer/result: SerÃĄ la funciÃģn que se ejecute al final del proceso, es decir cuando ya no haya mÃĄs valores para procesar. Al igual que @@transducer/init, el comportamiento que se espera por defecto es delegar sus funciones al siguiente transductor en la composiciÃģn.

  • @@transducer/step: Aquí es donde reside la lÃģgica para nuestro transductor, es decir la "operaciÃģn" que queremos ejecutar. BÃĄsicamente esta funciÃģn serÃĄ nuestro reducer.

AÚn no hemos terminado, tambiÃĐn necesitamos una manera de seÃąalar que el proceso serÃĄ interrumpido y regresar el resultado que se tiene en ese momento. Para esto el protocolo indica la existencia de un objeto especial que llama reduced (reducido). La idea es que cuando la funciÃģn reduce detecte este objeto se de por terminado el proceso. Este objeto debe tener la siguiente forma.

const reduced = {
  '@@transducer/reduced': true,
  '@@transducer/value': algo // el valor procesado hasta el momento
};
ðŸ“Ē āđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™: vonheikemen/transductores en javascript kc - āļ›āļĢāļ°āđ€āļ”āđ‡āļ™āļĢāđ‰āļ­āļ™ ðŸ“Ē āđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™: vonheikemen/transductores en javascript kc - āļ›āļĢāļ°āđ€āļ”āđ‡āļ™āļĢāđ‰āļ­āļ™

Un verdadero transducer

Es momento de aplicar todo lo que hemos aprendido, vamos a reimplementar filter de la manera correcta. Podemos hacerlo, la mayor parte va a ser igual.

Empezamos con una funciÃģn que retorna un objeto.

function filter(predicate, next) {
  return {

  };
}
ðŸ“Ē āđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™: vonheikemen/transductores en javascript kc - āļ›āļĢāļ°āđ€āļ”āđ‡āļ™āļĢāđ‰āļ­āļ™ ðŸ“Ē āđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™: vonheikemen/transductores en javascript kc - āļ›āļĢāļ°āđ€āļ”āđ‡āļ™āļĢāđ‰āļ­āļ™

Ahora la inicializaciÃģn, ÂŋQuÃĐ necesitamos hacer? Nada, en realidad. Entonces lo que haremos serÃĄ delegar.

  function filter(predicate, next) {
    return {
+     '@@transducer/init': function() {
+       return next['@@transducer/init']();
+     },
    };
  }
ðŸ“Ē āđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™: vonheikemen/transductores en javascript kc - āļ›āļĢāļ°āđ€āļ”āđ‡āļ™āļĢāđ‰āļ­āļ™ ðŸ“Ē āđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™: vonheikemen/transductores en javascript kc - āļ›āļĢāļ°āđ€āļ”āđ‡āļ™āļĢāđ‰āļ­āļ™

Al finalizar, ÂŋQuÃĐ necesitamos hacer? Nada. Ya saben el procedimiento.

  function filter(predicate, next) {
    return {
      '@@transducer/init': function() {
        return next['@@transducer/init']();
      },
+     '@@transducer/result': function(state) {
+       return next['@@transducer/result'](state);
+     },
    };
  }
ðŸ“Ē āđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™: vonheikemen/transductores en javascript kc - āļ›āļĢāļ°āđ€āļ”āđ‡āļ™āļĢāđ‰āļ­āļ™ ðŸ“Ē āđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™: vonheikemen/transductores en javascript kc - āļ›āļĢāļ°āđ€āļ”āđ‡āļ™āļĢāđ‰āļ­āļ™

Ahora para el gran final, la operaciÃģn en sí.

  function filter(predicate, next) {
    return {
      '@@transducer/init': function() {
        return next['@@transducer/init']();
      },
      '@@transducer/result': function(state) {
        return next['@@transducer/result'](state);
      },
+     '@@transducer/step': function(state, value) {
+       if(predicate(value)) {
+         return next['@@transducer/step'](state, value);
+       }
+
+       return state;
+     },
    };
  }
ðŸ“Ē āđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™: vonheikemen/transductores en javascript kc - āļ›āļĢāļ°āđ€āļ”āđ‡āļ™āļĢāđ‰āļ­āļ™ ðŸ“Ē āđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™: vonheikemen/transductores en javascript kc - āļ›āļĢāļ°āđ€āļ”āđ‡āļ™āļĢāđ‰āļ­āļ™

Y que no se les olvide el toque mÃĄgico.

  function filter(predicate, next) {
+   if(arguments.length === 1) {
+     return (_next) => filter(predicate, _next);
+   }

    return {
      '@@transducer/init': function() {
        return next['@@transducer/init']();
      },
      '@@transducer/result': function(state) {
        return next['@@transducer/result'](state);
      },
      '@@transducer/step': function(state, value) {
        if(predicate(value)) {
          return next['@@transducer/step'](state, value);
        }

        return state;
      },
    };
  }
ðŸ“Ē āđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™: vonheikemen/transductores en javascript kc - āļ›āļĢāļ°āđ€āļ”āđ‡āļ™āļĢāđ‰āļ­āļ™ ðŸ“Ē āđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™: vonheikemen/transductores en javascript kc - āļ›āļĢāļ°āđ€āļ”āđ‡āļ™āļĢāđ‰āļ­āļ™

Ya tenemos el transductor, pero ahora tenemos un problema: no tenemos una funciÃģn reduce capaz de utilizarlo.

reduce mejorado

Ahora nos toca hacerle unos ajustes a nuestro reduce.

Recuerdan esto.

function reduce(reducer, initial, collection) {
  let state = initial;

  for(let value of collection) {
    state = reducer(state, value);
  }

  return state;
}
ðŸ“Ē āđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™: vonheikemen/transductores en javascript kc - āļ›āļĢāļ°āđ€āļ”āđ‡āļ™āļĢāđ‰āļ­āļ™ ðŸ“Ē āđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™: vonheikemen/transductores en javascript kc - āļ›āļĢāļ°āđ€āļ”āđ‡āļ™āļĢāđ‰āļ­āļ™

Primero vamos a manejar la inicializaciÃģn.

- function reduce(reducer, initial, collection) {
+ function reduce(transducer, initial, collection) {
+   if(arguments.length === 2) {
+     collection = initial;
+     initial = transducer['@@transducer/init']();
+   }
+
    let state = initial;

    for(let value of collection) {
      state = reducer(state, value);
    }

    return state;
  }
ðŸ“Ē āđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™: vonheikemen/transductores en javascript kc - āļ›āļĢāļ°āđ€āļ”āđ‡āļ™āļĢāđ‰āļ­āļ™ ðŸ“Ē āđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™: vonheikemen/transductores en javascript kc - āļ›āļĢāļ°āđ€āļ”āđ‡āļ™āļĢāđ‰āļ­āļ™

Cuando la funciÃģn reciba dos argumentos la colecciÃģn estarÃĄ en initial y collection serÃĄ undefined, así que lo que hacemos es asignar initial a collection y darle la oportunidad a nuestro transductor de generar el estado inicial del proceso.

Ahora veremos cÃģmo ejecutar el reducer que como saben ahora estÃĄ situado en @@transducer/step.

  function reduce(transducer, initial, collection) {
    if(arguments.length === 2) {
      collection = initial;
      initial = transducer['@@transducer/init']();
    }

    let state = initial;

    for(let value of collection) {
-     state = reducer(state, value);
+     state = transducer['@@transducer/step'](state, value);
    }

    return state;
  }
ðŸ“Ē āđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™: vonheikemen/transductores en javascript kc - āļ›āļĢāļ°āđ€āļ”āđ‡āļ™āļĢāđ‰āļ­āļ™ ðŸ“Ē āđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™: vonheikemen/transductores en javascript kc - āļ›āļĢāļ°āđ€āļ”āđ‡āļ™āļĢāđ‰āļ­āļ™

Lo siguiente serÃĄ evaluar el resultado del reducer y determinar si debemos seguir con el proceso.

  function reduce(transducer, initial, collection) {
    if(arguments.length === 2) {
      collection = initial;
      initial = transducer['@@transducer/init']();
    }

    let state = initial;

    for(let value of collection) {
      state = transducer['@@transducer/step'](state, value);
+
+     if(state != null && state['@@transducer/reduced']) {
+       state = state['@@transducer/value'];
+       break;
+     }
    }

    return state;
  }
ðŸ“Ē āđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™: vonheikemen/transductores en javascript kc - āļ›āļĢāļ°āđ€āļ”āđ‡āļ™āļĢāđ‰āļ­āļ™ ðŸ“Ē āđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™: vonheikemen/transductores en javascript kc - āļ›āļĢāļ°āđ€āļ”āđ‡āļ™āļĢāđ‰āļ­āļ™

Por Último debemos asegurarnos que todas las operaciones sepan que el proceso ha terminado.

  function reduce(transducer, initial, collection) {
    if(arguments.length === 2) {
      collection = initial;
      initial = transducer['@@transducer/init']();
    }

    let state = initial;

    for(let value of collection) {
      state = transducer['@@transducer/step'](state, value);

      if(state != null && state['@@transducer/reduced']) {
        state = state['@@transducer/value'];
        break;
      }
    }

-
-   return state;
+   return transducer['@@transducer/result'](state);
  }
ðŸ“Ē āđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™: vonheikemen/transductores en javascript kc - āļ›āļĢāļ°āđ€āļ”āđ‡āļ™āļĢāđ‰āļ­āļ™ ðŸ“Ē āđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™: vonheikemen/transductores en javascript kc - āļ›āļĢāļ°āđ€āļ”āđ‡āļ™āļĢāđ‰āļ­āļ™

Hay un paso extra que me gustaría hacer. Tal vez notaron que renombrÃĐ reducer a transducer, pero me gustaría que siguiera funcionando con reducers normales, como los que se usa con Array.reduce. Entonces lo que haremos serÃĄ crear un transductor que pueda transformar un reducer en un transductor.

function to_transducer(reducer) {
  if(typeof reducer['@@transducer/step'] == 'function') {
    return reducer;
  }

  return {
    '@@transducer/init': function() {
      throw new Error('Method not implemented');
    },
    '@@transducer/result': function(state) {
      return state;
    },
    '@@transducer/step': function(state, value) {
      return reducer(state, value);
    }
  };
}
ðŸ“Ē āđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™: vonheikemen/transductores en javascript kc - āļ›āļĢāļ°āđ€āļ”āđ‡āļ™āļĢāđ‰āļ­āļ™ ðŸ“Ē āđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™: vonheikemen/transductores en javascript kc - āļ›āļĢāļ°āđ€āļ”āđ‡āļ™āļĢāđ‰āļ­āļ™

Ahora podemos usarla en reduce.

  function reduce(transducer, initial, collection) {
+   transducer = to_transducer(transducer);
+
    if(arguments.length === 2) {
      collection = initial;
      initial = transducer['@@transducer/init']();
    }

    let state = initial;

    for(let value of collection) {
      state = transducer['@@transducer/step'](state, value);

      if(state != null && state['@@transducer/reduced']) {
        state = state['@@transducer/value'];
        break;
      }
    }

    return transducer['@@transducer/result'](state);
  }
ðŸ“Ē āđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™: vonheikemen/transductores en javascript kc - āļ›āļĢāļ°āđ€āļ”āđ‡āļ™āļĢāđ‰āļ­āļ™ ðŸ“Ē āđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™: vonheikemen/transductores en javascript kc - āļ›āļĢāļ°āđ€āļ”āđ‡āļ™āļĢāđ‰āļ­āļ™

Es momento de probar todo el arduo trabajo.

const is_positive = number => number > 0;

const data = [-2, -1, 0, 1, 2, 3];
const combine = (state, value) => (state.push(value), state);

reduce(filter(is_positive, to_transducer(combine)), [], data);
// Array(3) [ 1, 2, 3 ]
ðŸ“Ē āđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™: vonheikemen/transductores en javascript kc - āļ›āļĢāļ°āđ€āļ”āđ‡āļ™āļĢāđ‰āļ­āļ™ ðŸ“Ē āđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™: vonheikemen/transductores en javascript kc - āļ›āļĢāļ°āđ€āļ”āđ‡āļ™āļĢāđ‰āļ­āļ™

Bien, todo funciona. Pero es mucho trabajo usar reduce. Es por eso que tenemos la funciÃģn transduce, pero justo ahora le falta algo, tenemos que agregarle to_transducer.

function transduce(combine, initial, transducer, collection) {
  return reduce(
    transducer(to_transducer(combine)),
    initial,
    collection
  );
}
ðŸ“Ē āđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™: vonheikemen/transductores en javascript kc - āļ›āļĢāļ°āđ€āļ”āđ‡āļ™āļĢāđ‰āļ­āļ™ ðŸ“Ē āđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™: vonheikemen/transductores en javascript kc - āļ›āļĢāļ°āđ€āļ”āđ‡āļ™āļĢāđ‰āļ­āļ™

Vamos de nuevo.

const is_positive = number => number > 0;

const data = [-2, -1, 0, 1, 2, 3];
const combine = (state, value) => (state.push(value), state);

transduce(combine, [], filter(is_positive), data);
// Array(3) [ 1, 2, 3 ]
ðŸ“Ē āđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™: vonheikemen/transductores en javascript kc - āļ›āļĢāļ°āđ€āļ”āđ‡āļ™āļĢāđ‰āļ­āļ™ ðŸ“Ē āđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™: vonheikemen/transductores en javascript kc - āļ›āļĢāļ°āđ€āļ”āđ‡āļ™āļĢāđ‰āļ­āļ™

Ahora vamos a probar la composiciÃģn.

const is_even = number => number % 2 === 0;
const is_positive = number => number > 0;

const data = [-2, -1, 0, 1, 2, 3];
const combine = (state, value) => (state.push(value), state);

const transducer = compose(filter(is_positive), filter(is_even));

transduce(combine, [], transducer, data);
// Array [ 2 ]
ðŸ“Ē āđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™: vonheikemen/transductores en javascript kc - āļ›āļĢāļ°āđ€āļ”āđ‡āļ™āļĢāđ‰āļ­āļ™ ðŸ“Ē āđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™: vonheikemen/transductores en javascript kc - āļ›āļĢāļ°āđ€āļ”āđ‡āļ™āļĢāđ‰āļ­āļ™

Pueden visualizar todo el cÃģdigo de este ejemplo aquí

Oficialmente hemos terminado. No hay nada mÃĄs quÃĐ hacer. Creo que ya tienen suficiente informaciÃģn para crear sus propios transductores.

ConclusiÃģn

ÂĄLo lograron! Llegaron al final del artículo. Debo felicitarlos, especialmente si entendieron todo en el primer intento, este no fue nada fÃĄcil. Celebren, se lo merecen.

En fin, hoy aprendimos que los transductores (en javascript) son transformaciones que pueden operar en diferentes tipos de colecciones, siempre y cuando estas provean una funciÃģn reduce que sea compatible. TambiÃĐn tienen algunas propiedades sumamente Útiles como interrupciÃģn temprana (como la de un ciclo for), mecanismos para seÃąalar la finalizaciÃģn e inicio de un proceso y pueden ser combinadas usando composiciÃģn de funciones. Y por Último, tambiÃĐn deberían ser eficientes, pero no son mÃĄs rÃĄpidos que un ciclo for. Aunque no sean la soluciÃģn mÃĄs eficiente en cuestiÃģn de desempeÃąo su nivel de compatibilidad con diferentes colecciones y la forma declarativa para combinar operaciones hacen que sea una herramienta poderosa.

Fuentes


Gracias por su tiempo. Si este artículo les pareciÃģ Útil y quieren apoyar mis esfuerzos para crear mÃĄs contenido pueden dejar una propina en ko-fi.com/vonheikemen.

buy me a coffee

Top comments (0)