<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><atom:link href="https://www.api.antoniodiaz.me:443/api/v1/es/xml/rss" rel="self" type="application/rss+xml"/><title>Antonio Díaz - Sitio personal</title><link>https://www.api.antoniodiaz.me:443</link><description>Software y diseño</description><language>es-ES</language><item><title>JavaScript funcional: transductores</title><pubDate>Wed, 21 Jan 1970 00:00:00 GMT</pubDate><guid isPermaLink="true">https://www.antoniodiaz.me/es/blog/14</guid><description><![CDATA[<p><p>En este tercer artículo sobre programación funcional en JavaScript vamos a hablar de una operación realmente interesante: la transducción. Para ello vamos a tener que valernos de la composición de transformers que vimos anteriormente.</p><h2>Composición de transformers</h2><p>Las funciones <mark>map</mark> y <mark>filter</mark> que vimos en el artículo anterior tienen una diferencia respecto de las proporcionadas por <mark>Array.map</mark> y <mark>Array.filter</mark>: crean funciones que reciben un reducer y devuelven otro reducer. </p><pre><code>language-javascript<br />⁠<br />⁠⁠⁠// map = (fn) =&gt; (reducer) =&gt; reducer<br />⁠<br />⁠const map = (mapper) =&gt; (reducer) =&gt; (acc, val) =&gt; <br />⁠  reducer(acc, mapper(val));<br />⁠<br />const filter = (predicate) =&gt; (reducer) =&gt; (acc, val) =&gt;<br />⁠  predicate(val) ? reducer(acc, val) : acc;</code></pre><p>Lo que significa que nuestros transformers se pueden componer.</p><pre><code>language-javascript<br />⁠<br />⁠(reducer) =&gt; reducer<br />            ⁠(reducer) =&gt; reducer<br />            ⁠            ⁠(reducer) =&gt; reducer ⁠...</code></pre><p>Probemos con un ejemplo: podríamos necesitar filtrar primero aquellos elementos de un array que sean menores a <mark>5</mark>, y calcular sus cuadrados. Creemos una función filter para la selección, y una función mapper para el cálculo de la potencia, y usémoslos con nuestra función <mark>reduce</mark>.</p><pre><code>language-javascript<br />⁠⁠<br />⁠const data = [1, 2, 3, 4, 5];<br />⁠const lessThanFive = filter((x) =&gt; x &lt; 5);<br />⁠const squares = map((x: number) =&gt; power(x, 2));<br />⁠<br />⁠const filteredAndSquared = reduce(<br />⁠  lessThanFive(squares(arrayConcat)),<br />⁠  data, []<br />⁠⁠);<br />⁠console.log(filteredAndSquared); // [1, 4, 9, 16]</code></pre><figcaption>Run me in <a href="https://www.typescriptlang.org/play/?strict=false&amp;noImplicitAny=false&amp;strictNullChecks=false&amp;strictFunctionTypes=false&amp;strictPropertyInitialization=false&amp;strictBindCallApply=false&amp;noImplicitThis=false&amp;noImplicitReturns=false&amp;alwaysStrict=false&amp;jsx=0#code/C4TwDgpgBAsghmSAnAPAQQDRQEID4oC8UAFHAMZkBcUmUAbnADYCuE12AlIftgNwBQoSFADCAewB2ZOMGARUafEVIVqaJEjgh0uLAxZsaXAvnWbtigYPDQAShAAmzMvPRY8hWAmRuc+AD6iktKyrnhWZJIAzsBQSI7O0EQoACpYAKq4xPxQcQkuSNQqZMwAtsyMMgCWktTpWFVypdQpxvj1OVCN8nAARoyGZlqpup1VEo1VTABqTKx1-Bx13FAA3p0DseQl5ZXANRKe45MzcxBWuQBmYkgkkRIxXU1QYpdPPf0QXOu5udtlFWqkk88ScBWKAL2BwaTQ4AlyAF9+J14sBmEhDv9dkCJAIERFolsNFpxFIZJ4dKRiSA1NSdHomGoljQ6YoVgBtAB03Lg1IZjAAugSHrEwGIAO7yTzEXpwKKGCRlXryLAQAAeYokEAkwGoitKyqQzP1hpWsvlUAAVJaoOrNdrgMLHpcqow5LcCJ1fHhiGBQVUQoZSEyzWIxAM4BI2p1iKDEoUoPYwa5aHho7livz078-Y4AzIIMR9FwAPx5ZNITP0JhcajbJ2xUoIQhe1NZJuIeRFOAhkw4bOx-JdxNDhTuXAD7b81jZ3Jx8FTqAd5BFs4cOHI+6PBwyOCedkARiwACYsABmLAAFiwAFYhfwt7EBlEoikABaRgBiVToSSgLrdeRiGINU9SVeQ2igNUoBQKAbw3R8oCiABHZheQgKJPA7YDQKgE0IJWMVJUrNUT3XBt-1dd1HDQCQHAAZVQ9CHBBIdiGfV8Pwkb9f2IFC0PiKIqXMUkQnXLAd2AOAsHZAUEOicMIE5RgxAAc2IADqIcWiGKY0E4SgAB6QyoEPK8sAATiwA8ADYBX4IA" target="_blank">Playground</a>.</figcaption><p>Y de nuevo, recordemos que dijimos que la composición es asociativa, pero no conmutativa:</p><p class="centered" style="text-align: center"><span><span class="math-inline">f \circ g \neq g \circ f</span></span></p><p>&nbsp;</p><p>De modo que podríamos sacar provecho de ello y, en caso de que fuera necesario, invertir las posiciones de <mark>lessThanFive</mark> y <mark>squares</mark>  para primero calcular los cuadrados, y después obtener aquellos que son menores a cinco:</p><pre><code>language-javascript<br />⁠<br />⁠⁠⁠const data = [1, 2, 3, 4, 5];<br />⁠⁠const squaredAndFiltered = reduce(<br />  squares(lessThanFive(arrayConcat)),<br />⁠  data, []<br />⁠);<br />⁠console.log(squaredAndFiltered); // [1, 4]</code></pre><figcaption>Run me in <a href="https://www.typescriptlang.org/play/?strict=false&amp;noImplicitAny=false&amp;strictNullChecks=false&amp;strictFunctionTypes=false&amp;strictPropertyInitialization=false&amp;strictBindCallApply=false&amp;noImplicitThis=false&amp;noImplicitReturns=false&amp;alwaysStrict=false&amp;jsx=0#code/C4TwDgpgBAsghmSAnAPAQQDRQEID4oC8UAFHAMZkBcUmUAbnADYCuE12AlIftgNwBQoSFADCAewB2ZOMGARUafEVIVqaJEjgh0uLAxZsaXAvnWbtigYPDQAShAAmzMvPRY8hWAmRuc+AD6iktKyrnhWZJIAzsBQSI7O0EQoACpYAKq4xPxQcQkuSNQqZMwAtsyMMgCWktTpWFVypdQpxvj1OVCN8nAARoyGZlqpup1VEo1VTABqTKx1-Bx13FAA3p0DseQl5ZXANRKe45MzcxBWuQBmYkgkkRIxXU1QYpdPPf0QXOu5udtlFWqkk88ScBWKAL2BwaTQ4AlyAF9+J14sBmEhDv9dkCJAIERFolsNFpxFIZJ4dKRiSA1NSdHomGoljQ6YoVgBtAB03Lg1IZjAAugSHrEwGIAO7yTzEXpwKKGCRlXryLAQAAeYokEAkwGoitKyqQzP1hpWsvlUAAVJaoOrNdrgMLHpcqow5LcCJ1fHhiGBQVUQoZSEyzWIxAM4BI2p1iKDEoUoPYwa5aHho7livz078-Y4AzIIMR9FwAPx5ZNITP0JhcajbJ2xUoIQhe1NZJuIeRFOAhkw4bOx-JdxNDhTuXAD7b81jZ3Jx8FTqAd5BFs4cOHI+6PBwyOCedkARiwACYsABmLAAFiwAFYhfwt7EBlEoikABaRgBiVToSSgLrdeRiGINU9SVeQ2igNUoBQKAbw3R8oCiABHZheQgKJPA7YDQKgE0IJWMVJUrNUT3XBskNQ9CHDQCQHG-QDQRBIdiBQtD4iiYhn1fD8JG-X8qXMUkQnXLAd2AOAsHZAUEOicMIE5RgxAAc1YqjQVo+jXXdRw4SgAB6fSoEPK8BX4IA" target="_blank">Playground</a>&nbsp;<a href="https://www.typescriptlang.org/play/?strict=false&amp;noImplicitAny=false&amp;strictNullChecks=false&amp;strictFunctionTypes=false&amp;strictPropertyInitialization=false&amp;strictBindCallApply=false&amp;noImplicitThis=false&amp;noImplicitReturns=false&amp;alwaysStrict=false&amp;jsx=0#code/C4TwDgpgBAsghmSAnAPAQQDRQEID4oC8UAFHAMZkBcUmUAbnADYCuE12AlIftgNwBQoSFADCAewB2ZOMGARUafEVIVqaJEjgh0uLAxZsaXAvnWbtigYPDQAShAAmzMvPRY8hWAmRuc+AD6iktKyrnhWZJIAzsBQSI7O0EQoACpYAKq4xPxQcQkuSNQqZMwAtsyMMgCWktTpWFVypdQpxvj1OVCN8nAARoyGZlqpup1VEo1VTABqTKx1-Bx13FAA3p0DseQl5ZXANRKe45MzcxBWuQBmYkgkkRIxXU1QYpdPPf0QXOu5udtlFWqkk88ScBWKAL2BwaTQ4AlyAF9+J14sBmEhDv9dkCJAIERFolsNFpxFIZJ4dKRiSA1NSdHomGoljQ6YoVgBtAB03Lg1IZjAAugSHrEwGIAO7yTzEXpwKKGCRlXryLAQAAeYokEAkwGoitKyqQzP1hpWsvlUAAVJaoOrNdrgMLHpcqow5LcCJ1fHhiGBQVUQoZSEyzWIxAM4BI2p1iKDEoUoPYwa5aHho7livz078-Y4AzIIMR9FwAPx5ZNITP0JhcajbJ2xUoIQhe1NZJuIeRFOAhkw4bOx-JdxNDhTuXAD7b81jZ3Jx8FTqAd5BFs4cOHI+6PBwyOCedkARiwACYsABmLAAFiwAFYhfwt7EBlEoikABaRgBiVToSSgLrdeRiGINU9SVeQ2igNUoBQKAbw3R8oCiABHZheQgKJPA7YDQKgE0IJWMVJUrNUT3XBskNQ9CHDQCQHG-QDQRBIdslyFC0PiKJiGfV8Pwkb9fypcxSRCdcME6Q8T3PK9bwFcTcnZAVFgELdwwgTlGDEABzYh2Oo2j6Ndd1HDhKAAHozKgSSoEvJSgA" target="_blank">&nbsp;</a>.</figcaption><p>O podríamos necesitar filtrar aquellos elementos cuyos cuadrados son menores que cinco:</p><pre><code>language-javascript<br />⁠<br />⁠⁠const data = [1, 2, 3, 4, 5];<br />⁠const isSquareLessThanFive = filter((x) =&gt; power(x, 2) &lt; 5);<br />⁠const filtered = reduce(<br />⁠  isSquareLessThanFive(arrayConcat), <br />⁠  data, ⁠[]<br />⁠);<br />⁠console.log(filtered); // [1, 2]</code></pre><figcaption>Run me in <a href="https://www.typescriptlang.org/play/?strict=false&amp;noImplicitAny=false&amp;strictNullChecks=false&amp;strictFunctionTypes=false&amp;strictPropertyInitialization=false&amp;strictBindCallApply=false&amp;noImplicitThis=false&amp;noImplicitReturns=false&amp;alwaysStrict=false&amp;jsx=0#code/C4TwDgpgBAsghmSAnAPAQQDRQEID4oC8UAFHAMZkBcUmUAbnADYCuE12AlIftgNwBQoSFADCAewB2ZOMGARUafEVIVqaJEjgh0uLAxZsaXAvnWbtigYPDQAShAAmzMvPRY8hWAmRuc+AD6iktKyrnhWZJIAzsBQSI7O0EQoACpYAKq4xPxQcQkuSNQqZMwAtsyMMgCWktTpWFVypdQpxvj1OVCN8nAARoyGZlqpup1VEo1VTABqTKx1-Bx13FAA3p0DseQl5ZXANRKe45MzcxBWuQBmYkgkkRIxXU1QYpdPPf0QXOu5udtlFWqkk88ScBWKAL2BwaTQ4AlyAF9+J14sBmEhDv9dkCJAIERFolsNFpxFIZJ4dKRiSA1NSdHomGoljQ6YoVgBtAB03Lg1IZjAAugSHrEwGIAO7yTzEXpwKKGCRlXryLAQAAeYokEAkwGoitKyqQzP1hpWsvlUAAVJaoOrNdrgMLHpcqow5LcCJ1fHhiGBQVUQoZSEyzWIxAM4BI2p1iKDEoUoPYwa5aHho7livz078-Y4AzIIMR9FwAPx5ZNITP0JhcajbJ2xBwyOCedkARgwACYMABmDAAFgwAFYhfx7o8BlEoikABaRgBiVToSSgLrd8mIxDVeqV8jaUDVUBQUCHcLHhK6UQAygBHZi8iAAGQgU9nC6XK7X7s326gJr3KxipKlZqlgnZcMep4Nqurruo4IL5IWVTXneD7Pq+c4SIuy5UuYpIhBwWBNsAcBYOyApnuO4YQJyjBiAA5sQX7yI4cJQAA9OxUDtmBAr8EAA" target="_blank">Playground</a>.</figcaption><p>E incluso obtener el cuadrado de cada elemento y reducirlo a un único valor:</p><pre><code>language-javascript<br />⁠<br />⁠const accumulate = (a: number, b: number): number =&gt; a + b;<br />⁠<br />⁠⁠⁠const data = [1, 2, 3, 4, 5];<br />const filteredSquaredAndAccumulated = reduce(<br />⁠  lessThanFive(squares(accumulate)),<br />⁠  data,<br />⁠  0<br />⁠);<br />⁠console.log(filteredSquaredAndAccumulated); // 30</code></pre><figcaption>Run me in <a href="https://www.typescriptlang.org/play/?strict=false&amp;noImplicitAny=false&amp;strictNullChecks=false&amp;strictFunctionTypes=false&amp;strictPropertyInitialization=false&amp;strictBindCallApply=false&amp;noImplicitThis=false&amp;noImplicitReturns=false&amp;alwaysStrict=false&amp;jsx=0#code/C4TwDgpgBAsghmSAnAPAQQDRQEID4oC8UAFHAMZkBcUmUAbnADYCuE12AlIftgNwBQoSFADCAewB2ZOMGARUafEVIVqaJEjgh0uLAxZsaXAvnWbtigYPDQAShAAmzMvPRY8hWAmRuc+AD6iktKyrnhWZJIAzsBQSI7O0EQoACpYAKq4xPxQcQkuSNQqZMwAtsyMMgCWktTpWFVypdQpxvj1OVCN8nAARoyGZlqpup1VEo1VTABqTKx1-Bx13FAA3p0DseQl5ZXANRKe45MzcxBWuQBmYkgkkRIxXU1QYpdPPf0QXOu5udtlFWqkk88ScBWKAL2BwaTQ4AlyAF9+J14sBmEhDv9dkCJAIERFolsKJCZEkSHBqBIyr15FhepTqfIllAqaUabcTFA4FAANRQXoEh6xMBiADu8k8xF6cCihlZ7KwEAAHiKJBAJMAGWymVr2StpbKoAAqI1QZWq9XAQWPS5VRhyDmdXx4YhgUFVEKGUhqNr8sRiAZwCRtTrEUGJQpQexg1y0PAh3LFPRMBO-N2OD2k4j6LgAfjyMaQSfoKag1G21tipQQhCdcay1cQ8iKFKMK043FD4YK1GjEeduFTxf0rFTuW78mLjeQ2bOHDhyPujwcMm5RAA2gBGLAAJiwAGYsAAWLAAVgAugIl7EBlEoikABZBgBiVToZNt9snxCVuqZKyVKAUCgU8F34a8oCiABHZg4HiKJPEbYgfz-JBfRFcUiyVXd50rKBPwdRw0AkBw0GJbE5AcEF8ggbJclve8nwkV932IaDYPgiEKK+DgME6FdgDgPjcgABkWK9ogDCAADpGDEABzYgCPkIiSLInZAUouEoAAeh0qB9zEoA" target="_blank">Playground</a>.</figcaption><p>Y como nuestros transformers se pueden componer podemos hacer uso de nuestra función <mark>compose</mark> para todas estas operaciones:</p><pre><code>⁠⁠language-javascript<br />⁠<br />⁠const compose = (...fns) =&gt; (items) =&gt;<br />  fns.reduceRight((acc, curr) =&gt; curr(acc), items);<br />⁠<br />⁠⁠⁠const data = [1, 2, 3, 4, 5];<br />⁠const filterAndSquare = compose(lessThanFive, squares);<br />⁠<br />⁠const filteredSquaredAndAccumulated = reduce(<br />⁠  filterAndSquare(accumulate), data, 0<br />⁠);<br />⁠<br />⁠console.log(filteredSquaredAndAccumulated); // 30</code></pre><figcaption>Run me in <a href="https://www.typescriptlang.org/play/?strict=false&amp;noImplicitAny=false&amp;strictNullChecks=false&amp;strictFunctionTypes=false&amp;strictPropertyInitialization=false&amp;strictBindCallApply=false&amp;noImplicitThis=false&amp;noImplicitReturns=false&amp;alwaysStrict=false&amp;jsx=0#code/C4TwDgpgBAsghmSAnAPAQQDRQEID4oC8UAFHAMZkBcUmUAbnADYCuE12AlIftgNwBQoSFADCAewB2ZOMGARUafEVIVqaJEjgh0uLAxZsaXAvnWbtigYPDQAShAAmzMvPRY8hWAmRuc+AD6iktKyrnhWZJIAzsBQSI7O0EQoACpYAKq4xPxQcQkuSNQqZMwAtsyMMgCWktTpWFVypdQpxvj1OVCN8nAARoyGZlqpup1VEo1VTABqTKx1-Bx13FAA3p0DseQl5ZXANRKe45MzcxBWuQBmYkgkkRIxXU1QYpdPPf0QXOu5udtlFWqkk88ScBWKAL2BwaTQ4AlyAF9+J14sBmEhDv9dkCJAIEVYhNAUpoHtckKV5AAxCS+DzKBwyOBqNo4ASEoKlMBiKJJNadWlZS4SNAtElRMkUpDUgVLKDEuCkm6S6W0cL81VYESC4WihXipVUmnYTW6KBC7C6xXkw0yy3661SmmdXKYZ2iTq4eFQWmarAAESybqFIrlYolNpE-tGvzNEgtob14cdKGNomjvyFIjtSZV7lwnVl8qtypptADeIi0S2FEhMl5pGoEjKvXkWF6jeb8llTdKLduJigcCgAGooL1Kw9YlyAO7yTzEXpwHkd3utqAQAAeXIkEAkwBXfe7nf7+EXPKgACoL+ut5Jd8AJ49LlVGHJ++q88QwKCqiFDA2jBWXoxDEAYFTaTpiFBRJCigewwVcVVcAg3Jij0JgUN+b9HF-OtiH0LgAH48gQpA0PoDCoGobYBHuR4BiiKIUgACwVSkqjoXln1feRiGIDcDy7FYN29KAAFY4WROjYlKBBCA-PxiFkxB5CKJlAIHThuEg6CCmoeCYJlbTUO2dCDEw3JdN40yoGU5B8LODhJP4aSoEiTluUMcQPPPAhIIAOkCoUokw4hulKELjIzB5-Ks2wqgAc2Y4A+JskoNBZdKyO2DgYQgCLnNchlgCHIgAG0AEYsAAJiwABmLAABYsDEgBdWiqygKIAEdmDgeIok8ZS+IEqAe0PFYZ14jcaqcjrJzNF83zQCQHAAZV6-reXcrkeWIBimNYiR2M4rAer6gbCs67i30cFb1s20EQXyCBiBu+R7o2i7XqxQE5FyqBirgLAAAYroeUCIH8xgxASt6lvkO7Vq+raHDhKAAHoMagOqQf4IA" target="_blank">Playground</a>.</figcaption><p>Con esto tenemos ya todas las piezas necesarias para la operación que nos ocupa en este artículo: la <em>transducción</em>. </p><h1>Transductores</h1><p>Como decíamos, nuestras funciones filter y map tienen sus análogos en <mark>Array.filter</mark> y <mark>Array.map</mark>. Cabría por tanto preguntarse qué sentido tendría implementar nuestras propias funciones cuando el objeto <mark>Array</mark> ya tiene estos métodos.</p><p>Pues bien, planteémonos el siguiente caso: digamos que tenemos un array de gran tamaño, de diez millones de elementos, sobre el que tenemos que realizar diversas operaciones. </p><pre><code>language-javascript<br />⁠<br />⁠const data = Array.from({ length: 10000000 }, (_, index) =&gt; index + 1);</code></pre><p>Para procesarlo con <mark>Array.map</mark> y <mark>Array.filter</mark> tenemos que guardar primero el array entero en memoria. Y dado que ambos métodos devuelven un nuevo array, cada vez que llamemos a uno de ellos estaremos creando una copia del mismo. </p><p>Esto para un array de algunos centenares de elementos no supone un problema. Pero significa que no es un patrón escalable, ya que cuando tengamos una lista de un tamaño relevante podríamos estar consumiendo una cantidad considerable de recursos.</p><p>Además podemos tener otro problema: si algo va mal durante el procesamiento de nuestro array, al estar guardando los datos procesados en memoria, si la aplicación sufre algún percance podríamos perder los datos ya procesados.</p><p>Todo esto sucede porque los métodos de <mark>Array</mark> se ejecutan sucesivamente sobre el array completo: esto es, si queremos aplicar <mark>Array.map</mark> y <mark>Array.filter</mark> sobre un array, primero <mark>map</mark> lo recorrerá por completo transformando todos sus elementos y devolviendo un array nuevo, y luego <mark>filter</mark> hará lo mismo seleccionando aquellos elementos que nos interesen.</p><pre><code>language-javascript<br />⁠<br />const data = [&quot;1&quot;, &quot;2&quot;, &quot;3&quot;];<br />⁠<br />⁠const result = data<br />⁠  .map((x) =&gt; (console.log(&quot;map&quot;), Number(x)))<br />⁠  .filter((x) =&gt; (console.log(&quot;filter&quot;), x % 2 !== 0));<br />⁠console.log(result); // [1, 3]<br />⁠<br />⁠// map<br />⁠⁠// map<br />⁠⁠⁠// map<br />⁠⁠⁠// filter<br />⁠⁠⁠⁠// filter<br />⁠⁠⁠⁠// filter<br />// [1, 3]</code></pre><figcaption>Run me in <a href="https://www.typescriptlang.org/play/?strict=false&amp;noImplicitAny=false&amp;strictNullChecks=false&amp;strictFunctionTypes=false&amp;strictPropertyInitialization=false&amp;strictBindCallApply=false&amp;noImplicitThis=false&amp;noImplicitReturns=false&amp;alwaysStrict=false&amp;ssl=6&amp;ssc=1&amp;pln=1&amp;pc=1#code/MYewdgzgLgBAtgTwIICcUEMEwLwwNoBEAjAQDQwEBMZFAzAQLoDcAUKJLGAKYDuqGWXIn6YWMGADo46AA4AKOQA8AlDgB8MOewggANlwm6QAczkFpMgsvIA5AK5wARlxRLl7sZIBmAS11QXBRV1TW09AyNTAl9-FytyRRgAUhhKGABCbFwABndWMP1DEzluPjRMZSYYAHpq-CJyWgYWIA" target="_blank">Playground</a>.</figcaption><p>Sería fantástico si pudiésemos desacoplar el procesamiento de cada elemento individual respecto del procesamiento del array completo.</p><p>Para ello podríamos empezar con un cambio: en lugar de hacer pasadas sucesivas sobre el array entero, podríamos intentar realizar primero todas las acciones necesarias sobre el primer elemento, luego sobre el segundo, etc., y así hasta el último elemento.</p><p>Pues bien, para lograrlo no nos hace falta hacer nada, porque esto es precisamente lo que ya están haciendo nuestras funciones <mark>map</mark> y <mark>filter</mark> con <mark>reduce</mark>.</p><pre><code>language-javascript<br />⁠<br />⁠const data = [&quot;1&quot;, &quot;2&quot;, &quot;3&quot;];<br />⁠<br />⁠const toInt = map((x: string) =&gt; (console.log(&quot;map&quot;), Number(x)));<br />⁠const isOdd = filter((x: number) =&gt; (console.log(&quot;filter&quot;), x % 2 !== 0));<br />⁠const toIntAndFilterOdds = compose(toInt, isOdd);<br />⁠const result = reduce(toIntAndFilterOdds(concat), data, []);<br />⁠<br />⁠console.log(result); <br />⁠<br />⁠⁠⁠// map<br />⁠⁠⁠// filter<br />⁠⁠⁠⁠// map<br />⁠⁠⁠// filter<br />⁠⁠// map<br />⁠⁠⁠// filter<br />⁠// [1, 3]</code></pre><figcaption>Run me in <a href="https://www.typescriptlang.org/play/?strict=false&amp;noImplicitAny=false&amp;strictNullChecks=false&amp;strictFunctionTypes=false&amp;strictPropertyInitialization=false&amp;strictBindCallApply=false&amp;noImplicitThis=false&amp;noImplicitReturns=false&amp;alwaysStrict=false&amp;jsx=0#code/C4TwDgpgBAsghmSAnAPAQQDRQEID4oC8UAFHAMZkBcUmUAbnADYCuE12AlIftgNwBQoSFADCAewB2ZOMGARUafEVIVqaJEjgh0uLAxZsaXAvnWbtigYPDQAShAAmzMvPRY8hWAmRuc+AD6iktKyrnhWQtAAKpoSAM4AZmJIALbyAPq+HsoOMnBqxjwRNkEpYGJx0EQA3vxQUFm4xAkSaNQxcPFJqRmNHO2xiclpSJm04XUN41giTS1tUB1dw73YM7pQLdgDnUM9o307y-tj6wL1WTNYACJNk-XzR3sjmSI3uvebEtuLg90vKDWog+9QeEhET3+vXGuEm-V+uyhB1otwEAF8rGRJHFgFAkI5nFUGlEsABVO71fFOFxIagqKhQUlYACWchS7UKjIwk1Z8jgACNGIYzFoUFEQVBmRJWcymAA1Jisaik-jw0ncKC1epC3HkMieKUy+WKiBWB7JEhY+K43kpKBiBKSuSaQUQLha0F6zxUwlIekstkcc5QNH8Sb44DMJASKB69GY7G44CDalEsVkilQZOIla0kg+mnUeyp1Akxm4Tn09oBiDsxmcknhgmFqDF33p8vc+q8l1CtQaUXkruS6XAWWMBUGdqq9oaj1WnFZv4rBxtmmebPHEbEAvyIOTHWxigG0fjyesM2bC3EBc2tn2x09gVC92fL1ETfPRxr+T+p21-d6lDJtI2jI8yHjMNbygK0Qk8HRSAHEB+3MHQ9CYAoUNFRQNQAbQAOkIuAkPQxgAF0E2tGCxDKCpDHEWjKkISZiEI-CWjiQoWNtTjuE+Dj8N3WxmQAcwAC2AYg-zIKMkE5GSNHpDgaxSTjKMXBJmUYZ1mIuGFiDAKlmRCQxSAKDV+TEMQhU6Lj6h3Zt5CLRyFHcCs+PsvVSLs0FDMcYyZAgYh9C4AB+PEXL-EKoGoOMoMTKAUgQXSpjc4gksQJySHyIwNU4Dz8xc5ySz6AqopNHzKUirzEu8X99FYDh934aDcmAOBPFwgAiABGLqsC6gAmfqoC6gBmLqKPiqjgDEABJCRcSIDKpIAD2oHEkClETKwXayIHwxgxBE4guoyrrlKgAA5ZgUn5X9Vqa-doOZOIAHkHAcTxNO039iHWqAJFu+65I1G9sX2w7jtOn7nQurBVqgABSKBBqgABCAgiAABiagRoNmhbgDQCQHAAMS050PocOJPCxRigsJxaWXez7noS-E4mYbSNxTQliCZ4nSYp36kGpuIsFgmQsDauAsFwsjmr2oUoZOznueAIMoAAem1qBcJ6rAxrIoA" target="_blank">Playground</a>.</figcaption><p>Si bien parece una diferencia menor, no lo es, ya que nos abre la puerta para transformar <mark>reduce</mark> en una función que, en lugar de recibir un transformer con un reducer ya aplicado, los reciba por separado y los aplique dentro de sí.</p><p>De este modo, en lugar de reducir con la función <mark>reduce</mark>:</p><pre><code>language-javascript<br />⁠<br />⁠⁠const reduce = (reducer, iterable, initialValue) =&gt; {<br />⁠  let acc = initialValue;<br />⁠<br />⁠  for (const item of iterable) {<br />⁠    acc = reducer(acc, item);<br />⁠  }<br />⁠<br />⁠  return acc;<br />⁠};</code></pre><p>Usaríamos una nueva función <mark>transduce</mark>, que tendría el siguiente aspecto:</p><pre><code>language-javascript<br /><br />⁠const transduce = (<br />⁠  transformer, <br />⁠  reducer, <br />  ⁠iterable,<br />⁠  initialValue<br />⁠) =&gt; {<br />⁠⁠  let acc = initialValue;<br />⁠  const transformedReducer = transformer(reducer);<br />⁠<br />⁠  for (const item of iterable) {<br />⁠    acc = transformedReducer(acc, item);<br />⁠  }<br />⁠<br />⁠  return acc;<br />⁠};<br />⁠<br />⁠const data = [&quot;1&quot;, &quot;2&quot;, &quot;3&quot;];<br />⁠<br />⁠const toInt = map((x: string) =&gt; (console.log(&quot;map&quot;), Number(x)));<br />⁠const isOdd = filter((x: number) =&gt; (console.log(&quot;filter&quot;), x % 2 !== 0));<br />⁠const toIntAndFilterOdds = compose(toInt, isOdd);<br />const result = transduce(toIntAndFilterOdds, concat, data, []);<br />⁠<br />⁠console.log(result);<br />⁠⁠<br />⁠⁠⁠// map<br />⁠⁠⁠// filter<br />⁠⁠⁠⁠// map<br />⁠⁠⁠// filter<br />⁠⁠// map<br />⁠⁠⁠// filter<br />⁠// [1, 3]</code></pre><figcaption>Run me in <a href="https://www.typescriptlang.org/play/?strict=false&amp;noImplicitAny=false&amp;strictNullChecks=false&amp;strictFunctionTypes=false&amp;strictPropertyInitialization=false&amp;strictBindCallApply=false&amp;noImplicitThis=false&amp;noImplicitReturns=false&amp;alwaysStrict=false&amp;jsx=0#code/C4TwDgpgBAsghmSAnAPAQQDRQEID4oC8UAFHAMZkBcUmUAbnADYCuE12AlIftgNwBQoSFADCAewB2ZOMGARUafEVIVqaJEjgh0uLAxZsaXAvnWbtigYPDQAShAAmzMvPRY8hWAmRuc+AD6iktKyrnhWQtAAKpoSAM4AZmJIALbyAPq+HsoOMnBqxjwRNkEpYGJx0EQA3vxQUFm4xAkSaNQxcPFJqRmNHO2xiclpSJm04XUN41giTS1tUB1dw73YM7pQLdgDnUM9o307y-tj6wL1WTNYACJNk-XzR3sjmSI3uvebEtuLg90vKDWog+9QeEhET3+vXGuEm-V+uyhB1otwEAF8rGRJHFgFAkI5nFUGlEsABVO71fFOFxIagqKhQUlYACWchS7UKjIwk1Z8jgACNGIYzFoUFEQVBmRJWcymAA1Jisaik-jw0ncKC1epC3HkMieKUy+WKiBWB7JEhY+K43kpKBiBKSuSaQUQLha0F6zxUwlIekstkcc5QNH8Sb44DMJASKB69GY7G44CDalEsVkilQZOIla0kg+mnUeyp1Akxm4Tn09oBiDsxmcknhgmFqDF33p8vc+q8l1CtQaUXkruS6XAWWMBUGdqq9oaj1WnFZv4rBxtmmebPHEbEAvyIOTHWxigG0fjyesM2bC3EBc2tn2x09gVC92fL1ETfPRxr+T+p21-d6lDJtI2jI8yHjMNbygK0Qk8HRSAHEB+3MHQ9CYAoUNFRQNQAbQAOkIuAkPQxgAF0E2tGCxDKCpDHEWjKkISZiEI-CWjiQoWNtTjuE+Dj8N3WxmQAcwAC2AYg-zIKMkE5GSNHpDgaxSTjKMXBJmUYZ1mIuGFiDAKlmRCQxSAKDV+TEMQhU6Lj6h3Zt5CLRyFHcCs+PsvVSLs0FDMcYyZAgYh9C4AB+PEXL-EKoGoOMoMTKAUgQXSpjc4gksQJySHyIwNU4Dz8xc5ySz6AqopNHzKUirzEu8X99FYDh934aDcmAOBPFwgAiABGLqsC6gAmfqoC6gBmLqKPiqjgDEABJCRcSIDKpIAD2oHEkClETKwXayIHwxgxBE4guoyrrlKgAA5ZgUn5X9Vqa-doOZOIAHkHAcTxNO039iHWqAJFu+65I1G9sX2w7jtOn7nQurBVqgABSKBBqgABCAgiAABiagRoNmhbgDQCQHAAMS050PocOJPCxRigsJxaWXez7noS-E4mYbSNxTQliCZ4nSYp36kGpuIsFgmQsDauAsFwsjmr2oUoZOznueAIMoAAem1qBcJ6rAxrIoA" target="_blank">Playground</a>.</figcaption><p>Esto ya es un transductor: una función que toma un <em>transformer</em> —que pueden ser una serie de transformers compuestos— y un <em>reducer</em> —encargado de la operación de reducción—, más un iterable y un dato inicial, y reducirá el iterable sobre el dato inicial usando el transformer y el reducer.</p><p>Los transductores tienen una notable ventaja en cuanto a eficiencia frente al encadenamiento de métodos, como sucede con los métodos de <mark>Array</mark>. Esto se puede ver claro si recuperamos el array de gran tamaño que comentábamos más arriba.</p><p>Digamos que tenemos una lista de diez millones de productos con precios diferentes que vendemos a distancia, con un coste de envío de diez dólares. Los usuarios pueden financiar el pago en doce mensualidades, pero sólo si cada cuota mensual resultante es un número entero. Nos han pedido encontrar el mayor gasto que puede hacer un comprador que desee adquirir un producto financiable.</p><p>Podemos hacerlo con los métodos de <mark>Array</mark>:</p><pre><code>language-javascript<br />⁠<br />⁠// Array with 10.000.000 elements<br />⁠const data = [<br />⁠  { name: &quot;Product 1&quot;, price: 73 },<br />⁠  ...<br />⁠];<br />⁠<br />⁠const addShippingCosts = (product: Product) =&gt; product.price + 10;<br />⁠const isEligibleForFinancing = (price: number) =&gt; price % 12 === 0;<br />⁠const getBiggerExpense = (a: number, b: number) =&gt; (a &gt; b ? a : b);<br />⁠<br />⁠console.time(&quot;array_methods&quot;);<br />⁠const result = data<br />⁠  .map(addShippingCosts)<br />⁠  .filter(isEligibleForFinancing)<br />⁠  .reduce(getBiggerExpense);<br />⁠<br />console.log(result); // 120<br />⁠<br />⁠console.timeEnd(&quot;array_methods&quot;); // 141</code></pre><figcaption>⁠Run me in <a href="https://www.typescriptlang.org/play/?strict=false&amp;noImplicitAny=false&amp;strictNullChecks=false&amp;strictFunctionTypes=false&amp;strictPropertyInitialization=false&amp;strictBindCallApply=false&amp;noImplicitThis=false&amp;noImplicitReturns=false&amp;alwaysStrict=false#code/C4TwDgpgBACgTgewCYFcDGwoF4oG8oB2AhgLYQBcUAzsHAJYEDmA3FGPWhYSiQEYRwoAX2YAoUWgQEaUJEWBFKAQThwiIADzxk6YAD5sUFWpAAKAIwAGazcsBKUVCgA6AGZ0ANh9MoCSCO4EEEgOTs4kRGCmpgD6ADRQDP4AHnbYBriOTlAA9DmwiKgYUADudMAAFlC8dIyMEDLsdJxZTnSuUKZJEMnYWDgAzKHZTnAQwChwBHitI4SkXABE2kWYZZXVtfWNHNCV8olUUIFEBGh0RLweEItxsyNNnJTmVndzwmKzYxNTM+-EZEoAAMVrooAASXDdXoAaig5iEQLec0eXAArJYoHCALLyCpuDwIBBwUy4yrONR+BAkUxpABUUDR5jsCTyRjYu2q4xKEAg0wx4KgpyQ8Os4NmIiyQjsn0k0kwRCQSAAyhU6GAwAxGABhBA0I44UzsHQYSigjBpLAGY2rZyorGisRymR0KgAUQ8tToVwgADFib6GKdzkxDEbdpQCDx+HBLdbOQBSeEAJj6OEsTqkMnqwAAQlsBG7kpBpNBDYpuHwBAleJHowI450iFADLwoAB+IVQSi8GXia6YGhEODAAAqdDIhkgcFcxIiZwgzgICBKtMz8qgJD1wCLJaodAAbhA3UeCObMDg5AosuFIqZFSq1Rqtbr9cM3J5gAIuu7PYxvdc-pwIGxBnFq75jEUECmDm+Z1IWxZ8lQEB9hIWYINcziEowphbjQu5IYex6nueMq5Pk5jJpYogDlAfJIOOk44NOs5wPOnBLiua7iM6GGLthpiLMAE7Hh4kTIUglC3HRfiMdAAC01AKCOclkWyyrUtAJBUKIQA" target="_blank">Playground</a>&nbsp;</figcaption><p>Bien, obtenemos el resultado correctamente. Tomemos nota del tiempo que lleva ejecutar este código: a la hora de escribir esto el Playground de TypeScript muestra 141 ms., si bien puede cambiar según la máquina donde se ejecute.</p><p>Ahora probemos usando un transductor:</p><pre><code>language-javascript<br />⁠<br />⁠// Array with 10.000.000 elements<br />⁠const data = [<br />⁠  { name: &quot;Product 1&quot;, price: 73 },<br />⁠  ...<br />⁠];<br />⁠<br />⁠const eligibleForFinancing = filter(isEligibleForFinancing);<br />⁠const priceWithShippingCosts = map(addShippingCosts);<br />⁠const piped = pipe(eligibleForFinancing, priceWithShippingCosts);<br />⁠<br />⁠⁠console.time(&quot;transduce&quot;);<br />⁠const result = transduce(piped, getBiggerExpense, data, 0);<br />console.log(result); // 120<br />⁠console.timeEnd(&quot;transduce&quot;); // 62</code></pre><figcaption>⁠Run me in <a href="https://www.typescriptlang.org/play/?strict=false&amp;noImplicitAny=false&amp;strictNullChecks=false&amp;strictFunctionTypes=false&amp;strictPropertyInitialization=false&amp;strictBindCallApply=false&amp;noImplicitThis=false&amp;noImplicitReturns=false&amp;alwaysStrict=false&amp;jsx=0#code/C4TwDgpgBAsghmSAnAPAQQDRQEID4oC8UAFHAMZkBcUmUAbnADYCuE12AlIftgNwBQoSFADCAewB2ZOMGARUafEVIVqaJEjgh0uLAxZsaXAvnWbtigYPDQAShAAmzMvPRY8hWAmRuc+AD6iktKyrnhWQtAAKpoSAM4AZmJIALbyAPq+HsoOMnBqxjwRNkEpYGJx0EQA3vxQUFm4xAkSaNQxcPFJqRmNHO2xiclpSJm04XUN41giTS1tUB1dw73YM7pQLdgDnUM9o307y-uZk-WYZ6KTuAL1WTNYACJNl-NHeyOZIk+6rxLbi0G3U+KDWol+9XqLRE72BvXGuEm-UBuzhB1ozwEAF8rGRJHFgFAkI5nFUGlEsABVF71YlOFxIagqKhQSlYACWchS7UKrIwk058jgACNGIYzFoUFEIVB2RJOeymAA1JisaiU-jIyncKC1epiwnkMieOUK5WqiBWKHJEh4+KEwUpKBiBKyuSaUUQLh6yFGzx00lIZkcrkcW5QLH8SbE4DMJASKBG7G4-GE4CDelkqVUmlQdOolaMkgBhnUeyZ1AU1m4XnM9ohiDc1m8inRkmlqDlwPZ6v8+qCj1itQaSXUvuy+XARWMFUGdqa9o6n12gl5oErBxdhmefPHEbEEvyMOTA2JigmyfT2esK2bG3EFcOrnO10DkVi72XP1EXcfRxb+RgzdRtj3qSM21jeMzzIZMo0fKA7RCTwdFIEcQGHcwdD0JgCgwyVFB1ABtAA6Ui4DQ7DGAAXRTe0oDAdlIGocQygqKpJmIUjiJaOJCg4gAPPjISJdsIGIICyDjJBeUkjRmQ4LAeKwQTaNXBJ2UYd1CEmRpiDAOl2RCQxSAKHVhTEMQxU6ITi1EosAIUdwa24DijUomz6n0xxDJkMT9C4AB+ESKyA-yoGoJM4NTKAUgQbS7gRYhYsQeQmXyIwdU4Fz6gPOyyzsvpspINz6AtDzgsDIDkuQYh9FYDhj2sYQAAUkDEelCRqKAJDgNJqAJJA5QAc14ejBpcagJGYFJhXkCNVMJXJgHSiVtFa9rnGAJQaDQ4gAEYAAYjuOg6OEmbiNMYYhmAkBwIHUiRHDO+piOS8T0g5W6IEEpdLgAej+qB1o6qAAHdOQACygYV2SGoaIFXfTDIgS52VdYg5Tu-jCAIIgAGZnuEkTIITH0ie63rDAAImBzawch6HYfhxHxugYAIZkWU4k2OVOjIRVPSp8dyaRiaoD2w7hchHEo2EmM41Jy56h6vqoAAA1pshCQAEmqTHvqgABqcWsTVqXPNZ6gAFYDqNrx2e4xgLKDeAHdiBwxBSYguAAKigK29oUqAAZoMbkehiBgFBiAIATG3tcTW7xaO7XLhlsDGv4eC4AcBwAGUIcYhiJCG8QCW55R9I2rXqE14BeSrjriNF6BjcOgR4PZOIAFFGFh9lPQAMWSQfeakYbPD0y3uum2bpJ1FuoAAUnFgAmHGiAOjvovh4BsCZ+Ru-4yB4jJEyZ5m+QsGFSbZ6PHVSCgfBhSgIK4HC6HGvgiA+6GgexWHkgUePVx4l08OpTSgEu6937kPEeY9+Yl2PPBFuAB1SGBci7DTLsACuMUECkFzpgxA2CKi4K-tFBikAHCeCoWJH+sCAHwJAYgoaWA0EYMLiQkuODeJWFPASciwAojsjSLQ+QwI+YQGIhIMQoNvYLREnEZgmkdwZlJHpRijgsC733nDQ+x9Y6VCwEtOAWBTrb3iJZaRTshq5WUZpMMwdAZ7VXgdKMp5Y4OBEWIogyBJFSGkbI+RyD8TWOIrY4gVMpxpF7ggSoDhqBCygF4nx0AAC0UBBFIGEaIr0o0Q5509tAFIcR+BAA" target="_blank">Playground</a>&nbsp;</figcaption><p>Como se ve se obtiene el mismo resultado, pero con una ganancia de eficiencia notable. Esto por supuesto dependerá de la complejidad temporal y espacial de los algoritmos que se usen: pero se aprecia que, frente a los mismos algoritmos, los transductores son más eficientes que el encadenamiento de métodos.</p><p>Ahora bien, una vez llamamos a <mark>transduce</mark>, este va a intentar procesar el array entero guardándolo en memoria, con una posible sobrecarga del sistema. En otros términos: el transducer actual es evaluado ansiosamente.</p><p>Lo que nosotros buscamos en realidad es una función que nos permita procesar los datos con evaluación perezosa de modo que los datos sean procesados bajo demanda únicamente. Y si recordamos nuestro primer artículo, en él mencionamos que la evaluación perezosa en JavaScript se puede implementar con generadores. </p><p>Veamos un ejemplo de un generador:</p><pre><code>language-javascript<br /><br />⁠function* lazySum(a, b) {<br />⁠  yield a + b;<br />⁠}<br />⁠<br />⁠let result = lazySum(1, 3);<br />⁠console.log(result); //  {} (not evaluated yet)<br />⁠console.log(result.next().value); // 4</code></pre><figcaption>Run me in <a href="https://www.typescriptlang.org/play/?strict=false&amp;noImplicitAny=false&amp;strictNullChecks=false&amp;strictFunctionTypes=false&amp;strictPropertyInitialization=false&amp;strictBindCallApply=false&amp;noImplicitThis=false&amp;noImplicitReturns=false&amp;alwaysStrict=false&amp;target=99&amp;jsx=0&amp;module=1&amp;ts=4.0.2#code/GYVwdgxgLglg9mAVAAgDYEMBeBPAyiAWwAp0AaZAIwEpkBvAKGWWxgFNUATZdZAakoDc9AL716qVlGQAnVgGcQqKQF40WPISIBGcgGYqA5AHojyCHAIAHEFHSwEyDu3TZWHeubBy4EgHSo4AHMiWQUlA2NTWmFkMDgpVgA3dFQQOzdmSQ8Ebz8A4NDFKF8wVgAPKCIqX2TU1giTZAAWIA" target="_blank">Playground</a>.</figcaption><p>Por tanto intentemos implementar nuestro <mark>transduce</mark> para que evalue perezosamente los datos:</p><pre><code>language-javascript<br />⁠<br />function* lazyTransduce(<br />⁠  transformer,<br />⁠  reducer,<br />⁠  iterable,<br />⁠  initialValue<br />⁠) {<br />⁠  const transformedReducer = transformer(reducer);<br />⁠  let acc = initialValue;<br />⁠<br />⁠  for (const item of iterable) {<br />⁠    acc = transformedReducer(acc, item);<br />⁠<br />⁠    yield acc;<br />⁠  }<br />⁠}<br /></code></pre><p>En lugar de retornar, <mark>lazyTransduce</mark> va a devolver un generador que devolverá su estado en <mark>acc</mark> por cada vez que se ejecute su método <mark>next()</mark>.</p><pre><code>language-javascript<br />⁠<br />⁠// Array with 10.000.000 elements<br />⁠const data = [<br />⁠  { name: &quot;Product 1&quot;, price: 73 },<br />⁠  ...<br />⁠];<br />⁠<br />⁠const eligibleForFinancing = filter(isEligibleForFinancing);<br />⁠const priceWithShippingCosts = map(addShippingCosts);<br />⁠const piped = pipe(eligibleForFinancing, priceWithShippingCosts);<br />⁠<br />⁠const result = lazyTransduce(piped, getBiggerExpense, data, 0);<br />⁠<br />⁠console.log(result.next().value); // 0<br />⁠console.log(result.next().value); // 0<br />⁠console.log(result.next().value); // 0<br />⁠⁠console.log(result.next().value); // 120<br />⁠⁠⁠console.log(result.next().value); // 120<br />⁠⁠⁠console.log(result.next().value); // 120<br />⁠...</code></pre><figcaption>Run me in <a href="https://www.typescriptlang.org/play/?strict=false&amp;noImplicitAny=false&amp;strictNullChecks=false&amp;strictFunctionTypes=false&amp;strictPropertyInitialization=false&amp;strictBindCallApply=false&amp;noImplicitThis=false&amp;noImplicitReturns=false&amp;alwaysStrict=false&amp;jsx=0#code/C4TwDgpgBAsghmSAnAPAQQDRQEID4oC8UAFHAMZkBcUmUAbnADYCuE12AlIftgNwBQoSFADCAewB2ZOMGARUafEVIVqaJEjgh0uLAxZsaXAvnWbtigYPDQAShAAmzMvPRY8hWAmRuc+AD6iktKyrnhWQtAAKpoSAM4AZmJIALbyAPq+HsoOMnBqxjwRNkEpYGJx0EQA3vxQUFm4xAkSaNQxcPFJqRmNHO2xiclpSJm04XUN41giTS1tUB1dw73YM7pQLdgDnUM9o307y-uZk-WYZ6KTuAL1WTNYACJNl-NHeyOZIk+6rxLbi0G3U+KDWol+9XqLRE72BvXGuEm-UBuzhB1ozwEAF8rGRJHFgFAkI5nFUGlEsABVF71YlOFxIagqKhQSlYACWchS7UKrIwk058jgACNGIYzFoUFEIVB2RJOeymAA1JisaiU-jIyncKC1epiwnkMieOUK5WqiBWKHJEh4+KEwUpKBiBKyuSaUUQLh6yFGzx00lIZkcrkcW5QLH8SbE4DMJASKBG7FWBLMKTAdmSABUUEYcAAXiAlnF6RApVSaVBgECVoySAGGdR7KXUBTWbhecz2iGINzWbyKdGSY2oM3A+X2-z6oKPWK1BpJdSp7L5RnzQZ2prqABxCASIXAZJS-A+u0Eqs1noOMcMzzV1G14gN+RhyYGxMUE2rxWMFUGK2bDaxBng6XLOq6M4imK3qXH6RD3scaTXsO8jBm6vavpcIDshAjAOB+ZDhpGkb8CBUB2iEng6KQC4gPO5g6HoTAFPRkqKDqADaAB0PFwLRTGMAAuri+KEmA7KQNQ4hlBUVSTMQPFcS0cSFPJAAeqmQkSKHEGhZBxkgvL6RozIcFgylYBpIn2ps7KMO6hCTI0xBgHS7IhIYpAFDqwpiGIYqdJp9YoXWN6uAiQVofoQX1K5jjuTIEDENFUAAPzaS2UVMFw1BJlGZEpAgjl3AixCFYg8hMvkRg6pw3Dyc+oUhX09X1FlBgxRlgZoeVyDJRaHCYZEUAAApIGI9KEjUUASHAaTUASSBygA5rwUCue5hgSMwKTCvIEbWeeuTANVEraGNE3OMASg0LRxAAIwAAzPS9j0cJMSl2YwxBpg4EAJHKjjvfUXHlbp6QchIf0aTqPr1AA9PDo3jZNUAAO6cgAFlAwrssty0QOeG0uJc7KusQcrQ4QBBEAAzMDWnabG8a6pcWmzfNUAAEQXajGPANjuP44TYlLS4VaYzIspxLZs1SIqnpc8ujPrWLhj3U9yuQjiUZaTGcYJnDkIc4YAAGvNXVAAAk1SUxAalQAA1FA91YqbWuxWr1AAKyPU7XgC0pjB+UG8CB7EDhiCkxBcDm3v3WZUCIzQqubTjEDAGjEB7lAvtW4mUMu89VuXDr9RYphpGiYmDgOAAypjEniRIy3iASMvKK5l1kMA1AWz3vJd5NXHE9AztPQIZHsnEACijB4+ynoAGLJEvcqdGQK2eC5XszTte2GTqo9QAApC7ABM1NEI9k-VwTwDYHjBNIDPamQPEZJeXvu3yFgwrUNtH+h8TAkDgFAfAwo0qJigNQYUmEyK4QXsvVe695Yt08ADeyqFp5zyQWKFeSA15y03i3V8ZFR4AHUsYNybitNuwAO5QDBnAWuNDEB0IqAw+B1dxKQHwkQXhSVEHLUXvglBxCVpYEodQxu7CW70JUodQkxI4jMHsp4PMhZiylhchJRwWB76P2Fi-N+e5KhYGOnALAb1Dr+QgFxYOy1iBcxUWo4AXF9xqWADHLi+g1TcywK4+yHj7beI4L4gaa1k5+xtGAwBB8JZSwBgwiW0A7QOAVPiKu8Q7EOLEE4lxhM3EhK8T4vxhglbaVUcEzxYSIkdSiUjGJSBoHxP2gLJJnIZYCzSZIDJGYslnlyY45xQT3G1LKRaaglSxklLqeUsMScmnOhaXE-e7TJaEmSd0zGvSoaZPiNkuIwz8mjKKTU0JkyNwBKqcUiZ4SFmNIvo9I5JyCmzPufU-xMzznjMuQ8yJSznmvLFHk95vy5lXO+YEiFnzHlAvuufF5QzQUjMKdUv5pSAXXJ+RiyF2LWCLOToil5-AgA" target="_blank">Playground</a>&nbsp;</figcaption><p>Aquí es la evaluación perezosa la que nos va a permitir procesar los datos bajo demanda y realizar acciones sobre los mismos teniendo pleno control sobre el ritmo y periodicidad al que lo hacemos. </p><p>Por ejemplo, podríamos querer persistir los datos cada determinado número de elementos, o enviar notificaciones a servicios externos sobre los datos encontrados. Y todo esto podríamos hacerlo además sin causar ninguna sobrecarga del sistema, ya que la complejidad espacial es insignificante y sólo se utilizará la memoria necesaria para cada paso de la iteración. </p><p>Y al igual que sucedía cuando usábamos nuestra función <mark>reduce</mark>, a nuestros transductores podemos pasarles diferentes reducers para acumular los datos de distintas formas. Por ejemplo, concatenando los datos en un nuevo array con <mark>concat</mark>:</p><pre><code>language-javascript<br />⁠<br />⁠const data = [<br />⁠  { name: &quot;Product 1&quot;, price: 74 },<br />⁠  { name: &quot;Product 2&quot;, price: 93 },<br />⁠  { name: &quot;Product 3&quot;, price: 110 },<br />⁠  { name: &quot;Product 5&quot;, price: 82 },<br />⁠];<br />⁠<br />⁠const eligibleForFinancing = filter(isEligibleForFinancing);<br />⁠const priceWithShippingCosts = map(addShippingCosts);<br />⁠const piped = pipe(eligibleForFinancing, priceWithShippingCosts);<br />⁠<br />const result = lazyTransduce(piped, concat, data, []);<br />⁠<br />⁠console.log(result.next().value); // [84]<br />⁠console.log(result.next().value); // [84]<br />⁠console.log(result.next().value); // [84, 20]<br />⁠console.log(result.next().value); // [84, 120]<br />⁠console.log(result.next().done); // true</code></pre><figcaption>Run me in <a href="https://www.typescriptlang.org/play/?strict=false&amp;noImplicitAny=false&amp;strictNullChecks=false&amp;strictFunctionTypes=false&amp;strictPropertyInitialization=false&amp;strictBindCallApply=false&amp;noImplicitThis=false&amp;noImplicitReturns=false&amp;alwaysStrict=false&amp;jsx=0#code/C4TwDgpgBAsghmSAnAPAQQDRQEID4oC8UAFHAMZkBcUmUAbnADYCuE12AlIftgNwBQoSFADCAewB2ZOMGARUafEVIVqaJEjgh0uLAxZsaXAvnWbtigYPDQAShAAmzMvPRY8hWAmRuc+AD6iktKyrnhWQtAAKpoSAM4AZmJIALbyAPq+HsoOMnBqxjwRNkEpYGJx0EQA3vxQUFm4xAkSaNQxcPFJqRmNHO2xiclpSJm04XUN41giTS1tUB1dw73YM7pQLdgDnUM9o307y-uZk-WYZ6KTuAL1WTNYACJNl-NHeyOZIk+6rxLbi0G3U+KDWol+9XqLRE72BvXGuEm-UBuzhB1ozwEAF8rGRJHFgFAkI5nFUGlEsABVF71YlOFxIagqKhQSlYACWchS7UKrIwk058jgACNGIYzFoUFEIVB2RJOeymAA1JisaiU-jIyncKC1epiwnkMieOUK5WqiBWKHJEh4+KEwUpKBiBKyuSaUUQLh6yFGzx00lIZkcrkcW5QLH8SbE4DMJASKBG7FWBLMKTAdmSABUUEYcAAXiAlnF6RApVSaVBgECVoySAGGdR7KXUBTWbhecz2iGINzWbyKdGSY2oM3A+X2-z6oKPWK1BpJdSp7L5RnzQZ2prqABxCASIXAZJS-A+u0Eqs1noOMcMzzV1G14gN+RhyYGxMUE2rxWMFUGK2bDaxBng6XLOq6M4imK3qXH6RD3scaTXsO8jBm6vavpcIDshAjAOB+ZDhpGkb8CBUB2iEng6KQC4gPO5g6HoTAFPRkqKDqADaAB0PFwLRTGMAAuri+KEmA7KQNQ4hlBUVSTMQPFcS0cSFPJAAeqmQkSKHEGhZBxkgvL6RozIcFgylYBpIn2ps7KMO6hCTI0xBgHS7IhIYpAFDqwpiGIYqdJp9YoXWN6uAiQVofoQX1K5jjuTIEDENFUAAPzaS2UVMFw1BJlGZEpAgjl3AixCFYg8hMvkRg6pw3Dyc+oUhX09X1FlBgxRlgZoeVyDJRaHCYZEUAAApIGI9KEjUUASHAaTUASSBygA5rwUCue5hgSMwKTCvIEbWeeuTAHAngcZM1QzXNhgAERjRNziEgAjDdWAbS41AAOwACwRsul2zfNUB3eNk1QAATK961LR9UAAJwAMx-RdV1AyDD1kISCNQ+9hhPU9AAMyP1AD13UOjYMAKw4zDhgABzg8jwn5aJiYOA4ADKAAWEniRIy3iAScSeC5oOPdQ92TbyrkY8AXG41AADUUCEwIZHsnEACijDsst7KegAYskBtyp0ZArSLuPUNtu0vjqCsAKQq4zBCu1ABNq6zy0QMA2C697SCa2pkDxGSXkzTte1IFgwrW5HdsmCQp34MKaWJlA1DCphZG4br+tikbSAm7NUgW0QCR2e6xAa9reeG8bpul-zr5kbjADqnJc9zvMrYLwDC0Q5WkOz3eIL3FT9y3rPiZA+FEDPSW53r9dF435v829tMd8AXc82P-N9yph2EsScTMPZnh5oWxali5EmOFgFEyFgx1wFgHGCdn+L+RAXGMGIy0nwQDPvZLi+41LAGIBwLi+hWBhigAAegQVADidNvqCVIt-MUf8AFAJAXLcBkDoGwK9GtJBKC0EYLPD-HBgDT7nwIRACBUCYEDTIcg1B30sBPXBgTKhWDf7-zocAhhYCmFENYR1dhFCuHOz4fwIAA" target="_blank">Playground</a>&nbsp;</figcaption><p>Hay mucho que decir sobre los transductores, especialmente porque hay numerosas implementaciones: se encuentran detrás de métodos ofrecidos por distintas librerías, como sucede con <mark>pipe</mark> de <a href="https://rxjs.dev/" target="_blank">RxJS</a>, o son directamente soportados como en el caso de <a href="https://ramdajs.com/docs/#transduce" target="_blank">Ramda</a>. Sucede con ellos lo mismo que con otras utilidades: no siempre tiene sentido utilizar las propias por el trabajo y riesgo que conlleva desarrollar la implementación más eficiente posible. Sin embargo es interesante tener presente su existencia y sus fundamentos, ya que nos muestra con claridad su potencial.</p><p>Como es habitual, el código se puede ejecutar en el Playground de TypeScript, así como está disponible para descarga en <a href="https://www.git.antoniodiaz.me/antoniodcorrea/blog-functional-programming-suporting-code/-/tree/master/src/III-transducers" target="_blank">git.antoniodiaz.me</a>.</p><p>Si tienes cualquier duda o has encontrado un error no dudes en escribirme a la dirección que figura en el pie.</p></p>]]></description></item><item><title>JavaScript funcional: conceptos básicos</title><pubDate>Wed, 21 Jan 1970 00:00:00 GMT</pubDate><guid isPermaLink="true">https://www.antoniodiaz.me/es/blog/12</guid><description><![CDATA[<p><p>Voy a iniciar una serie de artículos introductorios a la programación funcional. Abordaré este tema a modo de notas para recién llegados, pero también para mi futuro yo. El objetivo no será cubrir la programación funcional de un modo exhaustivo, sino proporcionar los conceptos y operaciones básicas sobre las que se basa.</p><p>La programación funcional se construye sobre operaciones muy comunes, como la composición o la aplicación parcial, desembocando en técnicas y operaciones especializadas, como los transductores. Su objetivo es minimizar los estados compartidos en nuestras aplicaciones, escribir código más declarativo —y por tanto más legible— y, finalmente, módulos más fáciles de testear.</p><p>Para esta serie vamos a utilizar un lenguaje común: Javascript. Este es un lenguaje que aunque funciona bien con programación funcional, no es un lenguaje pensado con este paradigma en mente como pueden serlo Erlang o Haskell. Por ello tendremos que implementar numerosas estructuras que en otros lenguajes se encuentran nativamente, lo que nos enseñará bastante sobre este tipo de programación.</p><p>Como aquí trataremos la programación funcional de un modo somero, para quien busque una aproximación algo más exhaustiva recomiendo dos recursos:</p><ul><li>Professor Fisby&#39;s Mostly Adequate Guide to Functional Programming, Brian Lonsdorf, 2021: <a href="https://mostly-adequate.gitbook.io/mostly-adequate-guide" target="_blank"><em>https://mostly-adequate.gitbook.io/mostly-adequate-guide</em></a>.</li><li>Funcional Light Javascript, Kyle Simpson, 2017: <a href="https://github.com/getify/Functional-Light-JS/tree/master" target="_blank"><em>https://github.com/getify/Functional-Light-JS/tree/master</em></a>.</li></ul><p>En este primer artículo vamos a desarrollar los conceptos y operaciones más básicas que podemos encontrar en la programación funcional. Hablaremos de:</p><ul><li>Inmutabilidad</li><li>Puridad</li><li>Aridad</li><li>Currying</li><li>Partial application</li><li>Estilo point free</li><li>Compose y pipe</li><li>Evaluación perezosa y ansiosa</li></ul><p>Repasaremos cada uno de estos conceptos, presentando ejemplos sencillos en Javascript para mantener todo lo más sencillo posible.</p><p>En subsiguientes artículos cubriremos la composición otra vez, pero con la mirada puesta en los tipos y su relación con operaciones de programación funcional más avanzadas.</p><h1>Inmutabilidad</h1><p>Uno de los principales problemas que nos podemos encontrar en programación es el <em>estado compartido</em>.</p><p>Digamos que estamos trabajando con Javascript, <mark>y</mark> que tenemos un objeto con una propiedad <mark>a</mark>, el cual es guardado en memoria en la variable <mark>x</mark> con cierto valor. Ahora copiamos <mark>x</mark> en una nueva variable <mark>y</mark>, lo que significa que hemos copiado la referencia de <mark>x</mark> en <mark>y</mark>. A partir de ahora todo lo que hagamos en <mark>y</mark> lo haremos sobre <mark>x</mark> también. Como se puede entender esto es problemástico, ya que podríamos cambiar <mark>x</mark> imperceptiblemente de maneras inesperadas.</p><pre><code>language-javascript<br />⁠<br />const sherlock = { name: &quot;Sherlock&quot; };<br />⁠const bilbo = sherlock;<br />⁠bilbo.name = &quot;Bilbo&quot;;<br />⁠⁠console.log(sherlock); // { name: &quot;Bilbo&quot; }</code></pre><figcaption>Run me in <a href="https://www.typescriptlang.org/play/?strict=false&amp;noImplicitAny=false&amp;strictNullChecks=false&amp;strictFunctionTypes=false&amp;strictPropertyInitialization=false&amp;strictBindCallApply=false&amp;noImplicitThis=false&amp;noImplicitReturns=false&amp;alwaysStrict=false&amp;target=99&amp;jsx=0&amp;module=1&amp;ts=4.0.2#code/MYewdgzgLgBBAWBTATgGxMA1jAvDA3jGAIYC2iAXDAEQDKSaGm1MAvgNwBQoksARgEtUfELjgN0WLoOEgAdCXJjqAISEjqXHhBCpEc9AHMAFAhSTMASnYwA9LYJEylGmtktWQA" target="_blank">Playground</a>&nbsp;</figcaption><p>Para solucionar este problema podemos usar el operador <em>spread</em>&nbsp;<mark>...</mark> y extraer las propiedades de <mark>john</mark> al crear un nuevo objeto <mark>bob</mark>:</p><pre><code>language-javascript<br /><br />const sherlock = { name: &quot;Sherlock&quot; };<br />⁠const bilbo = { ...sherlock };<br />⁠bilbo.name = &quot;Bilbo&quot;;<br />⁠sherlock.name = &quot;Sherlock&quot;;<br />⁠console.log(sherlock); // { name: &quot;Sherlock&quot; }</code></pre><figcaption>Run me in <a href="https://www.typescriptlang.org/play/?strict=false&amp;noImplicitAny=false&amp;strictNullChecks=false&amp;strictFunctionTypes=false&amp;strictPropertyInitialization=false&amp;strictBindCallApply=false&amp;noImplicitThis=false&amp;noImplicitReturns=false&amp;alwaysStrict=false&amp;target=99&amp;jsx=0&amp;module=1&amp;ts=4.0.2#code/MYewdgzgLgBBAWBTATgGxMA1jAvDA3jGAIYC2iAXDAEQDKSaGm1MAvgNwBQoksARgEtUfELgIwAdFIQp0WNl0HCQEkuTHUAQkJHUuMxllVlEG+rKZ7u4CCFSIJ6AOYAKA3MwBKdjAD0v8TVKGnNDZjYgA" target="_blank">Playground</a>&nbsp;</figcaption><p>Hay que tener en cuenta que el operator spread copiará por valor las propiedades en un primer nivel del objeto, <a href="https://www.typescriptlang.org/play/?strict=false&amp;noImplicitAny=false&amp;strictNullChecks=false&amp;strictFunctionTypes=false&amp;strictPropertyInitialization=false&amp;strictBindCallApply=false&amp;noImplicitThis=false&amp;noImplicitReturns=false&amp;alwaysStrict=false&amp;target=1&amp;jsx=0&amp;module=1&amp;ts=4.0.2#code/MYewdgzgLgBBAWBTATgGxMA1jAvDA3gFAwxgCGAtogFwwBEAykmhpnQDTExkAmPyiCBFr44UAYii06AJhkBGAEIxFZTChgNxiSRxgAHVGWA16AGXA9wdGAF9OtgNyFQkWACMAlqnchcBGAA6YIQUdCw7Zy8fEEDyKn86RW9fOiiU2N5+QQhA6AlYPCSyAHMYAFEwHjTCaN9ArIEhQMNjRESACRB3LyhrZxdwCBBURED0EoAKOtj49pwF+mSYugBKRxgAek2YcQBXREHIEbGJ6YyGvibc-J1CxeKyyur1rZ39w9dh0fGQKZnLtlmq0TLgHl0ep4+mA1httrtkAdCEdvqc-pNQiwsHFKPMHpjwmxXvCPiiTr8pgTWIDrnltJIwUU5EoVGoNFoCrC3jAAGZkVAQdqAGXIyT8zlTsY0ci0jKCFkULFVrMSdnyBcLCEA" target="_blank">pero las anidadas las copiará por referencia con <em>shallow copy</em></a>. Para evitar este problema podemos <a href="https://www.typescriptlang.org/play/?strict=false&amp;noImplicitAny=false&amp;strictNullChecks=false&amp;strictFunctionTypes=false&amp;strictPropertyInitialization=false&amp;strictBindCallApply=false&amp;noImplicitThis=false&amp;noImplicitReturns=false&amp;alwaysStrict=false&amp;target=1&amp;jsx=0&amp;module=1&amp;ts=4.0.2#code/MYewdgzgLgBBAWBTATgGxMA1jAvDA3gFAwxgCGAtogFwwBEAykmhpnQDTExkAmPyiCBFr44UAYii06AJhkBGAEIxFZTChgNxiSRxgAHVGWA16AGXA9wdGAF9OtgNyFQkWACMAlqnchcMACkGAHkAOQA6fTJkCEQACiCw8OhkTzAAc08AMwBPOIQUdCwASmLnLx8QcPIqfzpFb186csaq3n5BCGTtSTrVdJgAUTAeZsIK33D2gSFIoxM6gAkQdy8oa2cXcAgQVERw9HS4iaqaxFwcPHrWujKYAHp7mHEAV0QtyF39w+PWqb4Zl0UjpYJcrv0hiNbo4Hk9Xu9XDs9gcQEcTv8OrNDMZzmD6MtVp51mBobDnsg3oQPkjvqj8swiphqpRcZd6ExCqxSY9yZTEV8UUcCiwsBjAd0JKC2bIFMpVOpkJoelBuXCKQjtgKfsLGWLOnMcRcrhYRtY7jz4UA" target="_blank">usar</a> los métodos que nos provee el objeto JSON.</p><p>Otro ejemplo del problema de los estados compartidos podría suceder cuando nos movemos en operaciones concurrentes o que su. En este caso tenemos una operación <mark>increment</mark>, cuyo resultado es guardado en un objeto <mark>sharedState</mark>. Pero también tenemos la operación <mark>decrement</mark>, que computa un nuevo valor, guardándolo también en <mark>sharedState</mark>. El resultado es un estado compartido: <mark>sharedState</mark> hace referencia tanto al valor producido por <mark>increment</mark> como al de <mark>decrement</mark>. Teniendo en cuenta además que ambas operaciones son impuras, tenemos un código dificil de seguir y de testear.</p><pre><code>language-javascript<br /><br />const sharedState = { type: &quot;shared state&quot;, value: 0 };<br />⁠<br />⁠const increment = () =&gt; {<br />⁠  sharedState.value += 1;<br />⁠};<br />⁠<br />⁠const decrement = () =&gt; {<br />⁠  sharedState.value -= 1;<br />⁠};<br />⁠<br />⁠increment();<br />⁠decrement();<br />⁠decrement();<br />⁠increment();<br />⁠decrement();<br />⁠increment();<br />⁠<br />⁠⁠console.log(sharedState); // { type: &quot;shared state&quot;, value: 0 }</code></pre><figcaption>Run me in <a href="https://www.typescriptlang.org/play/?strict=false&amp;noImplicitAny=false&amp;strictNullChecks=false&amp;strictFunctionTypes=false&amp;strictPropertyInitialization=false&amp;strictBindCallApply=false&amp;noImplicitThis=false&amp;noImplicitReturns=false&amp;alwaysStrict=false&amp;target=99&amp;jsx=0&amp;module=1&amp;ts=4.0.2#code/MYewdgzgLgBBAWBDATgUwCYGUqKqmAvDAN4xQCeADqgFwwBECKGcOe9ANDAG6IA2AV1owADDAC+AbgBQ00JFgBLMMDQBbVGFhEAFAEpCAPhLSYcJGixtUAOl6D8AaiIBGGVNnzoMdKlWoNLUIYfSMTMyZLbFxbeyEYAFpXdxlpZX9AqH0ZXwzNLL0cv3V87LSVEq0y3MqCmXTasrlwCBA+Wz4QAHMdSIxovEKYAHphkjIqYXowcAS+9FYYzh5+IToxcVkgA" target="_blank">Playground</a>&nbsp;</figcaption><p>Este ejemplo no es exactamente concurrente, pero ejemplifica bien la dificultad de seguir el valor del estado compartido a través del flujo de la aplicación. Si además tuviésemos diferentes módulos consumiendo y modificando <mark>sharedState</mark> podríamos encontrarnos con problemas a la hora de saber cuál es el estado de nuestra aplicación en cada momento de su ejecución.</p><p>Este problema se puede solucionar fácilmente eliminando el estado compartido y produciendo uno nuevo con cada operación que se realiza:</p><pre><code>language-javascript<br /><br />const initialState = { type: &quot;non-shared state&quot;, value: 0 };<br />⁠<br />⁠const increment = (state) =&gt; ({<br />⁠  ...state,<br />⁠  value: state.value + 1,<br />⁠});<br />⁠<br />⁠const decrement = (state) =&gt; ({<br />⁠  ...state,<br />⁠  value: state.value - 1,<br />⁠});<br />⁠<br />⁠const stateIncremented = increment(initialState);<br />⁠const initialState2 = decrement(stateIncremented);<br />⁠const initialDecremented = decrement(initialState2);<br />⁠const initialState3 = increment(initialDecremented);<br />⁠const stateDecremented2 = decrement(initialState3);<br />⁠const initialState4 = increment(stateDecremented2);<br />⁠<br />⁠console.log(initialState4); // { type: &quot;non-shared state&quot;, value: 0 }</code></pre><figcaption>Run me in <a href="https://www.typescriptlang.org/play/?strict=false&amp;noImplicitAny=false&amp;strictNullChecks=false&amp;strictFunctionTypes=false&amp;strictPropertyInitialization=false&amp;strictBindCallApply=false&amp;noImplicitThis=false&amp;noImplicitReturns=false&amp;alwaysStrict=false&amp;target=99&amp;jsx=0&amp;module=1&amp;ts=4.0.2&amp;ssl=22&amp;ssc=1&amp;pln=1&amp;pc=1#code/MYewdgzgLgBAlmOU4EMA2BlKKoFMYC8MA3jFAJ4AOuAXDAERjgC0EAFigE64AmM0OXPQA0MAG7oArrRgAGGAF8A3ACgVoSLATBuAW1xhYRABQC8ASkIA+GMeIqYMAHQuzuYQ-FSZbpxLTSMADUMACMHgrmqurg0DA8uDq4+oaEtm6WBDZ2ni5Obh6O-tJ0vsX4zGERUWoacW4AkmBJKXh8RNp6BlDGCEiomNgWqnVaiMjoWIIATGkJLd2mQ7hNC4a8NaPw4wMAIold6+3xB8mLfRODM5uxY-2TywDMaZ1nhr076PtrbTea-Mtvoc2rMiPNgR97lc8I8-nELgMpngACwvZoQtxAt4gmoxSAgNC4JxoEAAc0hlyRuGRURgAHo6SQyFQZIwWOwuLwAYIRF4AjJ5Ao1EA" target="_blank">Playground</a>&nbsp;</figcaption><p>Los nombres de las variables elegidos no son elegantes ni demasiado expresivos, pero muestran la ventaja de este código frente al anterior: hemos pasado el estado de nuestro programa de una variable mutable cuyo contenido es sólo conocido en tiempo de ejecución, a una variable inmutable cuyo contenido es predecible en tiempo de compilación, motivo por el cual esta nueva versión de nuestro programa será más fácil de comprender, depurar y testear. En este sentido la inmutabilidad facilita la predictibilidad de nuestra aplicación.</p><p>Podríamos incluso guardar un histórico de todos los estados de nuestra aplicación, lo que nos facilitaría el proceso de depuración y análisis post-mortem en caso de que surjan problemas.</p><h1>Puridad </h1><p>La programación funcional tiene como objetivo la simplicidad y predictabilidad de nuestros programas, de ahí el uso intenso que hace de las funciones. Pero para que las funciones sean predecibles, dado el mismo input deben retornar el mismo output. En este sentido una función reprensenta una relación inmutable entre dos sets: input y output, y esta relación debe mantenerse constante:</p><pre><code>language-javascript<br /><br />⁠const pureFunction = (a, b) =&gt; a &lt; b;<br />⁠console.log(pureFunction(1, 2)); // true<br />⁠<br />⁠const impureFunction = (a, b) =&gt; (Math.random() &gt; 0.5 ? a &lt; b : a &gt; b);<br />⁠console.log(impureFunction(1, 2)); // true or false</code></pre><figcaption>Run me in <a href="https://www.typescriptlang.org/play/?strict=false&amp;noImplicitAny=false&amp;strictNullChecks=false&amp;strictFunctionTypes=false&amp;strictPropertyInitialization=false&amp;strictBindCallApply=false&amp;noImplicitThis=false&amp;noImplicitReturns=false&amp;alwaysStrict=false&amp;target=99&amp;jsx=0&amp;module=1&amp;ts=4.0.2#code/MYewdgzgLgBADgVwE4FMBiCzCgS3DAXhgAoBDAGhgCMBKQgPhlJgB5qBuAKFEhABsUAOj4gA5sUSoMWXOGIBGSgCYaNdjAD0GmFCQIUnbuGgwcAW0npM2PGEIkK1OgUbEAsqSgALQUlJgAExAzYjpGAAZBAFYYAH4mVmoYAC4ExlouHgh+IRFxc0tpGzlFGBU1TW1dfRgQJBgAM1I+CAMgA" target="_blank">Playground</a>&nbsp;</figcaption><p>La puridad facilita la predictabilidad, pero también nos permite memoizar resultados de funciones, evitando computaciones innecesarias.</p><h1>Aridad</h1><p>La aridad se refiere al número de parámetros que recibe una función.</p><ul><li>Una función es de aridad 0 cuando no recibe ningún parámetro: es <em>nularia</em>.</li><li>Una función es de aridad 1 cuando recibe sólo un parámetro: es <em>unaria</em>.</li><li>Una función es de aridad 2 cuando recibe sólo un parámetro: es <em>binaria</em>.</li><li>Una función tiene aridad n cuando puede recibir cualquier número de parámetros: es <em>n-aria </em>—también conocida como <em>variádica</em>—.</li></ul><pre><code>language-javascript<br /><br />⁠const nullaryFunction = () =&gt; &quot;Hello world&quot;;<br />⁠console.log(nullaryFunction()); // hello world<br />⁠<br />⁠const unaryFunction = (greeting) =&gt; `${greeting} world`;<br />⁠console.log(unaryFunction(&quot;hello&quot;)); // hello world<br />⁠<br />⁠const binaryFunction = (greeting, subject) =&gt; `${greeting} ${subject}`;<br />⁠console.log(binaryFunction(&quot;hello&quot;, &quot;world&quot;)); // hello world<br />⁠<br />⁠const nAryFunction = (...strings) =&gt; <br />⁠  strings.reduce((acc, curr) =&gt; `${acc}${curr} `, &quot;&quot;);<br />⁠console.log(nAryFunction(&quot;hello&quot;, &quot;world&quot;, &quot;and&quot;, &quot;pluton&quot;)); <br />⁠// hello world and pluton</code></pre><figcaption>Arities, run me in <a href="https://www.typescriptlang.org/play/?strict=false&amp;noImplicitAny=false&amp;strictNullChecks=false&amp;strictFunctionTypes=false&amp;strictPropertyInitialization=false&amp;strictBindCallApply=false&amp;noImplicitThis=false&amp;noImplicitReturns=false&amp;alwaysStrict=false&amp;target=99&amp;jsx=0&amp;module=1&amp;ts=4.0.2#code/MYewdgzgLgBGCuAbRBDATgTwGLzMKAluDALwwAUAlKQHwwBEAEgKbIgwDuIaiAJvQG4AUKEghEzAHSIQAc3IJk6bLnxEwVSgJgB6HTAAWrGZ258hI8NBi5lOPIWJlystM2aEws6iToADABIAb1d3T1kAX1MeXj9hUQhxKRl5W0x7NXByeiM2ekotXX1cky4YiwTYACMCMDtVRzBSClCPWtkAGhgIeCqAK2Z8H39g1vCo4J7+wagIuMsxCWk5chq69Ib1bJKQei76Mr58wr1DY3ZD3gqrWDAAQQ2HdWbySTfoNHaIYZghGG6oJ8vBBJG5ePBgMxyOQUMBgF1gPA0GgfoEgrDgBFgojkVE-Pt8vErEllvJ7o9Mhocuc9gxLrT6CgwPx9gAHRDwKDgY7aISnHbRPgwJm8GDszngIA" target="_blank">Playground</a>&nbsp;</figcaption><p>Nada especial aquí, únicamente algo de terminología. Pero es importante, ya que las funciones <em>unarias</em> están en el centro de algunas operaciones en programación funcional.</p><h1>Currying</h1><p>Currying es el proceso de transformar una función que recibe varios parámetros —aridad mayor que 1— en diversas funciones unarias anidadas —funciones que reciben únicamente un parámetro—.</p><pre><code>language-javascript<br /><br />⁠// Non curried function<br />⁠const sum = (a, b, c) =&gt; a + b + c;<br />⁠const result = sum(1, 2, 3);<br />⁠console.log(result); // 6<br />⁠<br />⁠// Curried function<br />⁠const sum = (a) =&gt; (b) =&gt; (c) =&gt; a + b + c;<br />⁠⁠const result = sum(1)(2)(3);<br />⁠console.log(result); // 6</code></pre><figcaption>Currying, run me in <a href="https://www.typescriptlang.org/play/?strict=false&amp;noImplicitAny=false&amp;strictNullChecks=false&amp;strictFunctionTypes=false&amp;strictPropertyInitialization=false&amp;strictBindCallApply=false&amp;noImplicitThis=false&amp;noImplicitReturns=false&amp;alwaysStrict=false&amp;target=99&amp;jsx=0&amp;module=1&amp;ts=4.0.2#code/PTAEDkHsDtQYwK4CckEsCmATUAzB04AXVGAKDhgGdDRKEBbUAXlAAoBDAGlACNu4AlMwB8odqADUvSfADc5KjSTo6AGxos69VgEZuAJm4BmAfIrRKkVegB0qyAHNWytYVOgQoAGylSngMLIaFi4+EQk0AoWNIgoGJgAygzMbOxCTKKsPOmZgiJiMjwycGaKoC4I6vopscGJDLoCrPpNJqUWVrb2ThVV7p5eQA" target="_blank">Playground</a>&nbsp;</figcaption><p>Como veremos más adelante ésta es una técnica clave en programación funcional: hay muchas operaciones en las que necesitaremos que las funciones tengan una estructura similar —un sólo input para un output—, permitiendo así su combinación en diferentes formas.</p><h1>Aplicación parcial</h1><p>La aplicación parcial se refiere al proceso de transformar una función recibiendo diversos parámetros en otra función diferente que recibirá únicamente algunos de esos parámetros, fijándolos en su contexto y devolviendo otra función que recibirá el resto de parámetros, combinándolos con los prefijados.</p><p>Esto es realmente útil, puesto que nos permite crear funciones con parámetros preaplicados con anterioridad. Lo que significa que podemos especializar funciones.</p><p>Por ejemplo, digamos que tenemos una función <mark>createUrl</mark> que recibe tres cadenas de texto como parámetros: <mark>protocol</mark>, <mark>domain</mark> y <mark>path</mark>, combinándolos y devolviendo otra cadena:</p><pre><code>language-javascript<br /><br />⁠const createUrl = (scheme, domain, path) =&gt; `${scheme}://${domain}/${path}`;<br />⁠<br />⁠const httpUrl = createUrl(&quot;http&quot;, &quot;example.com&quot;, &quot;hello&quot;);<br />⁠console.log(httpUrl); // &quot;http://example.com/hello&quot;<br />⁠<br />⁠const httpsUrl = createUrl(&quot;https&quot;, &quot;example.com&quot;, &quot;hello&quot;);<br />⁠console.log(httpsUrl); // &quot;https://example.com/hello&quot;</code></pre><figcaption>Run me in <a href="https://www.typescriptlang.org/play/?strict=false&amp;noImplicitAny=false&amp;strictNullChecks=false&amp;strictFunctionTypes=false&amp;strictPropertyInitialization=false&amp;strictBindCallApply=false&amp;noImplicitThis=false&amp;noImplicitReturns=false&amp;alwaysStrict=false&amp;target=99&amp;jsx=0&amp;module=1&amp;ts=4.0.2#code/MYewdgzgLgBMBOBTAhlRBVeAbGBeGAFBMABaIC2iANDACYjnICWYNADqiQJR4B8MAAwAkAb2JlKAXwBcAelmj6jFpIUiOUEpIEBuAFB7QkWCShQ2mHPgQo0lggCJT5hzQeIAHsnJssiAHSg5K4wTohYWCAOXPpGECB+-pEA5gTOFtgxMPKh6XKynt6+AUGyZBFRBnEmZmwQlnhwSKgY2I7pECHuXj6JQV3lkdGx4PGJKWm19Zk62bK5U-mFvSUMZeFDQA" target="_blank">Playground</a>&nbsp;</figcaption><p>Problablemente usemos esta función en numerosos lugares de nuestro código con &quot;http&quot; o &quot;https&quot;. Para escribir código más declarativo y evitar pasar el esquema siempre que necesitemos una urlpodemos crear dos versiones especializadas de <mark>createUrl</mark>: <mark>createHttpUrl</mark> y <mark>createHttpsUrl</mark> , fijando el parámetro <mark>scheme</mark> en el contexto de dichas funciones:</p><pre><code>language-javascript<br /><br />⁠const createUrl = (scheme, domain, path) =&gt; `${scheme}://${domain}/${path}`;<br />⁠<br />⁠// Partial application: fixing `scheme` parameter<br />⁠const createUrlWithProtocol = (scheme) =&gt; <br />⁠  (domain, path) =&gt; createUrl(scheme, domain, path);<br />⁠⁠<br />⁠⁠const createHttpUrl = createUrlWithProtocol(&quot;http&quot;);<br />⁠const createHttpsUrl = createUrlWithProtocol(&quot;https&quot;);<br />⁠<br />⁠const httpUrl = createHttpUrl(&quot;http&quot;, &quot;example.com&quot;, &quot;hello&quot;);<br />⁠console.log(httpUrl); // &quot;http://example.com/hello&quot;<br />⁠<br />⁠⁠const httpsUrl = createHttpsUrl(&quot;https&quot;, &quot;example.com&quot;, &quot;hello&quot;);<br />⁠console.log(httpsUrl); // &quot;https://example.com/hello&quot;</code></pre><figcaption>With partial application, run me in <a href="https://www.typescriptlang.org/play/?strict=false&amp;noImplicitAny=false&amp;strictNullChecks=false&amp;strictFunctionTypes=false&amp;strictPropertyInitialization=false&amp;strictBindCallApply=false&amp;noImplicitThis=false&amp;noImplicitReturns=false&amp;alwaysStrict=false&amp;target=99&amp;jsx=0&amp;module=1&amp;ts=4.0.2#code/MYewdgzgLgBMBOBTAhlRBVeAbGBeGAFBMABaIC2iANDACYjnICWYNADqiQJR4B8MAAwAkAb2JlKAXwBcAelmj6jFpIUiOUEpIEBuAFB75MAArJ4UJshzI2bLE2Com4aTABmTAB4sA5oPEUiAIwHPDIlGjweqCQsAgoaJhYAOpMmsbwIFAgoDj4RKSBPLj8BErMrCGcxbx6MHBIqBjYBRLUdAwV7NX60eDQDQmIABJQUGxJeINNSanpmdm5BABEJGNsy1z6MQPxTaPjEJP4e4nYcyQZWTkgWCtrh5u9O7APE9hTpyPrSSuInuE7IgAHSgcjLGirRBYLAgJ59SC3EGwnwEN5JLYwIyrdZyWT-QFYEFg2RkGFwgwvGBvI4fE6NNAHNi0u7LAnkIGghgQmBQ8nwnZI4EotHrFmY7E0vHszkksmw5Z6IA" target="_blank">Playground</a>&nbsp;</figcaption><p>Estas nuevas funciones son más expresivas semánticamente, así como más eficientes, puesto que reducen la aridad de las funciones que utilizamos para crear las urls.</p><h1>Estilo libre de puntos</h1><p>El estilo libre de puntos —<em>point-free style</em> en inglés— es una forma de escribir funciones en la que éstas son procesadas sin hacer mención a sus parámetros —puntos—.</p><p>Veamos un ejemplo: cuando pasamos una función como parámetro a otra función es práctica común escribirla dentro de ella:</p><pre><code>language-javascript<br /><br />⁠const userName = users.map(<br />⁠  ({ first, lastName }) =&gt; `${first} ${lastName}`<br />⁠);</code></pre><figcaption>With parameters</figcaption><p>En un estilo libre de puntos extraeríamos la declaración de la función para guardarla en una variable, que es lo que pasaremos a la nueva función:</p><pre><code>language-javascript<br /><br />⁠const composeName = ({ first, lastName }) =&gt; `${first} ${lastName}`;<br />⁠const userName = users.map(composeName⁠);</code></pre><figcaption>Point-free style</figcaption><p>En general el estilo libre de puntos es más declarativo y legible, facilitando la composición de funciones como veremos más adelante.</p><h1>Compose y pipe</h1><p>En álgebra la composición de funciones es el proceso de transformar un conjunto secuencial de funciones unarias en otras anidadas, de modo que el resultado de una función se pasará como parámetro a la siguiente envolviéndola.</p><p>Digamos que tenemos dos funciones:</p><p><span><span class="math-inline">f(x) = 2x\\
⁠⁠g(x) = x^2</span></span><br />⁠<br />Expresamos que las componemos con la siguiente sintaxis:</p><p><span><span class="math-inline">f \circ g = f(g(x))</span></span></p><p>Lo que quieres decir que ejecutamos primero <span><span class="math-inline">g(x)</span></span>, y después <span><span class="math-inline">f(x)</span></span> con el resultado obtenido. </p><p>Es importante remarcar que la composición no es conmutativa:</p><p><span><span class="math-inline">f \circ g \neq g \circ f</span></span></p><p>aunque sí es asociativa:</p><p><span><span class="math-inline">f(g(h(x))) = (f \circ g)(h(x)) = f(g(h(x)))</span></span>.</p><p>Todo esto se puede traducir a Javascript con facilidad, dado que en este lenguaje las funciones son <em>ciudadanos de primera clase</em>. Esto hace referencia a que podemos tratarlas como variables, de modo que cualquier función pueda aceptar otra como parámetro.</p><p>Dadas dos funciones podemos ejecutar una dentro de la otra:</p><pre><code>language-javascript<br /><br />⁠⁠const f = (x) =&gt; x * 2;<br />⁠const g = (x) =&gt; x * x;<br />⁠<br />⁠const result = f(g(3));<br />⁠console.log(result); // 18</code></pre><figcaption>Run me in <a href="https://www.typescriptlang.org/play/?strict=false&amp;noImplicitAny=false&amp;strictNullChecks=false&amp;strictFunctionTypes=false&amp;strictPropertyInitialization=false&amp;strictBindCallApply=false&amp;noImplicitThis=false&amp;noImplicitReturns=false&amp;alwaysStrict=false&amp;target=99&amp;jsx=0&amp;module=1&amp;ts=4.0.2#code/MYewdgzgLgBAZjAvDAFADwJRIHwzTAKhgCYBuAKFElgHMlVMc9C8LLxoYAnAUwgFcANrGRwUNFAGYMGClQghBPAHSCQE3gOGyYAel0wAjAA4gA" target="_blank">Playground</a>&nbsp;</figcaption><p>Y digamos que este tipo de operación es algo que hacemos a menudo: sería interesante si tuviésemos una utilidad que, dadas dos funciones nos las devolviese compuestas:</p><pre><code>⁠language-javascript<br /><br />⁠const f = (x) =&gt; x * 2;<br />⁠const g = (x) =&gt; x * x;<br />⁠<br />⁠const compose = (f, g) =&gt; (x) =&gt; f(g(x));<br />⁠<br />⁠const composed = compose(f, g);<br />⁠const resultComposed = composed(3); // 18</code></pre><figcaption>Run me in <a href="https://www.typescriptlang.org/play/?strict=false&amp;noImplicitAny=false&amp;strictNullChecks=false&amp;strictFunctionTypes=false&amp;strictPropertyInitialization=false&amp;strictBindCallApply=false&amp;noImplicitThis=false&amp;noImplicitReturns=false&amp;alwaysStrict=false&amp;target=99&amp;jsx=0&amp;module=1&amp;ts=4.0.2#code/MYewdgzgLgBAZjAvDAFADwJRIHwzTAKhgCYBuAKFElgHMlVMc9C8LLxoZQBbABxAgBTeijgAaGDSyJc6abjgoacjGyqce-IQBN6mgYNESpFdbABOgiAFcANlADCIPgd3J9OlAGZVMAPR+MACMABxAA" target="_blank">Playground</a>&nbsp;</figcaption><p>En este caso lo que tenemos es una función que recibe dos parámetros —aridad 2—, realiza una aplicación parcial para fijarlos en el contexto de la closura, y devuelve una nueva función que, recibiendo un nuevo parámetro, los usará para ejecutar nuestras funciones anidadas, usando ese nuevo parámetro como argumento inicial.</p><p>De esta manera la función devuelta es guardada en la variable <mark>composed</mark>, que tendrá <mark>f</mark> y <mark>g</mark> en su contexto, y hará uso de ellos cuando es llamada con un nuevo argumento —<mark>3</mark>—. </p><p>Ahora digamos que queremos componer tres funciones:</p><p><span><span class="math-inline">f \circ g \circ h = f(g(x))</span></span></p><p>Usando la misma lógica podemos traducir esto a Javascript con una función <mark>compose</mark> de aridad 3:</p><pre><code>language-javascript<br /><br />⁠const f = (x) =&gt; x * 2;<br />⁠⁠const g = (x) =&gt; x * x;<br />⁠const h = (x) =&gt; x - 1;<br />⁠<br />⁠const compose = (f, g, h) =&gt; (x) =&gt; f(g(h(x)));<br />⁠<br />⁠const composed = compose(f, g, h);<br />⁠console.log(composed(3)); // 8</code></pre><figcaption>Run me in <a href="https://www.typescriptlang.org/play/?strict=false&amp;noImplicitAny=false&amp;strictNullChecks=false&amp;strictFunctionTypes=false&amp;strictPropertyInitialization=false&amp;strictBindCallApply=false&amp;noImplicitThis=false&amp;noImplicitReturns=false&amp;alwaysStrict=false&amp;target=99&amp;jsx=0&amp;module=1&amp;ts=4.0.2#code/MYewdgzgLgBAZjAvDAFADwJRIHwzTAKhgCYBuAKFElgHMlVMc9C8KroYALe9LRXfAFoYARgqVwHUAFsADiAgBTHnAA0MGus59cvJnBQ0UnXhgzj2sGfKUATetYWKUajVvMTIIADaKAdN4gRo52KADMZqQwAPTRMAAcQA" target="_blank">Playground</a>&nbsp;</figcaption><p>Funciona perfectamente; pero no es muy escalable. El siguiente paso sería crear una función variádica que, dado cualquier número de funciones pasadas como parámetros, las devuelve compuestas.</p><pre><code>language-javascript<br /><br />⁠const f = (x) =&gt; x * 2;<br />⁠⁠const g = (x) =&gt; x * x;<br />⁠const h = (x) =&gt; x - 1;<br />⁠<br />const compose = (...fns) =&gt; (x) =&gt;<br />⁠  fns.reduceRight((acc, curr) =&gt; curr(acc), x);<br />⁠<br />⁠const composed = compose(f, g, h);<br />⁠console.log(composed(3)); // 8</code></pre><figcaption>Run me in <a href="https://www.typescriptlang.org/play/?strict=false&amp;noImplicitAny=false&amp;strictNullChecks=false&amp;strictFunctionTypes=false&amp;strictPropertyInitialization=false&amp;strictBindCallApply=false&amp;noImplicitThis=false&amp;noImplicitReturns=false&amp;alwaysStrict=false&amp;target=99&amp;jsx=0&amp;module=1&amp;ts=4.0.2#code/MYewdgzgLgBAZjAvDAFADwJRIHwzTAKhgCYBuAKFElgHMlVMc9C8KroYALe9LRXfAFoYARgqVwHUAFsADiAgBTHgDo1cSH1y8c5GPEgqATooAmAV2CKASgEsanKChQBDYMAA0MYOaNGt3r5Gru4YXpji7LAy8kqm9DEKiihwXjRenBhskiAANooquSA0KIlxKADMGFkwAPS1MAAcQA" target="_blank">Playground</a>&nbsp;</figcaption><p>Esta función recibe cualquier número de funciones como parámetros individuales, los distribuye en un array con el operador spread <mark>...</mark>, y los fija en el contexto de la closura, devolviendo una nueva función. Esta nueva función será <em>unaria</em>, recibiendo a su vez como parámetro el dato inicial para procesar nuestras funciones. Finalmente recorrerá el array de funciones de derecha a izquierda y ejecutándolas, lo que quiere decir que ejecutará primero la última con los datos iniciales proporcionados en el parámetro.</p><p>Lo que acabamos de hacer es crear una función <em>compose</em>: una utilidad variádica muy socorrida para componer cualquier número de funciones, siempre y cuando éstas sean unarias.</p><p>Ahora digamos que queremos componer nuestras funciones, pero empezando desde la primera hasta la última: en lugar de <mark>Array.reduceRight</mark> podemos usar <mark>Array.reduce</mark>: </p><pre><code>language-javascript<br /><br />⁠const f = (x) =&gt; x * 2;<br />⁠⁠const g = (x) =&gt; x * x;<br />⁠const h = (x) =&gt; x - 1;<br />⁠<br />const pipe = (...fns) =&gt; (x) =&gt;<br />  fns.reduce((acc, curr) =&gt; curr(acc), x);<br />⁠<br />⁠const piped = pipe(f, g, h);<br />⁠console.log(piped(3)); // 35</code></pre><figcaption>Run me in <a href="https://www.typescriptlang.org/play/?strict=false&amp;noImplicitAny=false&amp;strictNullChecks=false&amp;strictFunctionTypes=false&amp;strictPropertyInitialization=false&amp;strictBindCallApply=false&amp;noImplicitThis=false&amp;noImplicitReturns=false&amp;alwaysStrict=false&amp;target=99&amp;jsx=0&amp;module=1&amp;ts=4.0.2#code/MYewdgzgLgBAZjAvDAFADwJRIHwzTAKhgCYBuAKFElgHMlVMc9C8KroYALe9LRXfAFoYARgqVwHAA4BLKQFMeAOhVxIfXLxzkY8SEoBO8gCYBXYPJQoAhsGAAaGMFMGDGpy4M27GR5nHssLIKxvTBlnCONI6cGGySIAA28kqJIDQo4cYoAMwYcTAA9IUwOQCsQA" target="_blank">Playground</a>&nbsp;</figcaption><p>Este tipo de utilidad es conocida como <mark>pipe</mark>. Como dijimos anteriormente la composición no es conmutativa, por lo que devuelve un resultado distinto a la función <mark>compose</mark>.</p><h1>Evaluación perezosa y ansiosa</h1><p>Por &quot;evaluación ansiosa&quot; nos referimos al modelo de computación en que una variable es evaluada inmediatamente después de que una expresión aparezca en el código.</p><p>Por ejemplo, en Javascript:</p><pre><code>language-javascript<br /><br />⁠⁠const sum = (a, b) =&gt; a + b;<br />const result = sum(1, 2); // Computed here<br />⁠console.log(result); // 3</code></pre><figcaption>Run me in <a href="https://www.typescriptlang.org/play/?strict=false&amp;noImplicitAny=false&amp;strictNullChecks=false&amp;strictFunctionTypes=false&amp;strictPropertyInitialization=false&amp;strictBindCallApply=false&amp;noImplicitThis=false&amp;noImplicitReturns=false&amp;alwaysStrict=false&amp;target=99&amp;jsx=0&amp;module=1&amp;ts=4.0.2#code/MYewdgzgLgBBCuBbGBeGAKAhgGhgIwEpUA+GTGAanwG4AoUSWAJwFMEAbWNBRdARlwAmAtRgB6MTADCIRAAd4UFgBMYACxat64CCHYsAdOxABzdKw5QR4yQGYgA" target="_blank">Playground</a>&nbsp;</figcaption><p>Como vemos, la expresión es evaluada cuando llamamos a la función, no cuando el resultado de la función es usado.</p><p>Con la evaluación perezosa, por el contrario, se retrasa la computación hasta que el resultado es utilizado.</p><p>A excepción de los operadores lógicos <mark>&amp;&amp;</mark> y <mark>||</mark> y el operador ternario, Javascript no soporta la evaluación perezosa. Y eso es un problema, puesto que nuestro código siempre consumira recursos por el total de operaciones que reflejemos en el código.</p><p>Sin embargo ésta puede ser implementada de diversas formas. Con los mencionados operadores lógicos por ejemplo —y de una forma muy limitada—, o usando generadores:</p><pre><code>language-javascript<br /><br />⁠function* lazySum(a, b) {<br />⁠  yield a + b;<br />⁠}<br />⁠<br />⁠let result = lazySum(1, 3); // computation delayed<br />⁠console.log(result); // not evaluated yet<br />⁠console.log(result.next().value); // 4</code></pre><figcaption>Run me in <a href="https://www.typescriptlang.org/play/?strict=false&amp;noImplicitAny=false&amp;strictNullChecks=false&amp;strictFunctionTypes=false&amp;strictPropertyInitialization=false&amp;strictBindCallApply=false&amp;noImplicitThis=false&amp;noImplicitReturns=false&amp;alwaysStrict=false&amp;target=99&amp;jsx=0&amp;module=1&amp;ts=4.0.2#code/GYVwdgxgLglg9mAVAAgDYEMBeBPAyiAWwAp0AaZAIwEpkBvAKGWWxgFNUATZdZAakoDc9AL716qVlGQAnVgGcQqKQF40WPISIBGcgGYqA5AHojyCHAIAHEFHSwEyDu3TZWHeubBy4EgHSo4AHMiWQUlA2NTMDgpVgA3dFQQOzdmSQ8Ebz8A4NDFKF8wVgAPKCIqXwSk1giTZAAWIA" target="_blank">Playground</a>&nbsp;</figcaption><p>Aquí la suma no se ejecutará cuando llamamos a <mark>lazySum</mark>, sino cuando usamos el resultado y llamamos <mark>next()</mark>.</p><p>Más adelante veremos operaciones en programación funcional que se ejecutan perezosamente.</p><h1>Conclusión</h1><p>Esto es todo por hoy. Todos estos conceptos y operaciones son bastante comunes; pero son los cimientos de las operaciones que veremos próximamente. El código del post se puede ejecutar en el Playground de TypeScript, así como en <a href="https://www.git.antoniodiaz.me/antoniodcorrea/blog-functional-programming-suporting-code/-/tree/master/src/I-basic-concepts?ref_type=heads" target="_blank">git.antoniodiaz.me</a>.</p><p>Si tienes cualquier duda o has encontrado un error no dudes en escribirme a la dirección que figura en el pie.</p></p>]]></description></item><item><title>JavaScript funcional: transformers</title><pubDate>Wed, 21 Jan 1970 00:00:00 GMT</pubDate><guid isPermaLink="true">https://www.antoniodiaz.me/es/blog/13</guid><description><![CDATA[<p><p>En nuestro anterior artículo desarrollamos algunos de los elementos básicos de la programación funcional. En él discutimos conceptos como inmutabilidad, puridad o aridad y operaciones como aplicación parcial, currying o composición, todas ellas elementos fundamentales de este paradigma.</p><p>En este artículo vamos a apoyarnos en estas operaciones básicas para construir algunas funciones que nos van a ayudar a la hora de procesar datos voluminosos. Pero antes repasemos de nuevo la composición.</p><h1>Composición de funciones</h1><p>Cuando describimos la composición de funciones comenzamos con la definición que se da en álgebra:</p><blockquote>... proceso de transformar un conjunto secuencial de funciones unarias en otras anidadas, de modo que el resultado de una función se pasará como parámetro a la siguiente.</blockquote><p>Esto lo logramos haciendo uso de una función a la que llamamos <mark>compose</mark>, que se ejecuta de derecha a izquierda:</p><pre><code>language-javascript<br />⁠<br />⁠⁠const f = (x) =&gt; x * 2;<br />⁠const g = (x) =&gt; x * x;<br />⁠const h = (x) =&gt; x + 2;<br />⁠<br />const compose = (...fns) =&gt; (x) =&gt;<br />⁠  fns.reduceRight((acc, curr) =&gt; curr(acc), x);<br />⁠<br />⁠const composed = compose(f, g, h);<br />⁠const result = composed(3);<br />⁠console.log(result); // 50</code></pre><figcaption>Compose, run me in <a href="https://www.typescriptlang.org/play/?strict=false&amp;noImplicitAny=false&amp;strictNullChecks=false&amp;strictFunctionTypes=false&amp;strictPropertyInitialization=false&amp;strictBindCallApply=false&amp;noImplicitThis=false&amp;noImplicitReturns=false&amp;alwaysStrict=false#code/FAYw9gdgzgLgBAMzgXjgCgB4EoUD44ZwBUcATANyiSxwDmK62eBxBl408AFg5jsvkIBqMpSqc44ALYAHMFACmvAHSqE0fvj55gcRNGUAnBQBMAriAUAlAJa0uMNGgCGIEABpJZw4c1efLm5YnthiHDTScoomDJHyCmgInrSeXFjs1PDGUGYANvCocdFoAMzp4lBguQrKuWC0aNl5MOlwAPRtcACsAAxAA" target="_blank">Playground</a>.</figcaption><p>O una función <mark>pipe</mark>, que se ejecuta de izquierda a derecha:</p><pre><code>language-javascript<br />⁠<br />⁠⁠const f = (x) =&gt; x * 2;<br />⁠const g = (x) =&gt; x * x;<br />⁠const h = (x) =&gt; x + 2;<br />⁠<br />const pipe = (...fns) =&gt; (x) =&gt;<br />⁠  fns.reduce((acc, curr) =&gt; curr(acc), x);<br />⁠<br />⁠const piped = pipe(f, g, h);<br />⁠const result = piped(3);<br />⁠console.log(result); // 38</code></pre><figcaption>Pipe, run me in <a href="https://www.typescriptlang.org/play/?strict=false&amp;noImplicitAny=false&amp;strictNullChecks=false&amp;strictFunctionTypes=false&amp;strictPropertyInitialization=false&amp;strictBindCallApply=false&amp;noImplicitThis=false&amp;noImplicitReturns=false&amp;alwaysStrict=false#code/MYewdgzgLgBAZjAvDAFADwJRIHwzTAKhgCYBuAKFElgHMlVMc9C8KroYALe9LRXfAGoSFSuA4AHAJYSApknIxUAOlVxIfbIoabtS9RGUAnWQBMArsFkoUAQ2DAANDGDmjRzS7dG7DjM8xRdlhpOVN6UOs4ZxpnTgw2cVgTCHMAG1hkSNMUAGYE8jFIEDTZZTSQGhQU9KgEmAB6BphcgA4gA" target="_blank">Playground</a>.</figcaption><p>También comentamos que la composición no es conmutativa, <span><span class="math-inline">f \circ g \neq g \circ f</span></span>:</p><pre><code>⁠language-javascript<br />⁠<br />⁠const composedA = compose(f, g);<br />⁠const composedB = compose(g, f);<br />⁠<br />⁠const isCommutative = composedA(3) === composedB(3);<br />⁠<br />⁠console.log(isCommutative); // false</code></pre><figcaption>Run me in <a href="https://www.typescriptlang.org/play/?strict=false&amp;noImplicitAny=false&amp;strictNullChecks=false&amp;strictFunctionTypes=false&amp;strictPropertyInitialization=false&amp;strictBindCallApply=false&amp;noImplicitThis=false&amp;noImplicitReturns=false&amp;alwaysStrict=false&amp;target=1&amp;jsx=0&amp;module=1#code/MYewdgzgLgBAZjAvDAFADwJRIHwzTAKhgCYBuAKFElgHMlVMc9C8LLxoYBLCAYRAC2AgK5QAhlC4A3AKb04KGigDMGLIg0wlC1RgpUIIADYyAdEZBKe-IaInSZemAHpn8MUYgzyQA" target="_blank">Playground</a>.</figcaption><p>Si bien sí es asociativa, <span><span class="math-inline">f \circ (g \circ h) = (f \circ g) \circ h</span></span>:</p><pre><code>language-javascript<br />⁠<br />⁠const composedA = compose(f, compose(g, h));<br />⁠const composedB = compose(compose(f, g), h);<br />⁠<br />⁠const isAssociative = composedA(3) === composedB(3);<br />⁠<br />⁠console.log(isAssociative); // true</code></pre><figcaption>Run me in <a href="https://www.typescriptlang.org/play/?strict=false&amp;noImplicitAny=false&amp;strictNullChecks=false&amp;strictFunctionTypes=false&amp;strictPropertyInitialization=false&amp;strictBindCallApply=false&amp;noImplicitThis=false&amp;noImplicitReturns=false&amp;alwaysStrict=false&amp;target=1&amp;jsx=0&amp;module=1#code/MYewdgzgLgBAZjAvDAFADwJRIHwzTAKhgCYBuAKFElgHMlVMc9C8KroYALe9LRXfAGoSFSuA6gAtgAcQEAKY84AGhg0+uXkzgoavDKPawpshQBMAgvRNz5KFTBsLdqzhgNjqjkDNtmAQtY+pnZOdg7qrh6eHACWEBYQECDAsQCGULEAborIYZYoAMx8iHnBfv5F0ewgADbyAHS1IHrxicmpGdnyBjAA9H0wUABOAK7y5EA" target="_blank">Playground</a>.</figcaption><p>Nada nuevo, todo esto ya habíamos visto en nuestro anterior artículo. Pero es bueno repasarlo, ya que es fundamental para lo que vamos a tratar a continuación.</p><h1>Operando con iterables</h1><p>Hasta ahora hemos visto cómo podemos componer funciones para trabajar con primitivos u objetos; pero hemos obviado el caso de los iterables, como puede ser el caso de <mark>Array</mark> en Javascript. </p><p>Para poder trabajar con ellos tenemos que apoyarnos en algunas utilidades que vamos a implementar a continuación.</p><h2>Reducers</h2><p>La primera función que nos va a ayudar a trabajar con iterables es conocida como &quot;reducer: funciones binarias —de aridad 2— que <em>reducen</em> dos valores en un único resultado.</p><p>Un reducer sencillo tiene la siguiente forma:</p><pre><code>language-javascript<br />⁠<br />⁠const accumulate = (a, b) =&gt; a + b;<br />⁠console.log(accumulate(1, 2)); // 3<br />⁠<br />⁠const concat = (array, val) =&gt; [...array, val];<br />⁠console.log(concat([1], 2)); // [1, 2]<br />⁠<br />⁠const power = (base, exponent) =&gt; base ** exponent;<br />⁠console.log(power(2, 4)); // 16</code></pre><figcaption>Run me in <a href="https://www.typescriptlang.org/play/?strict=false&amp;noImplicitAny=false&amp;strictNullChecks=false&amp;strictFunctionTypes=false&amp;strictPropertyInitialization=false&amp;strictBindCallApply=false&amp;noImplicitThis=false&amp;noImplicitReturns=false&amp;alwaysStrict=false&amp;target=1&amp;jsx=0&amp;module=1#code/MYewdgzgLgBAhsYBXAtkgNnKBTGBeGACjgC4YxUAjbAJwBoZKyKVqaBKZq2-APnhgBqRgG4AUKEgh02AHToQAc2KJUGLNkIBGBgCZ27ETAD0xmAGYxE8NBiTgWfDAA8AQV7EaNOAE8yrr183XgYANzh0f04YAO8fYL4YAG1ZVLhAnzCIgF1xSQhpOQVleyxCJK1svQMjU2SdGF1sq3zYAAcQAHceAkJKOAhsLlZaBmwADw6wbDAoYbZoljZE-sGYACp1mAmpmag8m0L5JUIO7ppCXQYAFhqTMy0ANjEgA" target="_blank">Playground</a>.</figcaption><p>Aunque también puede hacer merge de dos objetos:</p><pre><code>language-javascript<br />⁠<br />⁠const merge = (a, b) =&gt; ({ ...a, ...b });<br />⁠console.log(merge({ a: 1 }, { b: 2 })); // { a: 1, b: 2 }</code></pre><figcaption>Run me in <a href="https://www.typescriptlang.org/play/?strict=false&amp;noImplicitAny=false&amp;strictNullChecks=false&amp;strictFunctionTypes=false&amp;strictPropertyInitialization=false&amp;strictBindCallApply=false&amp;noImplicitThis=false&amp;noImplicitReturns=false&amp;alwaysStrict=false&amp;target=1&amp;jsx=0&amp;module=1#code/MYewdgzgLgBAtgUwE4HMEwLwwDwBUA0MA0gHwAUAUDDAIYBcMASgqEgCbbRICWYKhuEviowARg2asOXXv2IkKASgksQ7TlB58BMAD7zMJGGQDeMAHSWahS+dEwAvooDcFCqEggANgnNeQKGSIqAimtAwAjI6EZuIwAEyOii4wAPSpMGb0MBGEcYkOFEA" target="_blank">Playground</a>.</figcaption><p>O puede contener una lógica un poco más elaborada: por ejemplo, un reducer que recibe una lista y un valor, el cual es sumado a cada elemento de esa lista:</p><pre><code>language-javascript<br />⁠<br />⁠const sumEach = (iterable, val) =&gt; {<br />⁠  const result = [];<br />⁠<br />⁠  for (const item of iterable) {<br />⁠    result.push(item + val);<br />⁠  }<br />⁠<br />⁠  return result;<br />⁠};<br />⁠<br />⁠const result = sumEach([1, 2], 3);<br />⁠console.log(result); // [4, 5]</code></pre><figcaption>Run me in <a href="https://www.typescriptlang.org/play/?strict=false&amp;noImplicitAny=false&amp;strictNullChecks=false&amp;strictFunctionTypes=false&amp;strictPropertyInitialization=false&amp;strictBindCallApply=false&amp;noImplicitThis=false&amp;noImplicitReturns=false&amp;alwaysStrict=false&amp;target=1&amp;jsx=0&amp;module=1#code/MYewdgzgLgBBCuBbAogQ2ACxgXhgCgCgYYAbAS2gC4YBBAJztQE8AeMJAIwFM6A+AGiIwAbqhLwu1dom50CASmr1GrabN44NAbyGhIsOlwQkoShszaceG3AG0AugG4CQgGYg6+PdBhkoXRBgQV1IKKHkYHWJiQ2MoADoAB3gIDDw-AJgAahExCXlnYgBfFxiuKHg6MBhY+BNnIucCbwMjOthcBBR0NNsARn4YACZ7QYBmAubwCBASLniSEABzPFqTApgAek2YWwAWQYBWeyA" target="_blank">Playground</a>.</figcaption><p>Esta última función es interesante, pero tiene un problema: hace dos cosas entrelazadas. Por una parte &quot;reduce&quot; los dos valores en uno sólo —dos enteros a un único valor, un array y un entero a otro array, dos objetos a uno sólo, etc.—, y por otra contiene cierta lógica que ejecuta sobre los valores a reducir —sumar un valor a cada item del array—.</p><p>Sería perfecto si pudiésemos desacopar ambas tareas. </p><p>Podemos empezar extrayendo la parte de la suma, que le pasaríamos como un callback y que a su vez sería un reducer: así podríamos ejecutarlo con el iterable a transformar, que será inicializado con un valor inicial <mark>initialValue</mark>. Y como nuestra función <mark>sumEach</mark> ya no tendrá la responsabilidad de la suma, podremos darle un nombre acorde a lo que hace: <mark>reduce</mark>.</p><pre><code>language-javascript<br />⁠<br />⁠const reduce = (reducer, iterable, initialValue) =&gt; {<br />⁠  let accumulation = initialValue;<br />⁠<br />⁠  for (const item of iterable) {<br />⁠    accumulation = reducer(accumulation, item);<br />⁠  }<br />⁠<br />⁠  return accumulation;<br />⁠};<br />⁠<br />⁠const data = [1, 2, 3];<br />⁠const increaseOne = (acc, val) =&gt; acc.concat([val + 1]);<br />⁠const result = reduce(increaseOne, data, []);<br />⁠console.log(result); // [2, 3, 4]</code></pre><figcaption>Run me in <a href="https://www.typescriptlang.org/play/?strict=false&amp;noImplicitAny=false&amp;strictNullChecks=false&amp;strictFunctionTypes=false&amp;strictPropertyInitialization=false&amp;strictBindCallApply=false&amp;noImplicitThis=false&amp;noImplicitReturns=false&amp;alwaysStrict=false&amp;target=1&amp;jsx=0&amp;module=1#code/MYewdgzgLgBATgUwCYFdgJgXhgCka9OAGhgEsoE4BDAIwBsETSxzSq6A1dlBASiwB8MAN4AoGDAawqwYCgC2KOlSilwWMi1XsudHgG5R4mADMQcXKEixyCeTBAmyFavT4jjEmXMXLV67Hw0ShxvBSUVNTAmCnleQwkAXyMJRCgUODAYMN9I8ENEw1EraBgkFSoNAG0ARhIAJhIAZgBdQxKbMGBEKggEAHkwDGxQ2RIAN3Z+TCFvADorYBUcKsm6GABqGBqW+OLwUsQIJVhA5GCcZm6EXoGhknKoKhIq3faDkAY5uhAAczwEMc6FB4jAAPRgmBVRowJokAAsLSAA" target="_blank">Playground</a>.</figcaption><p>Aquí ya hemos dado un paso: hemos logrado separar la parte de la operación de suma respecto de la operación de reducción, y albergarla en <mark>increaseOne</mark>. Y si esta función <mark>reduce</mark> te suena familiar, es porque es prácticamente idéntica al método <mark>Array.reduce</mark> de JavaScript.</p><pre><code>language-javascript<br />⁠<br />⁠const data = [1, 2, 3];<br />⁠const increaseOne = (acc, val) =&gt; acc.concat([val + 1]);<br />⁠const result = data.reduce(increaseOne, []);<br />⁠console.log(result); // [2, 3, 4]</code></pre><figcaption>Run me in <a href="https://www.typescriptlang.org/play/?strict=false&amp;noImplicitAny=false&amp;strictNullChecks=false&amp;strictFunctionTypes=false&amp;strictPropertyInitialization=false&amp;strictBindCallApply=false&amp;noImplicitThis=false&amp;noImplicitReturns=false&amp;alwaysStrict=false&amp;target=1&amp;jsx=0&amp;module=1#code/MYewdgzgLgBAJgQygmBeGBtAjAGhgJjwGYBdAbgChRJYBLMYAJwFMEJmB5MZtGACgTBgeAG4IANgEo0APhiDgAOmrAkfDGPEwA1DCwlJlatBgsIAV3Gx0iZIpZxzwZn3pNW7LszwYDR8BAg4syK4iAA5nxmllCGMAD08ZiEMER4ACwkQA" target="_blank">Playground</a>.</figcaption><p>Si ahora recordamos nuestra función <mark>compose</mark>, veremos que está usando <mark>Array.reduceRight()</mark>.</p><pre><code>language-javascript<br />⁠<br />⁠const compose = (...fns) =&gt; (initialData) =&gt; <br />⁠  fns.reduceRight((acc, curr) =&gt; curr(acc), initialData);</code></pre><p>Podemos ver qué sucede al reemplazar <mark>Array.reduceRight()</mark> por nuestro <mark>reduce</mark>.</p><pre><code>language-javascript<br />⁠<br />⁠const compose = (...fns) =&gt; (initialData) =&gt; <br />⁠  reduce((acc, curr) =&gt; curr(acc), fns.reverse(), initialData);<br />⁠<br />⁠const functionA = (value: number) =&gt; value + 1;<br />⁠const functionB = (value: number) =&gt; value * 2;<br />⁠const functionC = (value: number) =&gt; value + 3;<br />⁠<br />⁠const composed = compose(functionA, functionB, functionC);<br />⁠const result = composed(1);<br />⁠<br />⁠console.log(result); // 9</code></pre><figcaption>Run me in <a href="https://www.typescriptlang.org/play/?strict=false&amp;noImplicitAny=false&amp;strictNullChecks=false&amp;strictFunctionTypes=false&amp;strictPropertyInitialization=false&amp;strictBindCallApply=false&amp;noImplicitThis=false&amp;noImplicitReturns=false&amp;alwaysStrict=false&amp;target=1&amp;jsx=0&amp;module=1&amp;ts=4.2.3#code/MYewdgzgLgBATgUwCYFdgJgXhgCka9OAGhgEsoE4BDAIwBsETSxzSq6A1dlBASiwB8MAN4AoGDAawqwYCgC2KOlSilwWMi1XsudHgG5R4mADMQcXKEiwAbtwwgTZCtXp8RxiTLmLlq9dj4aJQ43gpKKmpgJHZ6fIYSAL5GEohQKHBgMGG+keCGiYaiVtAwoPIADiAQGNg4AHSNJpD8mEI4zKzsACIqVK1CxkHoOKGyJHJwcANlGXBjwLwkzRD1iDaUNThLml10vVD9RSWwJihgwP5gAIIaOLE8AFwwYAo0lDMPGADUMACMhhOpnOlyiACE7l9nq95O9poIYF8YAAqGAAJkB4FKZwuVwAwpD7NC3h8EUjfgBmY5Y2DlKo1JAaOnVBA4HGg8DXZYgq5g7m4qJ43iY6zwBAQJSwbDMhk4P7CowlEAMep0EAAczw4slwpgAHo9TAAJxAA" target="_blank">Playground</a>.</figcaption><p>Funciona perfectamente. Lógico, ya que los tipos de entrada y salida son prácticamente los mismos. Cabe destacar que hemos invertido el array que alberga las funciones a componer  con <mark>Array.reverse()</mark>, ya que <mark>compose</mark> debe ejectarlas de derecha a izquierda.</p><p>Para implementar <mark>pipe</mark> procederíamos del mismo modo, pero sin invertir el array, ya que las debe ejecutar de izquierda a derecha:</p><pre><code>language-javascript<br />⁠<br />⁠const pipe = (...fns) =&gt; (initialData) =&gt; <br />⁠  reduce((acc, curr) =&gt; curr(acc), fns, initialData);<br />⁠<br />⁠const functionA = (value: number) =&gt; value + 1;<br />⁠const functionB = (value: number) =&gt; value * 2;<br />⁠const functionC = (value: number) =&gt; value + 3;<br />⁠<br />⁠const piped = pipe(functionA, functionB, functionC);<br />⁠const result = piped(data);<br />⁠<br />⁠console.log(result); // 7</code></pre><figcaption>Run me in <a href="https://www.typescriptlang.org/play/?strict=false&amp;noImplicitAny=false&amp;strictNullChecks=false&amp;strictFunctionTypes=false&amp;strictPropertyInitialization=false&amp;strictBindCallApply=false&amp;noImplicitThis=false&amp;noImplicitReturns=false&amp;alwaysStrict=false&amp;target=1&amp;jsx=0&amp;module=1&amp;ts=4.2.3#code/MYewdgzgLgBATgUwCYFdgJgXhgCka9OAGhgEsoE4BDAIwBsETSxzSq6A1dlBASiwB8MAN4AoGDAawqwYCgC2KOlSilwWMi1XsudHgG5R4mADMQcXKEiwAbtwwgTZCtXp8RxiTLmLlq9dj4aJQ43gpKKmpgJHZ6fIYSAL5GEohQKHBgMGG+keCGiYaiVtAwAA6kZRjYOAB09SaQ-JhCOMys7AAiKlTNQkHoOKGyJHJwcH0wY3DDwLwkjRBMWmx03VC9RSWwJihgwP5gAIIaOLE8AFwwYAo0lJPnGADUMACMhtumewdRAEKnjyuN3kdwmghgjxgACoYAAmD7gUq7faHADCAPsQNu93BkJeAGYtojYKB5GUQBBkBoKlUcMifuAjgtvodfsyUVFUbwEdZ4AgIEpYNhSeTKUgcK9uUYSiAGLU6CAAOZ4fmC7kwAD0GpgAHYgA" target="_blank">Playground</a>.</figcaption><p>Fantástico, ya hemos implementado nuestro propio <mark>Array.reduce()</mark>, que es agnóstico respecto del reducer que se le pasa, y lo hemos usado en nuestros propios <mark>compose</mark> y <mark>pipe</mark>.</p><h2>Transformers</h2><p>Ahora que ya tenemos nuestra función <mark>reduce</mark> podemos avanzar un paso más allá y ver qué son y para qué sirven los &quot;transformers&quot;.</p><p>Una de las operaciones más comunes que se puede hacer sobre iterables es convertir cada uno de sus elementos de un tipo <mark>A</mark> a otro tipo diferente <mark>B</mark>. </p><p>Para ello podemos crear una función <mark>map</mark> que recibirá una función de transformación a la que llamaremos <mark>mapper</mark>, y que será la encargada de realizar la operación para transformar cada elemento del iterable. Este <mark>map</mark>  creará otra función que admitirá un reducer, y que se encargará de la operación de acumular los elementos del iterable a una estructura de datos determinada —otro array, un objeto, un entero, etc—, pero independientemente de la transformación que se realice sobre cada elemento del iterable. A su vez esta segunda función devolverá otro reducer, el cual aplicará ambas operaciones —la transformación (el <mark>mapper</mark>) y la acumulación (el <mark>reducer</mark>)— sobre el iterable que se le pase:</p><pre><code>language-javascript<br />⁠<br />const map = (mapper) =&gt; (reducer) =&gt; (acc, value) =&gt;<br />⁠  reducer(acc, mapper(value));</code></pre><p>Suena complicado, pero quedémonos con la idea de que nuestro <mark>map</mark>, al ser llamado con un <mark>mapper</mark>, crea una función que tiene una particularidad: recibe un reducer y devuelve otro reducer.</p><p>Fijémonos en el siguiente ejemplo: dado un array con enteros, podemos hacer uso de la función <mark>map</mark> pasándole una función que convertirá el entero que reciba a una cadena de texto. Después volvemos a llamar a esta función pasándole el acumulador a utilizar —devolver un nuevo array con <mark>concat</mark>— para finalmente pasarle esta última función a <mark>reduce</mark> y que la utilice.</p><pre><code>language-javascript<br />⁠<br />⁠const map = (mapper) =&gt; (reducer) =&gt; (acc, value) =&gt;<br />⁠  reducer(acc, mapper(value));<br />⁠<br />⁠const data = [1, 2, 3];<br />const integerToString = map((x) =&gt; x.toString());<br />⁠const integerToStringWithReducer = integerToString(concat);<br />⁠const result = reduce(integerToStringWithReducer, data, []);<br />⁠<br />⁠console.log(result); // [&quot;1&quot;, &quot;2&quot;, &quot;3&quot;]</code></pre><figcaption>Run me in <a href="https://www.typescriptlang.org/play/?strict=false&amp;noImplicitAny=false&amp;strictNullChecks=false&amp;strictFunctionTypes=false&amp;strictPropertyInitialization=false&amp;strictBindCallApply=false&amp;noImplicitThis=false&amp;noImplicitReturns=false&amp;alwaysStrict=false&amp;target=1&amp;jsx=0&amp;module=1&amp;ts=4.2.3&amp;ssl=35&amp;ssc=7&amp;pln=35&amp;pc=22#code/C4TwDgpgBAShAmBXAxtAvFA3gKClAPAIIB8AFAE4IoTkBcsVq5RANFCWwJbA0CGARgBsI9QuXK8QRYlwB23Tr0EA1JYhHsAlKIDcuAoTYAhMpSRN6cczVZQTXHhKEaxEqRyid5wRSrUajbTs9PFsTUn08M2o6BmtmQ3YZSM9HAWFRcUlpFhSvBSVVQXVMt2l9INdskz0AXz1sUEgoAFleMEgE42IoDFJeZGRRNgA3f3pA3p6jPSboAGEAe1lkXmBHaV6ofsHS7I8x4pdNKfYs92IGubiYsJ6MNo6bRJMoAB8oJZW1jZrsbGQywAzsAoNFUJZGOhtuCaA4+M45AU-EcTmgejg8MJQQNkIgALaIQRrTjLLb5HyFfwNPAAM0W5G2gNkIKgh3UUEWtNSCOEJ0xeCguIJRJJZIwsPIOzxhOJPmWo38mhCUFq-yiEGAiHIsiFgxFctJsjqemZrPx7V6+jupAtT1i-VEaOmzv0FChsSstxexFdeGlitRUxSkoDUDtnVI7IgmmVKQBwNBzNWoIw0n65z2F0DTqzm3RUAA2gA6Uu8c6BgC6DTNoPga14W0LAEY2AAmNgAZmr-1rnlkPAA5jQACqLADKwHIXkHWztpFIAA96LICfwaM6oIvi8AJ1OZ6RY6bE-2h6O99PZIOAOrcAAWXqY5IHEGH5DHk8vg9IybWcb7lBAkSqZglCpBeGe74XjOt7AA+HpsPWwC8GwhaVnGCYsoswjFoIizfoBwHKlAAD0JFFgARM2FFsBRbY0VAFGdhRlZAA" target="_blank">Playground</a>.</figcaption><p>Y del mismo modo que hemos creado una función <mark>map</mark> que transforma valores podríamos crear una función similar para seleccionar elementos de un array. </p><p>Por ejemplo, en lugar de nuestro <mark>map</mark>, podemos crear una función <mark>filter</mark> que recibiese como argumento otra función a la que llamaremos <mark>predicate</mark> y que evaluase si un elemento dado del array debe o no quedarse en el array. Y al igual que en el <mark>map</mark> esta segunda función recibirá un acumulador encargado de la operacion <mark>reduce</mark> a realizar y devolverá otro reducer. La diferencia radica en que en el caso del <mark>filter</mark> este último reducer aplicará el predicado sobre el valor en cuestión, y si el resultado es <mark>true</mark>, lo añadirá al array; en caso contrario, lo descartará.</p><pre><code>language-javascript<br />⁠<br />const filter = (predicate) =&gt; (reducer) =&gt; (acc, val) =&gt; <br />⁠  predicate(val) ? reducer(acc, val) : acc;</code></pre><p>Esta función filter la podríamos utilizar de un modo muy similar a como hicimos con <mark>map</mark>.</p><pre><code>language-javascript<br />⁠<br />const filter = (predicate) =&gt; (reducer) =&gt; (acc, val) =&gt;<br />⁠  predicate(val) ? reducer(acc, val) : acc;<br />⁠<br />⁠const data = [1, 2, 3];<br />⁠const isOdd = filter((x) =&gt; x % 2 !== 0);<br />⁠const isOddWithReducer = isOdd(concat);<br />⁠const result = reduce(isOddWithReducer, data, []);<br />⁠<br />⁠console.log(result); // [1, 3]</code></pre><figcaption>Run me in <a href="https://www.typescriptlang.org/play/?strict=false&amp;noImplicitAny=false&amp;strictNullChecks=false&amp;strictFunctionTypes=false&amp;strictPropertyInitialization=false&amp;strictBindCallApply=false&amp;noImplicitThis=false&amp;noImplicitReturns=false&amp;alwaysStrict=false&amp;target=1&amp;jsx=0&amp;module=1&amp;ts=4.2.3#code/C4TwDgpgBAShAmBXAxtAvFA3gKClAPAIIB8AFAE4IoTkBcsVq5RANFCWwJbA0CGARgBsI9QuXK8QRYlwB23Tr0EA1JYhHsAlKIDcuAoTYAhMpSRN6cczVZQTXHhKEaxEqRyid5wRSrUajbTs9PFsTUn08M2o6BmtmQ3YZSM9HAWFRcUlpFhSvBSVVQXVMt2l9INdskz0AXz1sUEgoAFleMEgE42IoDFJeZGRRNgA3f3pA3p6jPSboAGEAe1lkXmBHaV6ofsHS7I8x4pdNKfYs92IGubiYsJ6MNo6bRJMoAB8oJZW1jZrsbGQywAzsAoNFUJZGOhtuCaA4+M45AU-EcTmgejg8MJQQNkIgALaIQRrTjLLb5HyFfwNPAAM0W5G2gNkIKgh3UUEWtNSCOEJ0xeCguIJRJJZIwsPIOzxhOJPmWo38mhCUFq-yiEGAiHIsiFgxFctJsjqemZrNpnEEjl6+jupDAZk4qx49H6ojRPX4i0Wwl4sg9+goUNiVluL2IAbw0sVgkjgodCCdawgpEOJwA-GDg9G2UoTvRcSq8ADgaDmc6ttJ+uc9hcY+7a5t0VAANoAOg7vHOMYAug0zaD4GteFsWwBGNgAJjYAGY+-8B54gQB5eDwLYWq00UikAAe9FkBP4NA9UF3UAApFBJ1AAIRoDAABmVJZZoM4K7XAHVuAALUNMOSn7wKQ5ZrC+i6UECRKghKUKkB+q7wD+wD-sGbBDsAvBsC2PYvq+QI+hAbaCIsADmQbQVaypQAA9LRrZQBOUBzkAA" target="_blank">Playground</a>.</figcaption><p>Al igual que sucedía <mark>reduce</mark>, podemos ver que <mark>filter</mark> y <mark>map</mark> tienen sus análogos nativos en <mark>Array.map</mark> y <mark>Array.filter</mark>, produciendo resultados similares. Pero en este caso nuestras funciones tienen una particularidad: pueden componerse.</p><p>Por ejemplo, podemos crear sendas funciones que primero seleccionen los valores impares y seguidamente sumen <mark>1</mark> a cada uno de los resultantes con <mark>pipe</mark>.</p><pre><code>language-javascript<br />⁠<br />const data = [1, 2, 3, 4, 5];<br />⁠<br />⁠const isOdd = filter((x: number) =&gt; x % 2 !== 0);<br />⁠const incrementValue = map((x: number) =&gt; x + 1);<br />⁠<br />const incrementOddValues = pipe(incrementValue, isOdd);<br />⁠const incrementValueWithReducer = incrementOddValues(concat);<br />⁠const result = reduce(incrementValueWithReducer, data, []);<br />⁠<br />⁠console.log(result); // [2, 4, 6]</code></pre><figcaption>Run me in <a href="https://www.typescriptlang.org/play/?strict=false&amp;noImplicitAny=false&amp;strictNullChecks=false&amp;strictFunctionTypes=false&amp;strictPropertyInitialization=false&amp;strictBindCallApply=false&amp;noImplicitThis=false&amp;noImplicitReturns=false&amp;alwaysStrict=false&amp;target=1&amp;jsx=0&amp;module=1&amp;ts=4.2.3#code/C4TwDgpgBAShAmBXAxtAvFA3gKClAPAIIB8AFAE4IoTkBcsVq5RANFCWwJbA0CGARgBsI9QuXK8QRYlwB23Tr0EA1JYhHsAlKIDcuAoTYAhMpSRN6cczVZQTXHhKEaxEqRyid5wRSrUajbTs9PFsTUn08M2o6BmtmQ3YZSM9HAWFRcUlpFhSvBSVVQXVMt2l9INdskz0AXz1sUEgoAFleMEgE42IoDFJeZGRRNgA3f3pA3p6jPSboAGEAe1lkXmBHaV6ofsHS7I8x4pdNKfYs92IGubiYsJ6MNo6bRJMoAB8oJZW1jZrsbAgAA8wItyMAoNcACoSWQAZ3idy2FEYNEsKOYRm6JzQPSstxel0a4AWiwAtiDYegsPo7qQAGayQj0aG8OEIglBFls-HdEIGYxseZkBlMqBc+E8uwyKAMozMmESpj4TGfYichXswWE0IvQVsAAiZBSIvlrMVz26uTweFlpu5SpVQqt1oZ8zt5uY8wNxAq7s1UENdQayGWsPBYE4kHoS3Ji0pvX0pAAdCmGbDsT68KR8j4lPq1rwMyloqhSDtkGxkIhxBmoFXxOXNGw03ICoJ88BC8HQ+CSxo8agkX3yA4+M5W7m-Edazg8MJwQMq6TEII1pxllsc74iuoGjbQdsQ3DwYd1FBFnTUmPhCdZ9bF4hl6ufBuMMPy4+V2vlqN-Jo+bU-xRBAwDVrIUAPk+36yEG2BHmGUCku0CY6t0pBIU8sT9KItaTDiibDmi-omEWWaLr+05TMW6LlmwGGdKQp4QJo-4pHBPYypwgiOCh-JSqQYBmJwqw8PQ2FaKc-CLIswisqR2yETcSoclRZGDBR8l4IJCDCWsECMUoJwAPxQO+5FQIcJz0IufJ4Oxx51ssIlbNI-TnHsFwUThHmbDiUAANopkmvDnBRAC63YOfABZbP5ACMbAAExsAAzGwAAsbAAKwRf88HgpwsIAPLwPAWx0lxjhloC9CyI+-A0LWgJQAApFAiVQAAhGgGAAAysflngrJQpIQLIwA7lSGHVbV9WNaczUANRQHFrH2QhXjICNY3ACV8CTbCWwRpA2bDRAo3jZNXDFaVA0cZt22Xf4ADq3AABYDjQm5nRdu2lQdpBHiJd0OZQsIruCb4oqdW3nTtk2vcAH3omw0Wdmw-lhWt8EyRASaCIsADmyLg9x-5QAA9BTAXJVAmVQAAbGFQA" target="_blank">Playground</a>.</figcaption><p>Y al igual que anteriormente, la composición no es conmutativa, de modo que el orden de las funciones importa: si en lugar de <mark>pipe</mark> —de izquierda a derecha— usamos <mark>compose</mark> —de derecha a izquierda— vamos a ver un resultado diferente.</p><pre><code>language-javascript<br />⁠<br />const data = [1, 2, 3, 4, 5];<br />⁠<br />⁠const isOdd = filter((x: number) =&gt; x % 2 !== 0);<br />⁠const incrementValue = map((x: number) =&gt; x + 1);<br />⁠<br />const incrementOddValues = compose(incrementValue, isOdd);<br />⁠const incrementValueWithReducer = incrementOddValues(concat);<br />⁠const result = reduce(incrementValueWithReducer, data, []);<br />⁠<br />⁠console.log(result); // [3, 5]</code></pre><figcaption>Run me in <a href="https://www.typescriptlang.org/play/?strict=false&amp;noImplicitAny=false&amp;strictNullChecks=false&amp;strictFunctionTypes=false&amp;strictPropertyInitialization=false&amp;strictBindCallApply=false&amp;noImplicitThis=false&amp;noImplicitReturns=false&amp;alwaysStrict=false&amp;target=1&amp;jsx=0&amp;module=1&amp;ts=4.2.3#code/C4TwDgpgBAShAmBXAxtAvFA3gKClAPAIIB8AFAE4IoTkBcsVq5RANFCWwJbA0CGARgBsI9QuXK8QRYlwB23Tr0EA1JYhHsAlKIDcuAoTYAhMpSRN6cczVZQTXHhKEaxEqRyid5wRSrUajbTs9PFsTUn08M2o6BmtmQ3YZSM9HAWFRcUlpFhSvBSVVQXVMt2l9INdskz0AXz1sCAAPMAB7cmAoUEgoABUJWQBnADN2gFsbRJMoDFJ4XmBeUU0Z4mDsbG7oAFleMEgE4zXZ3mRkUTYAN396QNX1ragAYVbZZAXHaRmoUlPz9iy7hkUGuxRcKzQayqQIajysMTCxygu32kyOUAAPs9Xu9gJ8ahtmm0Ol1wNB+rwhvFEd8KIwaJZ6cwjEcIWt4UxEXpHi8xm1BugsPpEaRhrJCPQKUNRuQJoc7MQglKRuM0QqQgZjGwnmQxRK+gMVbK1fYoGKjJLDTK5fgWc9FZbKUabYkdRqwtq2AARMgpPWO6Wq+X2P2yC0Gp3Wmx2nW5PB4MVPAPOmxPb3ECrJqPyn11BrIV6DToFvmtAX0Xn89D6UgAOnrYsGbJr+R8Si9C14zfjUGiqFIvzObGQiHEbKgI-Eg+QmjYjdrlEuNAFpFnnm8vg7i00DQ2BaGnT7Gg5grp8QcfGccgKfjB45weGEnT+iDGiEEC04r2+rd8RXUDQJu0Pz7kWIL+FArTDKkl7CCsD7xi+b4fj434YEe5DTq+76fq8Vz+Du+i1BsUQQMAo6yFASE4ahsh5tgoGdGMewzMKUxkMxqKxL8yz3HckI1hhjLUux3Y-H8+F3qsKQYdObCcQcpCguomiET2DGFp0wycIIjisaE7GkGAZicLiGg8Vo9z8K0rTCJSYlnjEwkIqJ0l4HJ4GCGJeDGQgpkLBASlKCsAD8vZMh5oIrPQfwangGkHhOOILN80i-ICpTZB4oLLJlQL3AA2vWta8ICkkALr5ppUDzIs3wFQAjGwABMbAAMxsAALGwACslV7tVnCDAA8vA8DfNpuk0AOTT0LIr78DQ45NFAACkUDNVAACEaAYAADIRjHrsglATLIwD-oKnEzXNC1LfcK0ANRQA1hEJWBXgnRAZ3AKN8CXYM3wllWpCfadEDnZdXAjWNh2DW84OQ-4ADq3AABYnuQP4I99EO-WNAOkPuuJw4llCDO+nTofSoM4z9l2o8AGNMmwtW8GwBXlW9oG2RAtaCK0ADmdIU7pO5QAA9BLUAFR1UB9UAA" target="_blank">Playground</a>.</figcaption><h2>Conclusión</h2><p>Recapitulemos: hemos visto qué son los reducers, qué son los transformers y dos funciones relacionadas con ellos: mappers y filters.</p><ul><li><strong>Reducer</strong>: función pura que acepta dos valores y los reduce en un único valor. No se pueden componer, ya que devuelven un valor único, mientras que reciben dos.</li><li><strong>Transformer</strong>: función encargada de transformar datos en iterables. Las usamos para crear otro tipo de funciones que se pueden componer con facilidad.</li><li><strong>Mapper</strong>: función que acepta otra función como parámetro, la cual convierte datos del tipo <mark>A</mark> al tipo <mark>B</mark> usando la función recibida.</li><li><strong>Filter</strong>: función que acepta una función predicado —que devuelve  <mark>booleano</mark>—, y crea otra función que usará ese predicado para saber si un elemento del array en cuestión debe mantenerse en el valor devuelto o no.</li></ul><p>Es importante tener claros estos conceptos, ya que son la base de lo que vamos a ver a continuación: los <em>transducers</em>. </p><p>Esto es todo por hoy. Como siempre, el código se puede ejecutar en el Playground de TypeScript, así como está disponible para descarga en <a href="https://www.git.antoniodiaz.me/antoniodcorrea/blog-functional-programming-suporting-code/-/tree/master/src/II-transformers" target="_blank">git.antoniodiaz.me</a>.</p><p>Si tienes cualquier duda o has encontrado un error no dudes en escribirme a la dirección que figura en el pie.</p></p>]]></description></item><item><title>Una introducción a Rust</title><pubDate>Tue, 20 Jan 1970 00:00:00 GMT</pubDate><guid isPermaLink="true">https://www.antoniodiaz.me/es/blog/1</guid><description><![CDATA[<p><p>Desde hace algún tiempo Rust ha estado ganando popularidad entre desarrolladores, siendo el lenguaje más apreciado los últimos cinco años según las <a href="https://insights.stackoverflow.com/survey/2021#overview" target="_blank">estadísticas de stackoverflow</a>. Pero otros índices, como el de <a href="https://www.tiobe.com/tiobe-index/" target="_blank">Tiobe</a>, lo posicionan por debajo de los veinte lenguajes más usados. Es además dificil encontrar programadores actualmente trabajando con este lenguaje, o empresas utilizándolo en producción.</p><p>¿Qué significa todo esto? Mientras que Rust es reconocido como una herramienta de valor por parte de la comunidad de desarrolladores, su integración en la industria está requiriendo tiempo, quizás debido a su especialización y particularidades. ¿Por qué, entonces, invertir tiempo en un lenguaje así? ¿Cómo pinta el futuro de Rust? Para responder a todas estas cuestiones echaremos un vistazo al lenguaje, su sintaxis, sus funcionalidades fundamentales y cómo puede ser utilizado para aplicaciones web básicas.</p><h1>Características</h1><p>Rust se originó en el equipo de Mozilla como el proyecto personal de uno de sus desarrolladores, Graydon Hoare. Desde entonces ha evolucionado hasta convertirse en un proyecto soportado por una amplia comunidad de programadores, con la <a href="https://foundation.rust-lang.org/" target="_blank">Rust Foundation</a> como sus principal órgano de gobierno.</p><p>Tiene algunas características que, juntas, lo hacen diferente a otros lenguajes:</p><ul><li>Es un lenguaje <strong>compilado</strong>.</li><li>Es de <strong>tipado fuerte</strong>, con tipado <strong>nominativo</strong> para structs y <strong>estructural</strong> para tuplas.</li><li>Carece de <strong>recolector de basura</strong>, usando un «verificador de préstamos» —«borrow checker»— en su lugar<strong>.</strong></li><li>Provee de <strong>punteros/referencias</strong> para el stack, y<strong> punteros avanzados </strong>para el heap.</li><li>Está concebido tanto para <strong>OOP</strong> como para programación <strong>funcional.</strong></li><li>As <strong>agnóstico respecto del entorno de ejecución asíncrono</strong> que se use.</li><li>Tiene una <strong>suite de tests integrada</strong> en el lenguaje.</li></ul><p>Veamos cada punto para ver qué puede ofrecernos cada característica.</p><p>Siendo un lenguaje compilado, Rust nos ofrecerá gran cantidad de información sobre el uso de memoria de nuestro programa antes de la ejecución del mismo. Haciendo uso de estructuras fuertemente tipadas Rust garantiza que, una vez que el programa compila, es seguro en cuanto a uso de memoria. Es importante mencionar que usa tipado nominativo para structs y estructural para tuplas: esto quiere decir que dos instancias de structs diferentes tendrán diferente tipo para el compilador incluso cuando esos structs tengan una forma idéntica; lo contrario sucede con las tuplas: dos tuplas tendrán el mismo tipo siempre que tengan la misma forma. </p><p>Por supuesto, nosotros los programadores podremos cometer errores de muchas otras maneras, especialmente en cuanto a lógica de negocio se refiere; pero el compilador nos asegura que, estructuralmente, nuestro programa será tan eficiente y coherente como sea posible. </p><p>Esto sucede principalmente por el hecho de que Rust no depende de un sistema de recolección de basura. Lenguajes como Java, Python o Javascript dependen de un recolector de basura para manejar la memoria y eliminar variables que quedan en desuso. En otros lenguajes, como C o C++, se requiere manejar los recursos manualmente; pero en Rust tenemos un «comprobador de préstamos», esto es, un sistema que se asegura <em>en tiempo de ejecución</em> que cuando una variable queda fuera de uso, esta es eliminada.</p><p>Esta funcionalidad es automática para todos los valores cuyo tamaño sea conocido de antemano, almacenándose en el stack. Podemos hacer además uso de punteros al estilo C —<mark>(*&amp;)</mark>— para referenciarlos y deferenciarlos; siempre con algunos límites impuestos por Rust para evitar acciones inseguras con la memoria.</p><p>Para aquellos otros valores cuyo tamaño no es conocido de antemano por el compilador, y que tienen que ser almacenaos en el heap, como por ejemplo los arrays —«vectores» en Rust—, el lenguaje nos ofrecerá estructuras específicas —<mark>Box</mark>, <mark>Rc</mark>, <mark>Arc</mark>, etc.—para envolverlos y asegurar que serán elminados lo más pronto posible.</p><p>Una de las principales funcionalidades de Rust es el retorno a una versión simplificada de las estructuras al estilo C, en oposición a las clases a las que lenguajes como C++ o Java nos tienen acostumbrados. Aquí encontraremos tres términos clave relacionados con estas estructuras:  <mark>struct</mark>, <mark>impl</mark> y <mark>trait</mark>. Podemos definir un nuevo struct con <mark>struct</mark>; después de definirlo, podemos añadirle métodos usando <mark>impl</mark>. Finalmente, en caso que queramos describir un comportamiento común para structs diferentes podremos hacer uso de <mark>trait</mark>, que viene a ser algo similar a lo que en otros lenguajes se denominan «interfaces». Todo esto se vé más claro con un ejemplo:</p><pre><code>language-rust<br />⁠<br />⁠// Podemos definir un struct para Persona<br />⁠//  con un nombre que podrá ser un String<br />⁠struct Persona {<br />  nombre: String,<br />⁠}<br />⁠<br />⁠// Igualmente podemos definir un struc para Gato<br />⁠//  también con un nombre String<br />⁠struct Gato {<br />  nombre: String,<br />⁠}<br />⁠<br />⁠// Cada ser vivo puede realizar ciertas acciones.<br />⁠//  Por ejemplo, presentarse<br />⁠trait SerVivoAcciones {<br />  fn presentarse(&amp;self);<br />⁠}<br />⁠<br />⁠// La persona puede presentarse verbalmente<br />⁠impl SerVivoAcciones for Persona {<br />  fn presentarse(&amp;self) {<br />    println!(&quot;- {}: Hola! Es un placer :)&quot;, &amp;self.nombre);<br />  }<br />⁠}<br />⁠<br />⁠// El gato también puede presentarse, pero… al estilo gatuno<br />⁠impl SerVivoAcciones for Gato {<br />  fn presentarse(&amp;self) {<br />    println!(&quot;- {}: Mrrrrr… Miaaou&quot;, &amp;self.nombre);<br />  }<br />⁠}<br />⁠<br />⁠// Podemos instanciar una Persona y un Gato<br />⁠//  y hacer que se presenten<br />⁠fn main() {<br />  let fulanito = Persona {<br />    nombre: String::from(&quot;Fulanito&quot;),<br />  };<br />  let minino = Gato {<br />    nombre: String::from(&quot;Minino&quot;),<br />  };<br />  fulanito.presentarse(); // - Fulanito: Hola! Es un placer :)<br />  minino.presentarse(); // - Minino: Mrrrrr… Miaaou<br />⁠}</code></pre><figcaption>Sintaxis de Rust: <mark>struct</mark>, <mark>trait</mark>, and <mark>impl</mark>: pruébame en el <a href="https://play.rust-lang.org/?version=stable&amp;mode=debug&amp;edition=2021&amp;gist=fba7d06fa9fadb9f1f6105b45005b6b3" target="_blank">playground de Rust</a>&nbsp;</figcaption><p>Este uso de <mark>structs</mark> en lugar de clases evita que escribamos estructuras muy anidadas, favoreciendo la composicion de elementos sobre las herencias. Rust nos permitirá escribir código tanto orientado a objetos como funcional, pero con ciertos límites en ambos casos. Es perfectamente posible usar patrones clásicos de programación orientada a objetos como la inyección de dependencias o funcionalidades como el polimorfismo, así como también lo es escribir código funcional con clausuras sin mutación de estado. Ambas aproximaciones encajan con naturalidad, aunque con algunas restricciones que nos inducirán a escribir estructuras más atómicas y legibles. El código resultante tendrá una sintaxis algo más compleja que en otros lenguajes, pero con estructuras  planas y sencillas que, al final, harán el código más fácil de leer y entender.</p><p>Rust se distribuye con una librería estándar que provee de diferentes funcionalidades. Entre ellas se encuentran las herramientas de threading, que nos permiten escribir programas concurrents. Esto, con la ayuda del compilador, el tipado estático, el verificador de préstamos y los punteros avanzados nos asegurarán que nuestros programas concurrentes serán seuros en cuanto a memoria se refiere, incluso en caso de multi-threading.</p><p>Una funcionalidad interesante de Rust es que no trae un tiempo de ejecución asíncrono integrado. Para disponer de uno vamos a tener que elegir e instalar uno externo: una opción bastante popular es Tokio, que se puede encontrar en el <a href="https://crates.io/crates/tokio" target="_blank">repositorio oficial de Rust</a>, aunque tenemos más opciones entre las que elegir.</p><p>Los tests se escriben con la suite que Rust trae integrada. Es posible escribir tanto tests unitarios para cada módulo como tests de integraciíon para toda la aplicación. Tiene unas funcionalidades muy concretas, pero muy claras y coherentes con la filosofía del lenguaje.</p><p>Trabajando con Rust viniendo de lenguajes tipo Java puede generar algo de confusión, pues hay un cambio de paradigma considerable; sin embargo, una vez que esta primera fase es superada, el lenguaje se escribe con asombrosa naturalidad. Tiene similitudes con C y C++, pero con una sintaxis muchísimo más limpia. Dadas las herramientas que nos ofrece para manejo de memoria y genéricos bien se podría describir Rust como «una versión declarativa de C».</p><h1>Rust en práctica</h1><p>Para demostrar cómo se podría estructurar un progama en Rust podemos vamos a crear una aplicación sencilla, simplemente para visualizar la sintaxis del lenguaje.</p><p>Podríamos, por ejemplo, escribir un programa al que pudieramos pedir una lista de usuarios que estuviera almacenada en algún tipo de base de datos, digamos que en PostgreSQL. Y además, queremos que nos dé esta lista en sentido inverso al que está en la base de datos.</p><p>De este modo nuestro programa tendría varios conceptos, a saber:</p><ul><li>Un usuario <mark>User</mark>, que sería el modelo: un <mark>struct</mark>.</li><li>El proceso de invertir la lista, que correspondería a la lógica de negocio: esto sería un «caso de uso», que podríamos escribir en un <mark>struct</mark>. Este caso de uso utilizará el módulo que escribamos para recoger los datos de la base de datos, el cual tendrá que cumplir con un <mark>trait</mark> que vamos a describir en el siguiente punto. </li><li>Y finalmente, el sistema para recoger los datos de la base de datos en PostgreSQL, que será otro <mark>struct.</mark> Para evitar acoplar el caso de uso con el repositorio de PostgreSQL escribiremos un <mark>trait</mark> con la descripción general de nuestro <mark>struct</mark> para la base de datos, y que le pasaremos al caso de uso. Así, cualquier repositorio que queramos utilizar con nuestro caso de uso tendrá que cumplir con el <mark>trait</mark>, desacoplando el caso del uso del repositorio y haciendo más fácil cambiar de PostgreSQL a otro tipo de base de datos en el futuro.</li></ul><img src="https://www.api.antoniodiaz.me/media/files/e4e2bb46-c210-4a47-9e84-f45c789fcec1/articles/original/a95d136e-5833-4f39-82f9-8e9835da21d8.png" data-ratio="0.44" /><figcaption>Estructura de nuestra aplicación</figcaption><p>Ahora que nos hemos hecho una idea de cómo deberíaser nuestra aplicación, vamos a implementarla. Omitiremos alguna línea de código necesaria para que nuestro programa complile, pero el ejemplo completo se puede ver en el <a href="https://play.rust-lang.org/?version=stable&amp;mode=debug&amp;edition=2021&amp;gist=22e033ec941fbc7cab591ce88b56c9f0" target="_blank">playground de Rust</a>.</p><p>Primero, vamos a crear nuestra entidad o modelo: un usuario <mark>User</mark> con un campo <mark>id</mark>:</p><pre><code>language-rust<br />⁠<br />⁠pub struct User {<br />  pub id: i8,<br />⁠}</code></pre><figcaption> Struct para nuestro modelo <mark>User</mark></figcaption><p>Ahora que tenemos nuestro modelo principal —un <mark>struct</mark>—, la siguiente cuestión es qué queremos hacer con él. En otras palabras: queremos definir el caso de uso para recoger la lista de usuarios en sentido inverso. Sabemos que este caso de uso tendrá que tener una instancia del repositorio para recoger los datos de la base de datos, pero no sabemos todavía el <em>tipo</em> del repositorio: esto es una tarea para un tipo genérico:</p><pre><code>language-rust<br />⁠<br />⁠struct GetUsersRevertedUseCase&lt;T&gt; {<br />  repository: T,<br />⁠}</code></pre><figcaption> Struct para nuestro caso de uso <mark>GetUsersRevertedUseCase</mark></figcaption><p>Nuestro caso de uso —también un <mark>struct</mark>— infiere el tipo del repositorio cuando se la pasamos al instanciarlo. Pero un <mark>struct</mark> no define acciones, y nuestro caso de uso precisamente tiene que realizar las acciones definidas por la lógica de negocio de nuestro programa. Como decíamos anteriormente, esto lo haremos implementando los métodos necesarios con el término <mark>impl</mark>:</p><pre><code>language-rust<br />⁠<br />⁠impl&lt;T: Repository&gt; GetUsersRevertedUseCase&lt;T&gt; {<br />  fn new(repository: T) -&gt; GetUsersRevertedUseCase&lt;T&gt; {<br />    GetUsersRevertedUseCase { repository }<br />  }<br />⁠}</code></pre><figcaption> Implementando <mark>new</mark> para nuestro caso de uso</figcaption><p>Digno de mención aquí es la sintaxis <mark>&lt;T: Repository&gt;</mark>. Con esto le estamos diciendo al compliador que <mark>T</mark> puede ser cualquier <mark>struct</mark> implementando el <mark>trait</mark>&nbsp;<mark>Repository</mark>, devolviendo un error si pasamos cualquier otro tipo de instancia. Además, hemos añadido un método <mark>new</mark>  que devuelve una instancia de sí mismo pero con la instancia del repositorio en su interior. Así que ya tenemos nuestro caso de uso con la capacidad de recibir un repositorio, siempre que este cumpla con los requerimientos de un determinado <mark>trait</mark>, el cual tenemos pendiente.</p><p>Veamos cómo podemos definir el tipo de repositorio que necesitamos. Sabemos que tiene que realizar una acción asíncrona —recoger datos de una base de datos—, y que tiene que devolver los datos que ha recibido —una lista de usuarios—. Podemos escribir un <mark>trait</mark> con estas especificaciones de la siguiente manera:</p><pre><code>language-rust<br />⁠<br />⁠trait Repository {<br />  async fn get_users(&amp;self) -&gt; Result&lt;Vec&lt;User&gt;, String&gt;;<br />⁠}</code></pre><figcaption> Trait para nuestros repositorios</figcaption><p>Hay algunas cosas nuevas aquí: primero, el término <mark>async</mark>, el cual hace que nuestra fución devuelva un tipo <mark>Result</mark>. Este tipo es una de las particularidades de Rust: representa el resultado de una acción que puede tener éxito —<mark>Ok(T)</mark>— o sufrir un error —<mark>Err(K)</mark>—. En nuestro caso, si la acción definida por nuestro método tiene éxito, tendrá como resultado <mark>Ok&lt;Vec&lt;User&gt;&gt;</mark>, esto es, una lista de ususarios <mark>User</mark>; por el contrario, si nuestra acción fracasa, devolverá <mark>Err(String)</mark>. Para no liarnos mucho no vamos a implementar ningún error, pero el tipo <mark>Result</mark> nos obliga a tener esta posibilidad en consideración, puesto que estamos escribiendo código asíncrono. Esto es una de las ventajas de Rust: no nos deja ser perezosos con los tipos.</p><p>Ahora podemos pensar en cómo podemos lanzar nuestro caso de uso, para lo cual podemos añadirle un método <mark>execute</mark>.</p><pre><code>language-rust<br />⁠<br />⁠impl&lt;T: Repository&gt; GetUsersRevertedUseCase&lt;T&gt; {<br />  […]<br />  async fn execute(&amp;self) -&gt; Result&lt;Vec&lt;User&gt;, String&gt; {<br />    let users = self.repository.get_users().await.unwrap();<br />    let reverted_users: Vec&lt;User&gt; = users.into_iter().rev().collect();<br />    Ok(reverted_users)<br />  }<br />⁠}</code></pre><figcaption> Implementando <mark>execute</mark> para nuestro caso de uso</figcaption><p>Ah, el término <mark>async</mark> otra vez, esta vez para esperar a que la acción de nuestro repositorio termine. También está el tipo <mark>Result</mark>, esto empieza a ser familiar.</p><p>Como nuestro repositorio está devolviendo un tipo <mark>Result</mark> —<mark>Ok&lt;T&gt;</mark> o <mark>Err&lt;K&gt;</mark>—, necesitamos extraer el valor —la lista de usuarios— de nuestro <mark>Ok&lt;T&gt;</mark> para trabajar con ella. Esto lo hacemos con la función <mark>unwrap()</mark>, que nos devuelve el valor <mark>T</mark> —nuestra lista de usuarios— si estamos en un <mark>Ok</mark>, o devolviendo un error si fracasa.</p><p>Además tenemos algunas funciones operando sobre nuestra lista de usuarios para invertirla: <mark>into_iter</mark>, que convierte un vector en una lista de elementos que podamos iterar; <mark>rev</mark>, que invierte una lista de elementos iterable, y <mark>collect</mark>, que convierte una lista iterable de nuevo en un vector. Estas funcionalidades pertenecen todas al tipo <mark>Vec</mark>, que está integrado en Rust para manipular vectores al estilo funcional.</p><p>Vale, ahora ya tenemos nuestro modelo, nuestro caso de uso, y un trait para cualquier repositorio que queramos pasarle. El siguiente paso será crear nuestro repositorio para el tipo concreto de base de datos que queramos utilizar: PostgreSQL:</p><pre><code>language-rust<br />⁠<br />⁠impl Repository for PostgreSQLRepository {<br />  async fn get_users(&amp;self) -&gt; Result&lt;Vec&lt;User&gt;, String&gt; {<br />⁠    let duration_secs = Duration::from_secs(2);<br />⁠    sleep(duration_secs);<br />⁠    let user_1 = User { id: 1 };<br />⁠    let user_2 = User { id: 2 };<br />⁠    Ok(vec![user_1, user_2])<br />⁠  }<br />⁠}</code></pre><figcaption> Struct <mark>PostgreSQLRepository</mark></figcaption><p>Como esto es sólo una demostración simplemente vamos a simular que vamos a recoger datos de manera asíncrona de nuestra supuesta base de datos. Para ello usaremos dos métodos de la librería estándar: <mark>sleep</mark> y <mark>Duration</mark>. <mark>Duration</mark> devuelve un tipo específico de tiempo, que le podremos pasar a otros métodos, como por ejemplo <mark>sleep</mark>. Y <mark>sleep</mark> simplemente detendrá la ejecución del programa por el tiempo que le hayamos pasado. En nuestro caso estamos deteniendo nuestro programa por dos segundos, entonces instanciamos dos usuarios, y finalmente estamos devolviendo un <mark>Vector</mark> con ellos.</p><p>Ahora disponemos ya de todos los elementos necesarios para nuestro programa: el modelo <mark>User</mark>, el caso de uso <mark>GetUsersRevertedUseCase</mark>, el <mark>trait</mark>&nbsp;<mark>Repository</mark> y el <mark>struct</mark>&nbsp;<mark>PostgreSQLRepository</mark> que lo implementa. Sólo nos falta crear un punto de entrada para nuestro programa y hacer uso de ellos.</p><pre><code>language-rust<br />⁠<br />⁠#[tokio::main]<br />⁠async fn main() {<br />⁠  let repository = PostgreSQLRepository;<br />⁠  let use_case = GetUsersRevertedUseCase::new(repository);<br />⁠  let users = use_case.execute().await.unwrap();<br />⁠  println!(&quot;{:#?}&quot;, users);<br />⁠}</code></pre><figcaption>Punto de entrada de nuestro programa</figcaption><p>Como queremos ejecutar código asíncrono necesitaremos traer un tiempo de ejecución que nos lo facilite: vamos a usar Tokio, así que podemos llamar al macro <mark>#[tokio:main]</mark>. </p><p>Después no tenemos más que crear una instancia de nuestro repositorio, construir nuestro caso de uso pasándole la instancia que acabamos de crear, y ejecutarla, esperándola con <mark>await</mark>. El resultado será una lista de usuarios en sentido inverso.</p><pre><code>language-rust<br />⁠<br />⁠[<br />⁠  User {<br />⁠    id: 2,<br />⁠  },<br />⁠  User {<br />⁠    id: 1,<br />⁠  },<br />⁠]</code></pre><figcaption>Resultado: lista de usuarios en sentido inverso</figcaption><p>Podemos ver este código completo en el <a href="https://play.rust-lang.org/?version=stable&amp;mode=debug&amp;edition=2021&amp;gist=22e033ec941fbc7cab591ce88b56c9f0" target="_blank">playground de Rust</a>, donde podemos compilarlo y ejecutarlo.</p><h1>Conclusión</h1><p>Rust no es un lenguaje sencillo. Exige estudio y bastante paciencia para entender su sintaxis y las funcionalidades que nos ofrece. Además es muy joven, y su ecositema está en constante evolución. Pero promete.</p><p>Es en tareas que requieren eficiencia donde el lenguaje encaja perfectamente. Para desarrollo web en términos generales, donde la velocidad de prototipado y se requieren dinámicas de trabajo <em>agile</em> son importantes, lenguajes interpretados como Python o TypeScript encajan mejor. Pero cuando pérdidas de memoria puedan provocar repuntes o problemas de eficiencia en uso de servidores, Rust es una opción mucho mejor.</p><p>La aceptación actual de Rust en la industria es muy limitada, pero la Rust Foundatio es soportada por Google, Microsoft, AWS, Huawei y Mozilla; además hay una clara demanda para lenguajes enfocados a eficiencia, y probablemente veremos cómo logra introducirse en los próximos años. </p><p>En cualquier caso hay numerosas dificultades: las empresas que dan soporte a Rust intentarán <a href="https://news.ycombinator.com/item?id=28513130" target="_blank">influenciar sobre su desarrollo</a>&nbsp;<a href="https://news.ycombinator.com/item?id=28513130" target="_blank">&nbsp;</a>para que evolucione según sus necesidades particulares, y hay claros <a href="https://blog.rust-lang.org/inside-rust/2021/11/25/in-response-to-the-moderation-team-resignation.html" target="_blank">desacuerdos</a> entre el equipo interno. Pero el lenguaje encaja tan bien en las tareas que hemos mencionado que sería interesante seguir su evolución e integración en la industria.</p><p>Para concluir: Rust es un lenguaje complejo, pero es posible y claramente deseable usar Rust en contextos específicos donde performance y seguridad es un requerimiento.</p></p>]]></description></item><item><title>Algunas notas sobre el ordenamiento por mezcla</title><pubDate>Tue, 20 Jan 1970 00:00:00 GMT</pubDate><guid isPermaLink="true">https://www.antoniodiaz.me/es/blog/3</guid><description><![CDATA[<p><p>Algunas notas acerca del algoritmo de Ordenamiento por Mezcla.</p><h2>Descripción</h2><p>Algoritmo recursivo fundamentado en la estrategia «divide y vencerás». Dos componentes principales:</p><ul><li>Una función para separar listas recursivamente hasta que recibe un único elemento.</li><li>Una función que combina dos elementos ya ordenados en una lista ordenada.</li></ul><h2>Complejidad temporal</h2><img src="https://www.api.antoniodiaz.me/media/files/e4e2bb46-c210-4a47-9e84-f45c789fcec1/articles/original/fbdd6d86-3312-4384-945a-de4fa02ecdda.png" data-ratio="0.55" /><figcaption>Complejidad temporal para Ordenamiento por Mezcla</figcaption><h2>Pseudocódigo</h2><div><div class="math">Input: A = [A_1, A_2, …, A_n];\\Output: B = [B_1, B_2, …, B_n];\\[10pt]⁠function \enspace MergeSort(A)⁠\\\quad n \leftarrow length(A);⁠\\\quad if \space n \leqslant then:⁠\\\quad\quad return \space A;\\⁠\quad else:⁠\\\quad\quad l \leftarrow [A_1, …, A_{n/2}];⁠⁠\\\quad\quad r \leftarrow [A_{(n/2)+1}, …, A_n];⁠\\\quad\quad L \leftarrow MergeSort(l);⁠⁠\\\quad\quad R \leftarrow MergeSort(r);⁠⁠⁠\\\quad\quad B \leftarrow Merge(L, R);⁠\\\quad\quad return \space B;⁠⁠\\\quad end \space if;⁠⁠\\end \space function;\\[10pt]⁠function Merge(L, R)\\⁠\quad n \leftarrow length(L) + length(R);\\⁠\quad B \leftarrow [\space];\\⁠\quad i \leftarrow 0;\\⁠\quad j \leftarrow 0;\\⁠\quad for \space k \leftarrow 0 \space to \space n \space do:\\⁠\qquad if \space i \leqslant length(L) \space and \space j \leqslant length(R) \space then:\\⁠\qquad\quad if \space L[i] \leqslant R[j] \space then:\\⁠\qquad\qquad B[k] \leftarrow L[i];\\⁠\qquad\qquad i \leftarrow i + 1;\\⁠⁠\qquad\quad else \space if \space L[i] \text{\textgreater} R[j] \space then:\\⁠\qquad\qquad B[k] \leftarrow R[j];\\⁠\qquad\qquad j \leftarrow j + 1;\\⁠⁠\qquad\quad end \space if;\\⁠⁠\qquad else \space if \space i + 1 \leqslant length(L) \space then:\\⁠⁠\qquad\quad B[k] \leftarrow L[i];\\⁠\qquad\quad i \leftarrow i + 1;\\⁠⁠⁠\qquad else \space if \space j + 1 \leqslant length(R) \space then:\\⁠⁠\qquad\quad B[k] \leftarrow R[j];\\⁠\qquad\quad j \leftarrow j + 1;\\⁠⁠⁠⁠\qquad end \space if;\\⁠⁠⁠⁠⁠\quad end \space for;\\⁠\quad return \space B;\\end \space function;⁠</div></div><p>&nbsp;</p><h2>Implementación en Rust</h2><pre><code>language-rust<br />⁠<br />⁠pub fn merge_sorted_arrays(left: Vec&lt;i32&gt;, right: Vec&lt;i32&gt;) -&gt; Vec&lt;i32&gt; {<br />⁠  let left_length = left.len();<br />⁠  let right_length = right.len();<br />⁠<br />⁠  // El array resultante tendrá el tamaño de los dos arrays que se pasen<br />⁠  let mut sorted_items = vec![0; left_length + right_length];<br />⁠<br />⁠  let mut i = 0;<br />⁠  let mut j = 0;<br />⁠<br />⁠  // Iterar sobre el array que vamos a rellenarcon elementos de los arrays <br />⁠  // izquierdo o derecho<br />⁠  for k in 0..sorted_items.len() {<br />⁠    // Si ninguno de los punteros &quot;i&quot; ni &quot;j&quot; han llegado a su límite<br />⁠    if i + 1 &lt;= left.len() &amp;&amp; j + 1 &lt;= right.len() {<br />⁠      // Decidir entre el elemento actual del array izquierdo <br />⁠      // y el elemento actual del array derecho cuál de los dos será <br />⁠      // el siguiente para el array final<br />⁠      if left[i] &lt;= right[j] {<br />⁠        sorted_items[k] = left[i];<br />⁠        i = i + 1;<br />⁠      } else if left[i] &gt; right[j] {<br />⁠        sorted_items[k] = right[j];<br />⁠        j = j + 1;<br />⁠      }<br />⁠    } else if i + 1 &lt;= left.len() {<br />⁠      // Si sólo el puntero &quot;i&quot; no ha alcanzado el límite <br />⁠      // —y por tanto &quot;j&quot; lo ha hecho—<br />⁠      sorted_items[k] = left[i];<br />⁠      i = i + 1;<br />⁠    } else if j + 1 &lt;= right.len() {<br />⁠      // Si sólo el puntero &quot;j&quot; no ha alcanzado el límite <br />⁠      // —y por tanto &quot;i&quot; lo ha hecho—<br />⁠      sorted_items[k] = right[j];<br />⁠      j = j + 1;<br />⁠    }<br />⁠  }<br />⁠<br />⁠  sorted_items<br />⁠}<br />⁠<br />⁠pub fn sort_array(unsorted_array: &amp;Vec&lt;i32&gt;) -&gt; Vec&lt;i32&gt; {<br />⁠  let n = unsorted_array.len();<br />⁠  if n &lt;= 1 {<br />⁠    // Caso base: si el input es de longitud 1, <br />⁠    // entonces está ya ordenado: devolverlo<br />⁠    let sorted_array = unsorted_array.to_vec();<br />⁠    sorted_array<br />⁠  } else {<br />⁠    // Si el input es de longitudo dos o mayor<br />⁠    // 1. Separarlo<br />⁠    let (left, right) = unsorted_array.split_at(n / 2);<br />⁠    let left_vec = left.to_vec();<br />⁠    let right_vec = right.to_vec();<br />⁠<br />⁠    // 2. Ordenarlo recursivamente<br />⁠    let left_sorted = sort_array(&amp;left_vec);<br />⁠    let right_sorted = sort_array(&amp;right_vec);<br />⁠    let sorted_array_1 = merge_sorted_arrays(left_sorted, right_sorted);<br />⁠    sorted_array_1<br />⁠  }<br />⁠}<br />⁠<br />⁠fn main() {<br />⁠  let unsorted_array: Vec&lt;i32&gt; = vec![2, 1, 4, 3];<br />⁠  let sorted_array = sort_array(&amp;unsorted_array);<br />⁠  println!(&quot;unsorted_array: {:?}&quot;, unsorted_array);<br />⁠  println!(&quot;sorted_array: {:?}&quot;, sorted_array);<br />⁠}<br />⁠<br />⁠#[cfg(test)]<br />⁠mod tests {<br />⁠  use super::*;<br />⁠<br />⁠  #[test]<br />⁠  fn works_with_sorted_arrays() {<br />⁠    let unsorted_array = vec![1, 2];<br />⁠    let sorted_array = sort_array(&amp;unsorted_array);<br />⁠    let expected_result = vec![1, 2];<br />⁠    assert_eq!(expected_result, sorted_array);<br />⁠  }<br />⁠<br />⁠  #[test]<br />⁠  fn works_with_simple_unsorted_array() {<br />⁠    let unsorted_array = vec![2, 1];<br />⁠    let sorted_array = sort_array(&amp;unsorted_array);<br />⁠    let expected_result = vec![1, 2];<br />⁠    assert_eq!(expected_result, sorted_array);<br />⁠  }<br />⁠<br />⁠  #[test]<br />⁠  fn works_with_simple_uneven_lenght_arrays() {<br />⁠    let unsorted_array = vec![2, 1, 3];<br />⁠    let sorted_array = sort_array(&amp;unsorted_array);<br />⁠    let expected_result = vec![1, 2, 3];<br />⁠    assert_eq!(expected_result, sorted_array);<br />⁠  }<br />⁠<br />⁠  #[test]<br />⁠  fn it_works_with_sorted_arrays() {<br />⁠    let left = vec![1, 2];<br />⁠    let right = vec![3, 4];<br />⁠    let merged = merge_sorted_arrays(left, right);<br />⁠    let expected_result = vec![1, 2, 3, 4];<br />⁠    assert_eq!(expected_result, merged);<br />⁠  }<br />⁠<br />⁠  #[test]<br />⁠  fn it_works_with_splits() {<br />⁠    let left = vec![3, 4];<br />⁠    let right = vec![1, 2];<br />⁠    let merged = merge_sorted_arrays(left, right);<br />⁠    let expected_result = vec![1, 2, 3, 4];<br />⁠    assert_eq!(expected_result, merged);<br />⁠  }<br />⁠<br />⁠  #[test]<br />⁠  fn it_works_with_uneven_length_lists() {<br />⁠    let left = vec![3, 4, 5];<br />⁠    let right = vec![1, 2];<br />⁠    let merged = merge_sorted_arrays(left, right);<br />⁠    let expected_result = vec![1, 2, 3, 4, 5];<br />⁠    assert_eq!(expected_result, merged);<br />⁠  }<br />⁠<br />⁠  #[test]<br />⁠  fn it_works_with_single_item_lists() {<br />⁠    let left = vec![3];<br />⁠    let right = vec![1];<br />⁠    let merged = merge_sorted_arrays(left, right);<br />⁠    let expected_result = vec![1, 3];<br />⁠    assert_eq!(expected_result, merged);<br />⁠  }<br />⁠}</code></pre><figcaption>Implementación en Rust, ejecutame en el <a href="https://play.rust-lang.org/?version=stable&amp;mode=debug&amp;edition=2021&amp;gist=1aee4409d31789101bcaba255c7b0d11" target="_blank">Playground </a>de Rust</figcaption></p>]]></description></item><item><title>Repasando las closuras en Rust</title><pubDate>Tue, 20 Jan 1970 00:00:00 GMT</pubDate><guid isPermaLink="true">https://www.antoniodiaz.me/es/blog/4</guid><description><![CDATA[<p><p>When I went through the exercises on <a href="https://doc.rust-lang.org/book/" target="_blank">The Rust Programming Language</a> book I remember finding the <a href="https://doc.rust-lang.org/book/ch13-01-closures.html#storing-closures-using-generic-parameters-and-the-fn-traits" target="_blank">example about closures</a> interesting. The provided code was just about implementing a naive cache system to avoid repeated expensive calculations for previously passed inputs. It looked like this:</p><pre><code>language-rust<br />⁠<br />// Struct accepting a Generic, which has <br />⁠// to be a function with an u32 as parameter<br />⁠// and returning an u32 value<br />⁠pub struct Cacher&lt;T: Fn(u32) -&gt; u32&gt; {<br />⁠  pub calculation: T,<br />⁠  pub value: Option&lt;u32&gt;,<br />⁠}<br />⁠<br />⁠// Implementation for Cacher, where we add our behaviour<br />⁠impl&lt;T: Fn(u32) -&gt; u32&gt; Cacher&lt;T&gt; {<br />⁠  // Instantiate the struct with a function, that will be<br />⁠  // stored within the struct scope as a closure<br />⁠  pub fn new(calculation: T) -&gt; Cacher&lt;T&gt; {<br />⁠    Cacher {<br />⁠      calculation,<br />⁠      value: None,<br />⁠    }<br />⁠  }<br />⁠<br />⁠  // Getter to retrieve the current value<br />⁠  pub fn value(&amp;mut self, input: u32) -&gt; u32 {<br />⁠    match self.value {<br />⁠      Some(value) =&gt; value,<br />⁠      None =&gt; {<br />⁠        let value = (self.calculation)(input);<br />⁠        self.value = Some(value);<br /><br />⁠⁠        value<br />⁠      }<br />⁠    }<br />⁠  }<br />⁠}<br />⁠</code></pre><figcaption>Cacher implementation with a closure and generics</figcaption><p>Easy enough. If <mark>value</mark> is empty the cacher will calculate the result and return it; otherwise, it will return the existing value. But there is a catch: if we run the <mark>Cacher::value()</mark> method twice with different values, it will return the first one that was stored, which we can demonstrate with this test:</p><pre><code>language-rust<br />⁠<br />#[cfg(test)]<br />⁠mod tests {<br />⁠  use super::*;<br />⁠  use ::std::thread;<br />⁠  use ::std::time::Duration;<br />⁠<br />⁠  #[test]<br />⁠  fn call_with_different_values() {<br />⁠    fn my_function(num: u32) -&gt; u32 {<br />⁠   ⁠   num<br />⁠    }<br />⁠<br />⁠    let mut cacher = Cacher::new(my_function);<br />⁠<br />⁠    let value_1 = cacher.value(1);<br />⁠    let value_2 = cacher.value(2);<br />⁠<br />⁠    assert_eq!(value_1, 1);<br />⁠    assert_eq!(value_2, 2);<br />⁠  }<br />⁠}</code></pre><figcaption>Failing test for our cacher: run it here in the <a href="https://play.rust-lang.org/?version=stable&amp;mode=debug&amp;edition=2021&amp;gist=1d80b54ca923a8c020d9ef79473b2b21" target="_blank">playground</a>&nbsp;</figcaption><p>The issue is that we are storing the value within <mark>Cacher.value</mark> directly, so once it is calculated the same value will be returned the rest of the times. What we need is a structure that can store the resulting values in relation to the inputs, and one with fast search and insert times. We need a Hashmap.</p><p>So instead of using <mark>value</mark> as an <mark>Option&lt;u32&gt;</mark> to store a single value, we can use <mark>values</mark> with an <mark>std::collections::HashMap</mark>, as follows:</p><pre><code>language-rust<br />⁠<br />use std::collections::HashMap; <br />⁠<br />⁠struct Cacher&lt;T: Fn(u32) -&gt; u32&gt; {<br />⁠  calculation: T,<br />⁠  values: HashMap&lt;u32, u32&gt;,<br />⁠}</code></pre><p>Our hashmap will have <mark>u32</mark> keys with <mark>u32</mark> values. We can now proceed to instantiate it on our constructor:</p><pre><code>language-rust<br />⁠<br />fn new(calculation: T) -&gt; Cacher&lt;T&gt; {<br />⁠  Cacher {<br />⁠    calculation,<br />⁠    values: HashMap::new(),<br />⁠  }<br />⁠}</code></pre><p>And finally create or getter/setter method with the match pattern that will decide what to do:</p><pre><code>language-rust<br />⁠<br />fn value(&amp;mut self, input: u32) -&gt; u32 {<br />⁠  match self.values.get(&amp;input) {<br />⁠    Some(value) =&gt; *value,<br />⁠    None =&gt; {<br />⁠      let value = (self.calculation)(input);<br />⁠      self.values.insert(input, value);<br />⁠<br />⁠      value<br />⁠    }<br />⁠  }<br />⁠}</code></pre><p>So if the input already exists in <mark>self.values</mark> —<mark>self.values.get(&amp;input)</mark>—, retrieve it; otherwise, set into <mark>self.values</mark> both the input and value as key/value pair. Notice that when a given value exists —<mark>Some(value)</mark>—, we retrieve it as <mark>*value</mark>: that&#39;s because what we store is the reference <mark>&amp;input</mark>, so we need to dereference it to retrieve its actual value. If we remove the dereference we will get the corresponding error notifying that Rust <mark>expected `u32`, found `&amp;u32`</mark>.</p><pre><code>language-none<br />⁠<br />⁠⣿ Standard Error<br />⁠<br />⁠Compiling playground v0.0.1 (/playground)<br />⁠error[E0308]: mismatched types<br />⁠  --&gt; src/lib.rs:18:28<br />⁠   |<br />⁠16 |     pub fn value(&amp;mut self, input: u32) -&gt; u32 {<br />⁠   |                    --- expected `u32` because of return type<br />⁠17 |         match self.values.get(&amp;input) {<br />⁠18 |             Some(value) =&gt; value,<br />⁠   |                            ^^^^^ expected `u32`, found `&amp;u32`<br />⁠   |<br />⁠help: consider dereferencing the borrow<br />⁠   |<br />⁠18 |             Some(value) =&gt; *value,<br />⁠   |                            +</code></pre><p>Finally note the parenthesis on <mark>(self.calculation)(input)</mark>. They seem unnecesary, but actually they are. Lets break things first and remove them:</p><pre><code>language-none<br />⁠<br />⣿ Standard Error<br />⁠<br />Compiling playground v0.0.1 (/playground)<br />⁠error[E0599]: no method named `calculation` found for mutable reference <br />⁠`&amp;mut Cacher&lt;T&gt;` in the current scope<br />⁠  --&gt; src/lib.rs:20:34<br />⁠   |<br />⁠20 |                 let value = self.calculation(input);<br />⁠   |                                  ^^^^^^^^^^^ field, not a method<br />⁠   |<br />⁠help: to call the function stored in `calculation`, surround the field access <br />⁠with parentheses<br />⁠   |<br />⁠20 |                 let value = (self.calculation)(input);<br />⁠   |                             +                +</code></pre><p>As previously, Rust informs us both about the error and possible fix. It is intelligent enough to identify that we are trying to call the function we stored as a closure, thus the need of the parenthesis. If we restore them and <a href="https://play.rust-lang.org/?version=stable&amp;mode=debug&amp;edition=2021&amp;gist=d5dcba3c9d3eca09294cbdea3c108a9b" target="_blank">try to run our test</a> again, this what we get:</p><pre><code>language-none<br />⁠<br />running 1 test<br />⁠test tests::call_with_different_values ... ok</code></pre><p>Fantastic, it is passing \(^O^)/. <br />⁠<br />⁠Of course this is something really simple, but I like the example because it has a reference and a dereference, a structure from <mark>std</mark> , a closure, a generic with a bound type and a <mark>match</mark> pattern. You can&#39;t ask for more!<br />⁠</p></p>]]></description></item><item><title>Inversión de dependencias en Rust</title><pubDate>Tue, 20 Jan 1970 00:00:00 GMT</pubDate><guid isPermaLink="true">https://www.antoniodiaz.me/es/blog/6</guid><description><![CDATA[<p><p>Dado un struct <mark>MyStruct</mark>, contenido en el módulo <mark>my_struct</mark>, y un trait <mark>Feature</mark> contenido en el módulo <mark>my_feature</mark>, se requiere que <mark>MyStruct</mark> pueda recibir diferentes implementaciones del trait <mark>Feature</mark> sin depender directamente de ellas.</p><p>Nuestro objetivo será poder indicarle al struct <mark>MyStruct</mark> que puede recibir mediante el método <mark>new()</mark> cualquier struct que implemente <mark>Feature</mark>.<br />En otros lenguajes —mayormente aquellos con tipado estructural—, la huella de <mark>new()</mark> tendría esta forma:</p><pre><code>language-rust<br />⁠<br />⁠fn new(feature: Feature) -&gt; MyStruct;</code></pre><p>Esto no funcionará en Rust, pues el compilador no podrá inferir el tamaño del tipo <mark>Feature</mark> en tiempo de compilación.</p><p>Sin embargo, esto puede ser logrado mediante dos estrategia diferentes: </p><ul><li>Usando genéricos</li><li>Usando<em> trait objects</em></li></ul><h2>Inversión de dependencias con genéricos</h2><p>Es posible pasar el tipo concreto de la implementación de <mark>Feature</mark> como un genérico. </p><p>Un primer paso sería definir el trait <mark>Feature</mark> en el módulo <mark>feature</mark>:</p><pre><code>⁠language-rust<br />⁠<br />mod feature {<br />⁠  pub trait Feature {<br />⁠    fn action(&amp;self) -&gt; String;<br />⁠  }<br />⁠}</code></pre><figcaption>Módulo <mark>Feature</mark>&nbsp;</figcaption><p>Dentro del módulo <mark>my_struct</mark> se encontraría el struct <mark>MyStruct</mark>, que recibiría como genérico un tipo que implementa <mark>Feature</mark>, lo que podemos ver en <mark>impl &lt;T: Feature&gt;...</mark>.</p><p><mark>MyStruct</mark> recibirá entonces <mark>T</mark> como genérico, con lo que será posible tipar el parámetro <mark>feature</mark> como <mark>T</mark>.</p><pre><code>language-rust<br />⁠<br />⁠mod my_struct {<br />⁠  use super::feature::Feature;<br />⁠<br />⁠  pub struct MyStruct&lt;T&gt; {<br />⁠    feature: T,<br />⁠  }<br />⁠<br />⁠  impl&lt;T: Feature&gt; MyStruct&lt;T&gt; {<br />⁠    pub fn new(feature: T) -&gt; MyStruct&lt;T&gt; {<br />⁠      MyStruct { feature }<br />⁠    }<br />⁠<br />⁠    pub fn action(&amp;self) -&gt; String {<br />⁠      self.feature.action()<br />⁠    }<br />⁠  }<br />⁠}</code></pre><figcaption>Módulo <mark>MyStruct</mark>&nbsp;</figcaption><p>Las diferentes implementaciones de <mark>Feature</mark> se encontrarían en módulos separados:</p><pre><code>language-rust<br />⁠<br />mod feature_implementation01 {<br />⁠  use super::feature::Feature;<br />⁠<br />⁠  pub struct FeatureImplementation01;<br />⁠<br />⁠  impl Feature for FeatureImplementation01 {<br />⁠    fn action(&amp;self) -&gt; String {<br />⁠      String::from(&quot;Feature 01 - with generics&quot;)<br />⁠    }<br />⁠  }<br />⁠}<br />⁠</code></pre><figcaption>Primera implementación de <mark>Feature</mark></figcaption><pre><code><br />language-rust<br />⁠<br />⁠mod feature_implementation02 {<br />⁠  use super::feature::Feature;<br />⁠<br />⁠  pub struct FeatureImplementation02;<br />⁠<br />⁠  impl Feature for FeatureImplementation02 {<br />⁠    fn action(&amp;self) -&gt; String {<br />⁠      String::from(&quot;Feature 02 - with generics&quot;)<br />⁠    }<br />⁠  }<br />⁠}</code></pre><figcaption>Segunda implementación de <mark>Feature</mark></figcaption><p>Finalmente, el código cliente consumiría esta lógica:</p><pre><code><br />language-rust<br />⁠<br />⁠pub fn main() {<br />⁠  use feature_implementation01::FeatureImplementation01;<br />⁠  use feature_implementation02::FeatureImplementation02;<br />⁠  use my_struct::MyStruct;<br />⁠<br />⁠  let feature_implementation_01_instance = FeatureImplementation01;<br />⁠  // If required, it is possible to pass the type of <br />⁠  // the implementation into the generic with turbofish syntax<br />⁠  let my_struct = <br />⁠    MyStruct::&lt;FeatureImplementation01&gt;::new(<br />⁠      feature_implementation_01_instance<br />⁠    );<br />⁠  let result = my_struct.action();<br />⁠  println!(&quot;{:#?}&quot;, result);<br />⁠<br />⁠  ⁠// If required, it is possible to pass the type of <br />⁠  // the implementation into the generic with turbofish syntax<br />⁠  let feature_implementation_02_instance = FeatureImplementation02;<br />⁠  let my_struct = <br />⁠    MyStruct::&lt;FeatureImplementation01&gt;::new(<br />⁠      feature_implementation_02_instance<br />⁠    );<br />⁠  let result = my_struct.action();<br />⁠  println!(&quot;{:#?}&quot;, result);<br />⁠}</code></pre><figcaption>Ejecútame en el <a href="https://play.rust-lang.org/?version=stable&amp;mode=debug&amp;edition=2021&amp;gist=33466215ba9d6b644ec56e258922d756" target="_blank"><strong>playground de Rust</strong></a>&nbsp;</figcaption><p>Es importante notar que cuando<mark>MyStruct::new</mark> es llamado, está recibiendo el tipo del struct implementando <mark>Feature</mark> mediante la sintaxis Turbofish.</p><pre><code>language-rust<br />⁠<br />let my_struct = <br />⁠    MyStruct::&lt;FeatureImplementation01&gt;::new(<br />⁠      feature_implementation_01_instance<br />⁠    );</code></pre><figcaption>Instanciando <mark>MyStruct</mark> e injectando una instancia implementando <mark>Feature</mark></figcaption><p>El compilador podría ser capaz de inferir este tipo en numerosas ocasiones: ésta es una de ellas, si se elimina el código compilará sin problema. Sin embargo no siempre es éste el caso, y el desarrollador estará forzado a marcarlo manualmente en numerosos casos.</p><h2>Con <em>trait objects</em></h2><p>Rust expone numerosos<em> smart pointers</em> para estructuras con tamaño dinámico: la elección más obvia aquí sería <mark>Box</mark>.<br />Los cambios requeridos para conseguir compilar tendrán lugar únicamente en el módulo <mark>my_struct</mark>, así como en el código cliente.</p><p>En primer lugar necesitamos eliminar los genéricos de <mark>MyStruct</mark>; el problema entonces será que <mark>feature</mark> tiene el tipo del trait <mark>Feature</mark>, cuyo tamaño es desconocido en tiempo de compilación, puesto que los structs implementando este trait podrían tener tamaños diversos. </p><p>Se puede solu⁠cionar este problema envolviendo <mark>Feature</mark> dentro de un <mark>Box&lt;dyn Feature&gt;</mark>⁠, diciéndole así al compilador que envié este struct al heap, siendo así redimensionado dinámicamente.</p><p>Para ser más explícito con ello Rust fuerza al desarrollador a marcar con el término <mark>dyn</mark> todos aquellos objetos que usen<em>&nbsp;</em>dispatch dinámico.</p><pre><code>language-rust<br />⁠<br />mod my_struct {<br />⁠  use super::feature::Feature;<br />⁠<br />⁠  pub struct MyStruct {<br />⁠    feature: Box&lt;dyn Feature&gt;,<br />⁠  }<br />⁠<br />⁠  impl MyStruct {<br />⁠    pub fn new(feature: Box&lt;dyn Feature&gt;) -&gt; MyStruct {<br />⁠      MyStruct { feature }<br />⁠    }<br />⁠<br />⁠    pub fn action(&amp;self) -&gt; String {<br />⁠      self.feature.action()<br />⁠    }<br />⁠  }<br />⁠}</code></pre><figcaption>Tipando <mark>feature</mark> como <mark>Box&lt;dyn Feature&gt;</mark></figcaption><p>El código cliente cambiará igualmente, pues requiere que envolvamos la instancia implementando <mark>Feature</mark> dentro de una instancia de <mark>Box</mark>:</p><pre><code>language-rust<br />⁠<br />fn main() {<br />⁠  use feature_implementation01::FeatureImplementation01;<br />⁠  use feature_implementation02::FeatureImplementation02;<br />⁠  use my_struct::MyStruct;<br />⁠<br />⁠  let feature_implementation_01_instance = FeatureImplementation01;<br />⁠  let my_struct = MyStruct::new(Box::new(feature_implementation_01_instance));<br />⁠  let result = my_struct.action();<br />⁠  println!(&quot;{:#?}&quot;, result);<br />⁠<br />⁠  let feature_implementation_02_instance = FeatureImplementation02;<br />⁠  let my_struct = MyStruct::new(Box::new(feature_implementation_02_instance));<br />⁠  let result = my_struct.action();<br />⁠  println!(&quot;{:#?}&quot;, result);<br />⁠}</code></pre><figcaption>Ejecútamente en el  <a href="https://play.rust-lang.org/?version=stable&amp;mode=debug&amp;edition=2021&amp;gist=2f727ee1f529d27b3dabd51c7f849393" target="_blank"><strong>Playground de Rust</strong></a>&nbsp;</figcaption><p>De esta forma el código compila y se ejecuta sin problemas.</p><p>Otro caso que se puede encontrar es que <mark>MyStruct</mark> haga uso de un servicio <mark>MyService</mark>, que a su vez requerirá contener una instancia de <mark>Feature</mark> dentro de sí.</p><pre><code>language-rust<br />⁠<br />mod my_service {<br />⁠  use super::feature::Feature;<br />⁠<br />⁠  pub struct MyService {<br />⁠    feature: Box&lt;dyn Feature&gt;,<br />⁠  }<br />⁠<br />⁠  impl MyService {<br />⁠    pub fn new(feature: Box&lt;dyn Feature&gt;) -&gt; MyService {<br />⁠      MyService { feature }<br />⁠    }<br />⁠  }<br />⁠}</code></pre><figcaption><mark>MyService</mark>, que requiere una instancia de <mark>Feature</mark></figcaption><pre><code>language-rust<br />⁠<br />mod my_struct {<br />⁠  use super::feature::Feature;<br />⁠  use super::my_service::MyService;<br />⁠<br />⁠  pub struct MyStruct {<br />⁠    feature: Box&lt;dyn Feature&gt;,<br />⁠  }<br />⁠<br />⁠  impl MyStruct {<br />⁠    pub fn new(feature: Box&lt;dyn Feature&gt;) -&gt; MyStruct {<br />⁠      MyStruct { feature }<br />⁠    }<br />⁠<br />⁠    pub fn action(self) -&gt; String {<br />⁠      let my_service = MyService::new(self.feature);<br />⁠      println!(&quot;{}&quot;, my_service.action());<br />⁠<br />⁠      self.feature.action().to_owned()<br />⁠    }<br />⁠  }<br />⁠}</code></pre><figcaption><mark>MyStruct</mark> haciendo uso —y dependiendo de— <mark>MyService</mark></figcaption><p>Ahora el compilador devuelve error, puesto que <mark>MyService</mark> está siendo transferido a <mark>self.feature</mark>:</p><pre><code>language-rust<br />⁠<br />⁠⁠error[E0382]: borrow of moved value: `self.feature`<br />⁠  --&gt; src/with_box.rs:25:7<br />⁠   |<br />⁠21 |       let my_service = MyService::new(self.feature);<br />⁠   |                                       ------------ value moved here<br />⁠...<br />⁠25 |       self.feature.action().to_owned()<br />⁠   |       ^^^^^^^^^^^^^^^^^^^^^ value borrowed here after move</code></pre><figcaption>Error when consuming <mark>self.feature</mark></figcaption><p>Cuando pasamos <mark>self.feature</mark> a <mark>MyService</mark> aquel es consumido, y no estará disponible tras esa acción. Una solución podría ser clonarlo con <mark>clone()</mark>, pero el compilador devolverá un nuevo error:</p><pre><code>language-rust<br />⁠<br />error[E0599]: the method `clone` exists for struct <br />⁠   `Box&lt;(dyn with_box::feature::Feature + &#39;static)&gt;`, <br />⁠   but its trait bounds were not satisfied<br />⁠   --&gt; src/with_box.rs:21:52<br />⁠    |<br />⁠2   |     pub trait Feature {<br />⁠    |     -----------------<br />⁠    |     |<br />⁠    |     doesn&#39;t satisfy `dyn with_box::feature::Feature: Clone`<br />⁠    |     doesn&#39;t satisfy `dyn with_box::feature::Feature: Sized`<br />⁠...<br />⁠21  |         let my_service = MyService::new(self.feature.clone());<br />⁠    |                                                      ^^^^^ method <br />⁠                cannot be called on <br />⁠                `Box&lt;(dyn with_box::feature::Feature + &#39;static)&gt;` <br />⁠                due to unsatisfied trait bounds</code></pre><p>El trait <mark>Box</mark> no implementa <mark>Clone</mark>. Podría hacerse una implementación <em>ad hoc</em> de <mark>Clone</mark> para <mark>Box&lt;dyn Feature&gt;</mark>, pero hay una solución más sencilla para este caso: usar el puntero <mark>Rc</mark> en lugar de <mark>Box</mark>.</p><pre><code>language-rust<br />⁠<br />⁠mod my_struct {<br />⁠  use super::feature::Feature;<br />⁠  use super::my_service::MyService;<br />⁠  use std::rc::Rc;<br />⁠<br />⁠  pub struct MyStruct {<br />⁠    feature: Rc&lt;dyn Feature&gt;,<br />⁠  }<br />⁠<br />⁠  impl MyStruct {<br />⁠    pub fn new(feature: Rc&lt;dyn Feature&gt;) -&gt; MyStruct {<br />⁠      MyStruct { feature }<br />⁠    }<br />⁠<br />⁠    pub fn action(self) -&gt; String {<br />⁠      let my_service = MyService::new(self.feature.clone());<br />⁠      println!(&quot;{}&quot;, my_service.action());<br />⁠<br />⁠      self.feature.action().to_owned()<br />⁠    }<br />⁠  }<br />⁠}</code></pre><figcaption>Run in <a href="https://play.rust-lang.org/?version=stable&amp;mode=debug&amp;edition=2021&amp;gist=7674212c892bf195a5881dc401108531" target="_blank"><strong>Rust playground</strong></a>&nbsp;</figcaption><p>Como quiera que <mark>Rc</mark> implementa <mark>Clone</mark>,  puede ser llamado sobre <mark>self.feature</mark> para obtener una copia del mismo y que sea consumida por otro objeto.</p><p>Un último caso que se puede encontrar es el del código asíncrono. En esta situación <mark>Rc</mark> no será de utilidad, pues no es seguro para subprocesos ni multi-threading.</p><p>⁠Los siguientes ejemplos harán uso de <a href="https://crates.io/crates/tokio" target="_blank">Tokio</a> para el runtime asíncrono, así como de <a href="https://crates.io/crates/async-trait" target="_blank">async_trait</a> para poder crear traits con métodos también asíncronos.</p><p>Por ejemplo, si <mark>MyStruct</mark> implementase un trait <mark>MyStructTrait</mark>, el compilador advertirá de que <mark>MyStructTrait</mark> y <mark>Feature</mark> deberían implementar <mark>Send + Sync</mark> :</p><pre><code>language-rust<br />⁠<br />mod my_struct {<br />⁠  use super::feature::Feature;<br />⁠  use async_trait::async_trait;<br />⁠  use std::rc::Rc;<br />⁠<br />⁠  pub struct MyStruct {<br />⁠    feature: Rc&lt;dyn Feature&gt;,<br />⁠  }<br />⁠<br />⁠  #[async_trait]<br />⁠  pub trait MyStructTrait: Send {<br />⁠    fn new(feature: Rc&lt;dyn Feature&gt;) -&gt; Self;<br />⁠    async fn action(&amp;self) -&gt; Result&lt;String, String&gt;;<br />⁠  }<br />⁠<br />⁠  #[async_trait]<br />⁠  impl MyStructTrait for MyStruct {<br />⁠    fn new(feature: Rc&lt;dyn Feature&gt;) -&gt; Self {<br />⁠      MyStruct { feature }<br />⁠    }<br />⁠<br />⁠    async fn action(&amp;self) -&gt; Result&lt;String, String&gt; {<br />⁠      self.feature.action().await<br />⁠    }<br />⁠  }<br />⁠}</code></pre><figcaption>Código asíncrono usando <mark>Rc</mark></figcaption><pre><code>language-rust<br />⁠<br />error: future cannot be sent between threads safely<br />⁠  --&gt; src/with_arc.rs:31:54<br />⁠   |<br />⁠31 |       async fn action(&amp;self) -&gt; Result&lt;String, String&gt; {<br />⁠   |  ______________________________________________________^<br />⁠32 | |       self.feature.action().await<br />⁠33 | |     }<br />⁠   | |_____^ future created by async block is not `Send`</code></pre><figcaption>Error en código asíncrono con <mark>Rc</mark></figcaption><p>Es posible decirle al compilador que extienda <mark>Feature</mark> y <mark>MyStructTrait</mark> con <mark>Send + Sync</mark>; pero encontraremos otro obstáculo: <mark>x cannot be sent between threads safely</mark>.</p><pre><code>language-none<br />⁠<br />error[E0277]: `Rc&lt;(dyn with_arc::feature::Feature + &#39;static)&gt;` <br />⁠  cannot be sent between threads safely<br />⁠  --&gt; src/with_arc.rs:26:8<br />⁠   |<br />⁠26 |   impl MyStructTrait for MyStruct {<br />⁠   |        ^^^^^^^^^^^^^ `Rc&lt;(dyn with_arc::feature::Feature + <br />⁠                          &#39;static)&gt;<br />⁠       ` cannot be sent between threads safely</code></pre><figcaption>Error cuando implementamos <mark>Send + Sync</mark></figcaption><p>El puntero <mark>Rc</mark> es útil en código síncrono, pero no es seguro para subprocesos: para este caso Rust expone <mark>Arc</mark>, que, según la documentación: «... provides shared ownership of a value of type <mark>T</mark>, allocated in the heap. […] Unlike <mark>Rc&lt;T&gt;</mark>, <mark>Arc&lt;T&gt;</mark> uses atomic operations for its reference counting. This means that it is thread-safe.»</p><p>Usando <mark>Arc</mark> consumiremos algo más de memoria que con <mark>Rc</mark>, pero para este caso es una cantidad que fácilmente puede ser ignorada. Por otra parte tiene la ventaja de que implementa <mark>Clone</mark>, con lo que es posible realizar <mark>self.feature.clone()</mark> para pasarlo a <mark>Service</mark>.</p><pre><code>language-rust<br />⁠<br />⁠mod my_struct {<br />⁠  use super::feature::Feature;<br />⁠  use async_trait::async_trait;<br />⁠  use std::sync::Arc;<br />⁠<br />⁠  pub struct MyStruct {<br />⁠    feature: Arc&lt;dyn Feature&gt;,<br />⁠  }<br />⁠<br />⁠  #[async_trait]<br />⁠  pub trait MyStructTrait: Send + Sync {<br />⁠    fn new(feature: Arc&lt;dyn Feature&gt;) -&gt; Self;<br />⁠    async fn action(&amp;self) -&gt; Result&lt;String, String&gt;;<br />⁠  }<br />⁠<br />⁠  #[async_trait]<br />⁠  impl MyStructTrait for MyStruct {<br />⁠    fn new(feature: Arc&lt;dyn Feature&gt;) -&gt; Self {<br />⁠      MyStruct { feature }<br />⁠    }<br />⁠<br />⁠    async fn action(&amp;self) -&gt; Result&lt;String, String&gt; {<br />⁠      self.feature.action().await<br />⁠    }<br />⁠  }<br />⁠}</code></pre><figcaption>Using <mark>Arc</mark>, run in <a href="https://play.rust-lang.org/?version=stable&amp;mode=debug&amp;edition=2021&amp;gist=4ae4c1f16b5044c960d084775e347237" target="_blank"><strong>Rust Playground</strong></a>&nbsp;</figcaption><h2>Conclusión</h2><p>La <a href="https://www.lurklurk.org/effective-rust/generics.html" target="_blank">conveniencia de los genéricos sobre los trait objects</a> dependerá del contexto. Los genéricos tendrán mejor performance por necesidad, puesto que su tamaño es conocido en tiempo de compilación; los trait objects, por otra parte, serán enviados al heap y usarán dispatch dinámico, lo cual tendrá un mayor consimo de memoria; pero la flexibilidad que ofrece quizás sea beneficiosa en comparación.</p><p>Para resumir, cada uno de los punteros: <mark>Box</mark>, <mark>Rc</mark> o <mark>Arc</mark>, nos ofrecerá diferentes acciones:</p><ul><li><mark>Box</mark>: código sincrónico donde la instancia será consumida una sola vez.</li><li><mark>Rc</mark>: código sincrónico donde la instancia debe ser clonada para pasarla a distintas factorías.</li><li><mark>Arc:</mark> código asíncrono que requiere seguridad de subprocesos.</li></ul><p>Con estas cuatro opciones —genéricos, <mark>Box</mark>, <mark>Rc</mark> y <mark>Arc</mark>— es posible obtener una inversión de dependencias correcta y aplicar los patrones que pueda requerir nuestro código.</p><p>El código completo de los cuatro ejemplos puede encontrarse en <a href="https://www.git.antoniodiaz.me/antoniodcorrea/rust-dependency-inversion" target="_blank">&nbsp;<a href="https://www.git.antoniodiaz.me/antoniodcorrea/rust-dependency-inversion" target="_blank">git.antoniodiaz.me/antoniodcorrea/rust-dependency-inversion</a>&nbsp;</a>.</p></p>]]></description></item><item><title>Conversión de tipos entre Rust y PostgreSQL</title><pubDate>Tue, 20 Jan 1970 00:00:00 GMT</pubDate><guid isPermaLink="true">https://www.antoniodiaz.me/es/blog/7</guid><description><![CDATA[<p><p>Algunas notas sobre conversión de tipos entre Rust y Postgres usando <a href="https://docs.rs/tokio-postgres/latest/tokio_postgres/" target="_blank">tokio-postgres</a>, cliente asíncrono de PostgreSQL para Rust.</p><p>Hay dos posibilidades de mapeado entre tipos de Rust y tipos relacionados a la infraestructura que se esté usando, por ejemplo Postgres:</p><ul><li>Conversión de tipos de Postgres a Rust.</li><li>Conversión de tipos de Rust a Postgres.</li></ul><p>Esta librería expone algunos traits con implementaciones básicas que pueden ser usadas para convertir los tipos de nuetra aplicación entre tipos de PostgreSQL, y a la inversa. A modo de ejemplo, está <a href="https://docs.rs/postgres/latest/postgres/types/trait.FromSql.html" target="_blank">FromSQL</a>, que convertirán primitivos de Rust en tipos de Postgres: un <mark>bool</mark> de Rust será transformado en un <mark>bool</mark> de Postgres; <mark>i64</mark> en <mark>bigint</mark>, <mark>&amp;str</mark> o <mark>String</mark> en <mark>text</mark>, etc. Se debe tener cuidado en la conversión, y podemos encontrar algunos resultados indeseados si el usuario no tiene claro cómo los tipos están siendo convertidos; pero en general está bien documentado y es consistente con las expectativas.</p><p>El problema surje cuando se precisa realizar una conversión de tipos entre objetos o tipos más complejos, como un <mark>enum</mark> de Postgres y un <mark>enum</mark> de Rust. Esta funcionalidad se puede encontrar frecuentemente en librerías que ofrecen alguna variante de Object Relational Mapping. Afortunadamente, <mark>tokio_postgres</mark> no lo hace.</p><p>Es importante remarcar «afortunadamente»: los ORM suelen ser promocionados como una manera de acelerar y facilitar el desarrollo para tareas relativamente sencillas; pero incorporan al código serios problemas, razón por la que se les ha dado en llamar el <a href="http://web.archive.org/web/20061115004703/http://blogs.tedneward.com/2006/06/26/The+Vietnam+Of+Computer+Science.aspx" target="_blank">Vietnam of Computer Science</a>.</p><p>El debate sobre las posibilidades y conveniencia del Object Relational Mapping es largo y está lleno de sutilezas. Entre otros encontramos dos problemas principales:</p><ul><li>Fuerzan al desarrollador a acoplar las entidades y la infraestructura..</li><li>Son incapaces de traducir consistentemente el espacio entre objetos y estructuras relacionales, a excepción de casos muy sencillos. La optimización proporcionada por SQL se pierde en cualquier caso no trivial, con duplicaciones de queries que pueden suponer un serio problema de eficiencia.</li></ul><p>El hecho de que tokio-postgres carezca de funcionalidades ORM y requiera al usuario mapear manualmente entre tipos de Rust y Postgres no sólo no supone un inconveniente, sino que es un indicio de buenas prácticas.</p><h2>Mapeando de SQL a Rust</h2><p>Como se ha mencionado, <mark>tokio-postgres</mark> expone un trait <a href="https://docs.rs/postgres/latest/postgres/types/trait.FromSql.html" target="_blank">FromSQL</a> con implementaciones básicas para primitivos y algunos tipos espeficos. Con estas implementaciones el usuario no tiene que preocuparse de la conversión entre <mark>text</mark> y <mark>String</mark>, o entre <mark>bigint</mark> y <mark>i64</mark>.</p><p>El problema surge cuando se requiere realizar una conversión entre tipos complejos, como entre un <mark>myenum</mark> de Postgres y un <mark>MyEnum</mark> de Rust.</p><pre><code>language-postgres<br />⁠<br />⁠create type myenum as enum(&#39;variant_a&#39;, &#39;variant_b&#39;);</code></pre><figcaption>Tipo de Postgres <mark>myenum</mark>&nbsp;</figcaption><pre><code>language-rust<br />⁠<br />⁠pub enum MyEnum {<br />⁠  VariantA,<br />⁠  VariantB,<br />⁠}</code></pre><figcaption>Tipo de Rust <mark>MyEnum</mark>&nbsp;</figcaption><p>Las conversiones de enums no están implementadas en <a href="https://docs.rs/tokio-postgres/latest/tokio_postgres/" target="_blank">tokio-postgres</a>, ya que esta librería no ofrece ninguna funcionalidad de mapeado <mark><u>Object &lt;-&gt; Relational</u></mark>: aquí es donde es útil el trait <a href="https://docs.rs/postgres/latest/postgres/types/trait.FromSql.html" target="_blank">FromSQL</a> expuesto por esta librería, y que puede ser usado por el usuario para crear una implementación propia para el enum <mark>MyEnum</mark> de Rust.</p><p>El trait <mark>FromSQL</mark> expone cuatro métodos, de los cuales se pueden usar dos para crear la implementación requerida: <mark>from_sql</mark>, que se encargará de la conversión entre tipos, y <mark>accepts</mark>, que comprobará si la conversión debe ser realizada para un tipo determinado.</p><pre><code>language-rust<br />⁠<br />pub trait FromSql&lt;&#39;a&gt;: Sized {<br />⁠  fn from_sql(ty: &amp;Type, raw: &amp;&#39;a [u8]) -&gt; Result&lt;Self, Box&lt;dyn Error + Sync + Send&gt;&gt;;<br />⁠  […]<br />⁠  fn accepts(ty: &amp;Type) -&gt; bool;<br />⁠}</code></pre><figcaption>Trait <mark>FromSQL</mark></figcaption><p>Se trata de una tarea sencilla:  <mark>from_sql</mark> deberá producir las variantes correctas del enum, que se podrán obtener haciendo match contra el raw string binario; en <mark>accepts</mark> podemos comprobar que el enum que vamos a convertir tiene el nombre correcto.</p><pre><code>language-rust<br />⁠<br />impl FromSql&lt;&#39;_&gt; for MyEnum {<br />⁠  fn from_sql(_sql_type: &amp;Type, value: &amp;[u8]) -&gt; Result&lt;Self, Box&lt;dyn Error + Sync + Send&gt;&gt; {<br />⁠    match value {<br />⁠      b&quot;variant_a&quot; =&gt; Ok(MyEnum::VariantA),<br />⁠      b&quot;variant_b&quot; =&gt; Ok(MyEnum::VariantB),<br />⁠      _ =&gt; Ok(MyEnum::VariantA),<br />⁠    }<br />⁠  }<br />⁠<br />⁠  fn accepts(sql_type: FromSqlType) -&gt; bool {<br />⁠    sql_type.name() == &quot;myenum&quot;<br />⁠  }<br />⁠}</code></pre><figcaption>Mapping from <mark>myenum</mark> Postgres type into <mark>MyEnum</mark> Rust type</figcaption><p>Como se trata de una funcionalidad directamente relacionada con Postgres, puede localizarse en el repositorio o módulo acoplado a la base de datos, manteniéndolo fuera de la capa de dominio. De este modo se evita crear una dependencia de dominio hacia infraestructura, siendo esta última quien referencia al dominio —cuando menciona <mark>MyEnum</mark> por ejemplo—, y no a la inversa.</p><p>Esta implementacion describe a la librería <a href="https://docs.rs/tokio-postgres/latest/tokio_postgres/" target="_blank">tokio-postgres</a> cómo realizar la conversión de tipos entre SQL y Rust para este tipo específico, de modo que siempre que una query devuelva un tipo <mark>myenum</mark> de Postgres aparecerá en la aplicación como <mark>MyEnum</mark>.</p><h2>Mapeando de Rust a SQL</h2><p>Para realizar la conversión inversa, de Rust <mark>MyEnum</mark> a Postgres <mark>myenum</mark>, se puede implementar el trait <a href="https://docs.rs/postgres/latest/postgres/types/trait.ToSql.html" target="_blank">ToSql</a> usando los métodos <mark>to_sql</mark> y <mark>accepts</mark>:</p><pre><code>language-rust<br />⁠<br />pub trait ToSql: fmt::Debug {<br />⁠    fn to_sql(&amp;self, ty: &amp;Type, out: &amp;mut BytesMut) -&gt; Result&lt;IsNull, Box&lt;dyn Error + Sync + Send&gt;&gt;<br />⁠    where<br />⁠        Self: Sized;<br />⁠    fn accepts(ty: &amp;Type) -&gt; bool<br />⁠    where<br />⁠        Self: Sized;<br />⁠    fn to_sql_checked(<br />⁠        &amp;self,<br />⁠        ty: &amp;Type,<br />⁠        out: &amp;mut BytesMut,<br />⁠    ) -&gt; Result&lt;IsNull, Box&lt;dyn Error + Sync + Send&gt;&gt;;<br />⁠}</code></pre><figcaption><mark>ToSQL</mark> trait</figcaption><p>Además hay que implementar <mark>to_sql_checked</mark>, pero el <a href="https://docs.rs/postgres/latest/postgres/types/macro.to_sql_checked.html" target="_blank">macro </a><mark>types::to_sql_checked!</mark> puede generar esta implementacion automaticamente.</p><p>Cuando implementamos <mark>to_sql</mark>  las variantes de <mark>MyEnum</mark> tienen que ser transformadas a <mark>&amp;str</mark> antes. Para ello hay que primero implementar <mark>Display</mark> para <mark>MyEnum</mark>, de modo que podamos posteriormente implementar <mark>ToSQL</mark>.</p><pre><code>language-rust<br />⁠<br />use std::fmt::{Display, Formatter, Result };<br />⁠<br />⁠impl Display for MyEnum {<br />⁠  fn fmt(&amp;self, f: &amp;mut Formatter) -&gt; Result {<br />⁠    match self {<br />⁠      MyEnum::VariantA =&gt; write!(f, &quot;variant_a&quot;),<br />⁠      MyEnum::VariantB =&gt; write!(f, &quot;variant_b&quot;),<br />    }<br />⁠  }<br />⁠}</code></pre><figcaption>Implementing <mark>Display</mark> for <mark>MyEnum</mark></figcaption><p>Una vez implementado <mark>Display</mark> para <mark>MyEnum</mark> es posible implementar<mark>ToSql</mark> , igualmente para <mark>MyEnum</mark>:</p><pre><code>language-rust<br />⁠<br />use tokio_postgres::{<br />⁠  types::{to_sql_checked, ToSql, IsNull, Type},<br />⁠};<br />⁠use bytes::BytesMut;<br />⁠use std::{error::Error, result::Result};<br />⁠<br />⁠impl ToSql for Role {<br />⁠  fn to_sql(&amp;self, ty: &amp;Type, out: &amp;mut BytesMut) -&gt; Result&lt;IsNull, Box&lt;dyn Error + Sync + Send&gt;&gt;; {<br />⁠    format!(&quot;{}&quot;, self).to_sql(ty, out)<br />⁠  }<br />⁠<br />⁠  fn accepts(sql_type: &amp;Type) -&gt; bool {<br />⁠    sql_type.name() == &quot;myenum&quot;<br />⁠  }<br />⁠<br />⁠  to_sql_checked!();<br />⁠}</code></pre><figcaption>Implementing ToSql for <mark>MyEnum</mark></figcaption><p>De esta manera siempre que Postgres reciba un enum de Rust <mark>MyEnum</mark>, éste será convertido en un enum de Postgres<mark>myenum</mark>.</p><h2>Conclusión</h2><p>Cabe mencionar otros casos donde será necesario realizar conversiones entre tipos, como sucede con el tipo <mark>Row</mark>, que podrá ser usado con el trait <mark>From</mark> de Rust para convertir entre filas de Postgres y objetos de Rust de la aplicación. </p><p>Por ejemplo, para un determinado struct <mark>Article,</mark> la implementación de <mark>From</mark> para este struct tendrá este aspecto:</p><pre><code>language-rust<br />⁠<br />// Domain<br />⁠pub struct Article {<br />⁠  pub id: String,<br />⁠  pub title: String,<br />⁠  pub created_at: DateTime,<br />⁠}<br />⁠<br />⁠// Repository<br />⁠impl From&lt;Row&gt; for Article {<br />⁠  fn from(row: Row) -&gt; Self {<br />⁠    Self {<br />⁠      id: row.get(&quot;id&quot;),<br />⁠      title: row.get(&quot;title&quot;),<br />⁠      created_at: row.get(&quot;created_at&quot;),<br />⁠    }<br />⁠  }<br />⁠}<br /><br />⁠let result = client.query(&quot;select * from article&quot;).await?;<br />⁠let articles: Vec&lt;Article&gt; = result.into_iter()<br />⁠    .map(Article::from)<br />    ⁠.collect();</code></pre><figcaption>Implementing <mark>From&lt;Row&gt;</mark> for some object <mark>Article</mark></figcaption><p>Como se puede apreciar en general esta librería es bastante sencilla de usar siempre que se consulten los traits expuestos, ofreciendo unas funcionalidades bien delimitadas y facilitando al usuario extenderlas implementando estos traits.</p></p>]]></description></item></channel></rss>