From 47af962c4de32b9438b8c2942e690c5c25b7dd87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Iv=C3=A1n=20Patricio=20Moreno?= Date: Fri, 6 May 2022 22:22:09 -0500 Subject: [PATCH 1/7] =?UTF-8?q?Correcciones=20peque=C3=B1as?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 00_intro.md | 4 +- 01_values.md | 53 ++- 02_program_structure.md | 26 +- 03_functions.md | 50 +-- 05_higher_order.md | 2 +- 07_robot.md | 2 +- 09_regexp.md | 2 +- 10_modules_original.md | 864 ++++++++++++++++++++++++++++++++++++++++ 11_async.md | 2 +- 9 files changed, 934 insertions(+), 71 deletions(-) create mode 100644 10_modules_original.md diff --git a/00_intro.md b/00_intro.md index 1dd4fb6d0..f129bc7e3 100644 --- a/00_intro.md +++ b/00_intro.md @@ -456,8 +456,8 @@ otro entorno en donde programar JavaScript. A lo largo del libro, hay cinco _capítulos de proyectos_, que describen programas de ejemplo más grandes para darte una idea de la programación real. En orden de aparición, trabajaremos en la construcción de un -[robot de delivery](robot), un [lenguaje de programación](lenguaje), -un [juego de plataforma](juego), un [programa de paint](paint) y un [sitio web dinámico](skillsharing). +[robot de entrega](robot), un [lenguaje de programación](lenguaje), +un [juego de plataforma](juego), un [programa de pintura](paint) y un [sitio web dinámico](skillsharing). La parte del lenguaje del libro comienza con cuatro capítulos para presentar la estructura básica del lenguaje de JavaScript. Estos introducen diff --git a/01_values.md b/01_values.md index f1fe11f7b..bec164908 100644 --- a/01_values.md +++ b/01_values.md @@ -51,7 +51,7 @@ Entonces ese es el número binario 00001101, o 8 + 4 + 1, o 13. {{index memory, "volatile data storage", "hard drive"}} -Imagina un mar de bits—un océano de ellos. Una computadora moderna +Imagina un mar de bits, un océano repleto. Una computadora moderna promedio tiene mas de 30 billones de bits en su almacenamiento de datos volátiles (memoria funcional). El almacenamiento no volátil (disco duro o equivalente) tiende a tener unas cuantas mas ordenes de magnitud. @@ -93,18 +93,18 @@ En un programa hecho en JavaScript, se escriben de la siguiente manera: {{index "binary number"}} -Utiliza eso en un programa, y ocasionara que el patron de bits +Utiliza eso en un programa, y ocasionará que el patrón de bits que representa el número 13 sea creado dentro de la memoria de la computadora. -{{index [number, representation], bit}} +{{index [number, representación], bit}} -JavaScript utiliza un número fijo de bits, específicamente 64 de ellos, +JavaScript utiliza un número fijo de bits, específicamente 64, para almacenar un solo valor numérico. Solo existen una cantidad finita de patrones que podemos crear con 64 bits, lo que significa que la cantidad de números diferentes que pueden ser representados es limitada. Para una cantidad de _N_ dígitos decimales, la cantidad de números que pueden ser representados es 10^N^. Del mismo modo, dados 64 dígitos binarios, podemos -representar 2^64^ números diferentes, lo que es alrededor de 18 mil trillones +representar 2^64^ números diferentes, lo que es alrededor de 18 trillones (un 18 con 18 ceros más). Eso es muchísimo. La memoria de un computador solía ser mucho mas pequeña que en la actualidad, @@ -118,14 +118,13 @@ con números verdaderamente astronómicos. {{index sign, "floating-point number", "fractional number", "sign bit"}} -A pesar de esto, no todos los números enteros por debajo de 18 mil +A pesar de esto, no todos los números enteros por debajo de 18 trillones caben en un número de JavaScript. Esos bits también almacenan números negativos, por lo que un bit indica el signo de un número. Un problema mayor es que los números no enteros tienen que ser representados también. Para hacer esto, algunos de los bits son usados para almacenar la posición del punto decimal. El número entero mas grande que puede ser -almacenado está en el rango de los 9 trillones (15 ceros)—lo cual es -todavía placenteramente inmenso. +almacenado está en el rango de los nueve mil billones (15 ceros), que sigue siendo inmenso. {{index [number, notation]}} @@ -150,8 +149,8 @@ Eso es 2.998 × 10^8^ = 299,800,000. {{index pi, [number, "precision of"], "floating-point number"}} Los cálculos con números enteros (también llamados _((integer))s_) -mas pequeños a los 9 trillones anteriormente mencionados están -garantizados a ser siempre precisos. Desafortunadamente, los calculos +mas pequeños a los nueve mil billones anteriormente mencionados están +garantizados a ser siempre precisos. Desafortunadamente, los cálculos con números fraccionarios, generalmente no lo son. Así como π (pi) no puede ser precisamente expresado por un número finito de números decimales, muchos números pierden algo de precisión cuando solo hay 64 bits disponibles @@ -202,17 +201,17 @@ El operador `/` tiene la misma precedencia que `*`. Lo mismo aplica para del otro, como en `1 - 2 + 1`, estos se aplican de izquierda a derecha: `(1 - 2) + 1`. -Estas reglas de precedencia no son algo de lo que deberias preocuparte. +Estas reglas de precedencia no son algo de lo que deberías preocuparte. Cuando tengas dudas, solo agrega un paréntesis. -{{index "modulo operator", division, "remainder operator", "% operator"}} +{{index "operador módulo", división, "operador de sobrante", "operador %"}} Existe otro operador aritmético que quizás no reconozcas inmediatamente. El símbolo `%` es utilizado para representar la operación de _residuo_. `X % Y` es el residuo de dividir `X` entre `Y`. Por ejemplo, `314 % 100` produce `14`, y `144 % 12` produce `0`. La precedencia del residuo es la la misma que la de la multiplicación y la división. Frecuentemente veras -que este operador es tambien conocido como _modulo_. +que este operador es también conocido como _modulo_. ### Números especiales @@ -245,8 +244,8 @@ significante. {{index syntax, text, character, [string, notation], "single-quote character", "double-quote character", "quotation mark", backtick}} -El próximo tipo de dato básico es el _((string))_. Los Strings -son usados para representar texto. Son escritos encerrando su contenido +El próximo tipo de dato básico es el _((string))_. Las cadenas (_strings_) +son usados para representar texto. Son escritas encerrando su contenido en comillas: ``` @@ -264,7 +263,7 @@ y al final coincidan. Casi todo puede ser colocado entre comillas, y JavaScript construirá un valor string a partir de ello. Pero algunos caracteres son mas difíciles. Te puedes imaginar que colocar comillas entre comillas podría ser difícil. -Los _Newlines_ (los caracteres que obtienes cuando presionas la tecla de Enter) +Los _saltos de línea_ (los caracteres que obtienes cuando presionas la tecla de Enter) solo pueden ser incluidos cuando el string está encapsulado con comillas invertidas (`` ` ``). @@ -307,8 +306,8 @@ Un carácter de salto de linea es escrito así: \"\\n\"." {{index [string, representation], Unicode, character}} -También los strings deben de ser modelados como una serie de bits para poder -existir dentro del computador. La forma en la que JavaScript hace esto +Las cadenas deben de ser modeladas como una serie de bits para poder +existir dentro de la computadora. La forma en la que JavaScript hace esto es basada en el estándar _((Unicode))_. Este estándar asigna un número a todo carácter que alguna vez pudieras necesitar, incluyendo caracteres en Griego, Árabe, Japones, Armenio, y asi sucesivamente. Si tenemos un número para @@ -327,9 +326,9 @@ tema en el [Capitulo 5](orden_superior#unidades_de_codigo). {{index "+ operator", concatenation}} -Los strings no pueden ser divididos, multiplicados, o substraidos, pero -el operador `+` _puede_ ser utilizado en ellos. No los agrega, sino que -los _concatena_—pega dos strings juntos. La siguiente línea producirá +Los strings no pueden ser divididas, multiplicados o restados, pero +el operador `+` _puede_ ser utilizado en ellos. No los suma, sino que +los _concatena_: pega dos cadenas una tras otra. La siguiente línea producirá el string `"concatenar"`: ``` @@ -343,10 +342,10 @@ a estas en el [Capítulo 4](datos#metodos). {{index interpolation, backtick}} Los strings escritos con comillas simples o dobles se comportan -casi de la misma manera—La unica diferencia es el tipo de comilla que necesitamos +casi de la misma manera—La única diferencia es el tipo de comilla que necesitamos para escapar dentro de ellos. Los strings de comillas inversas, usualmente llamados _((plantillas literales))_, pueden realizar algunos trucos -más. Mas alla de permitir saltos de lineas, pueden también incrustar otros +más. Mas allá de permitir salto de línea, pueden también incrustar otros valores. ``` @@ -361,7 +360,7 @@ posición. El ejemplo anterior produce "_la mitad de 100 es 50_". {{index operator, "typeof operator", type}} -No todo los operadores son simbolos. Algunos se escriben como palabras. +No todo los operadores son símbolos. Algunos se escriben como palabras. Un ejemplo es el operador `typeof`, que produce un string con el nombre del tipo de valor que le demos. @@ -404,7 +403,7 @@ true (verdadero) y false (falso) que se escriben de la misma forma. ### Comparación -{{index comparison}} +{{index comparación}} Aquí se muestra una forma de producir valores Booleanos: @@ -415,7 +414,7 @@ console.log(3 < 2) // → false ``` -{{index [comparison, "of numbers"], "> operator", "< operator", "greater than", "less than"}} +{{index [comparación, "de números"], "> operador", "< operator", "greater than", "less than"}} Los signos `>` y `<` son tradicionalmente símbolos para "mayor que" y "menor que", respectivamente. Ambos son operadores binarios. @@ -439,7 +438,7 @@ incluidos en el ordenamiento. Cuando comparamos strings, JavaScript evalúa los caracteres de izquierda a derecha, comparando los códigos ((Unicode)) uno por uno. -{{index equality, ">= operator", "<= operator", "== operator", "!= operator"}} +{{index igualdad, "operador >=", "operador <=", "operador ==", "operador !="}} Otros operadores similares son `>=` (mayor o igual que), `<=` (menor o igual que), `==` (igual a), y `!=` (no igual a). diff --git a/02_program_structure.md b/02_program_structure.md index 6210df4ef..bf5c12156 100644 --- a/02_program_structure.md +++ b/02_program_structure.md @@ -4,14 +4,14 @@ Y mi corazón brilla de un color rojo brillante bajo mi piel transparente y translúcida, y tienen que administrarme 10cc de JavaScript para conseguir que -regrese. (respondo bien a las toxinas en la sangre.) Hombre, esa cosa es -increible! +regrese. (respondo bien a las toxinas en la sangre.) ¡Hombre, esa cosa es +increíble! quote}} {{index why, "Poignant Guide"}} -{{figure {url: "img/chapter_picture_2.jpg", alt: "Foto de tentaculos sosteniendo objetos", chapter: framed}}} +{{figure {url: "img/chapter_picture_2.jpg", alt: "Foto de tentáculos sosteniendo objetos", chapter: framed}}} En este capítulo, comenzaremos a hacer cosas que realmente se pueden llamar _programación_. Expandiremos nuestro dominio del lenguaje JavaScript @@ -64,8 +64,8 @@ ella. Esto es un programa: Sin embargo, es un programa inútil. Una ((expresión)) puede estar feliz solo con producir un valor, que luego pueda ser utilizado por el código circundante. Una ((declaración)) es independiente por si misma, por lo que equivale a -algo solo si afecta al mundo. Podría mostrar algo en la pantalla—eso -cuenta como cambiar el mundo—o podría cambiar el estado interno de +algo solo si afecta al mundo. Podría mostrar algo en la pantalla, eso +cuenta como cambiar el mundo, o podría cambiar el estado interno de la máquina en una manera que afectará a las declaraciones que vengan después de ella. Estos cambios se llaman _((efecto secundario))s_. Las declaraciones en el ejemplo anterior solo producen los valores `1` y `true` y luego @@ -138,7 +138,7 @@ console.log(humor); {{index [binding, "model of"], "tentacle (analogy)"}} Deberías imaginar a las vinculaciones como tentáculos, en lugar de cajas. -Ellas no _contienen_ valores; ellas los _agarran_—dos vinculaciones pueden +Ellas no _contienen_ valores; ellas los _agarran_, dos vinculaciones pueden referirse al mismo valor. Un programa solo puede acceder a los valores que todavía pueda referenciar. Cuando necesitas recordar algo, creces un tentáculo para aferrarte a él o vuelves a conectar uno de tus tentáculos @@ -202,8 +202,8 @@ fácilmente puedas consultarlo más adelante. {{index "underscore character", "dollar sign", [binding, naming]}} Los nombres de las vinculaciones pueden ser cualquier palabra. Los dígitos pueden -ser parte de los nombres de las vinculaciones pueden—`catch22` es un nombre -válido, por ejemplo—pero el nombre no debe comenzar con un dígito. +ser parte de los nombres de las vinculaciones pueden, `catch22` es un nombre +válido, por ejemplo, pero el nombre no debe comenzar con un dígito. El nombre de una vinculación puede incluir signos de dólar (`$`) o caracteres de subrayado (`_`), pero no otros signos de puntuación o caracteres especiales. @@ -262,7 +262,7 @@ prompt("Introducir contraseña"); {{index parameter, [function, application]}} -Ejecutar una función tambien se conoce como _invocarla_, _llamarla_, o +Ejecutar una función también se conoce como _invocarla_, _llamarla_, o _aplicarla_. Puedes llamar a una función poniendo ((paréntesis)) después de una expresión que produzca un valor de función. Usualmente usarás directamente el nombre de la vinculación que contenga la función. Los valores entre @@ -592,7 +592,7 @@ el ciclo continúa dando vueltas hasta que proporciones un nombre no-vacío. {{index "code structure", whitespace, "programming style"}} En los ejemplos, he estado agregando espacios adelante de declaraciones que -son parte de una declaración más grande. Estos no son necesarios—la computadora +son parte de una declaración más grande. Estos no son necesarios, la computadora aceptará el programa normalmente sin ellos. De hecho, incluso las nuevas ((líneas)) en los programas son opcionales. Podrías escribir un programa en una sola línea inmensa si asi quisieras. @@ -602,7 +602,7 @@ la estructura del código se destaque. En código donde se abren nuevos bloques dentro de otros bloques, puede ser difícil ver dónde termina un bloque y donde comienza el otro. Con la indentación apropiada, la forma visual de un programa corresponde a la forma de los bloques dentro de él. Me gusta -usar dos espacios para cada bloque abierto, pero los gustos varían—algunas +usar dos espacios para cada bloque abierto, pero los gustos varían, algunas personas usan cuatro espacios, y algunas personas usan ((carácteres de tabulación)). Lo cosa importante es que cada bloque nuevo agregue la misma cantidad de espacio. @@ -779,7 +779,7 @@ else accionPorDefault(); Existe un constructo llamado `switch` que está destinada a expresar tales "despachos" de una manera más directa. Desafortunadamente, la sintaxis que JavaScript usa para esto (que heredó de la línea lenguajes de programación -C/Java) es algo incómoda—una cadena de declaraciones `if` podria llegar a verse +C/Java) es algo incómoda, una cadena de declaraciones `if` podria llegar a verse mejor. Aquí hay un ejemplo: ``` @@ -807,7 +807,7 @@ encuentra ningún valor que coincida. Continuará ejecutándose, incluso a través de otras etiquetas, hasta que llegue a una declaración `break`. En algunos casos, como en el caso `"soleado"` del ejemplo, esto se puede usar para compartir algo de código entre casos (recomienda salir para ambos -climas soleado y nublado). Pero ten cuidado—es fácil olvidarse de +climas soleado y nublado). Pero ten cuidado, es fácil olvidarse de `break`, lo que hará que el programa ejecute código que no quieres que sea ejecutado. diff --git a/03_functions.md b/03_functions.md index a9812775e..2dc4fecf9 100644 --- a/03_functions.md +++ b/03_functions.md @@ -96,7 +96,7 @@ Cuando el control se encuentre con tal declaración, inmediatamente salta de la función actual y devuelve el valor retornado al código que llamó la función. Una declaración `return` sin una expresión después de ella hace que la función retorne `undefined`. Funciones que no tienen una -declaración `return` en absoluto, como `hacerSonido`, similarmente retornan +declaración `return` en absoluto, como `hacerSonido`, de manera similar retornan `undefined`. {{index parameter, [function, application], [binding, "from parameter"]}} @@ -110,10 +110,10 @@ el código en la función en sí. {{indexsee "top-level scope", "global scope"}} {{index "var keyword", "global scope", [binding, global], [binding, "scope of"]}} -Cada ((vinculación)) tiene un _((alcace))_, que correspone a la parte del +Cada ((vinculación)) tiene un _((alcace))_, que corresponde a la parte del programa en donde la vinculación es visible. Para vinculaciones definidas fuera de cualquier función o bloque, el alcance es todo el -programa—puedes referir a estas vinculaciones en donde sea que quieras. +programa, puedes referir a estas vinculaciones en donde sea que quieras. Estas son llamadas _globales_. {{index "local scope", [binding, local]}} @@ -122,19 +122,19 @@ Pero las vinculaciones creadas como ((parámetro))s de función o declaradas dentro de una función solo puede ser referenciadas en esa función. Estas se llaman _locales_. Cada vez que se llame a la función, se crean nuevas instancias de estas vinculaciones. Esto proporciona cierto -aislamiento entre funciones—cada llamada de función actúa sobre su pequeño +aislamiento entre funciones, cada llamada de función actúa sobre su pequeño propio mundo (su entorno local), y a menudo puede ser entendida sin saber mucho acerca de lo qué está pasando en el entorno global. {{index "let keyword", "const keyword", "var keyword"}} Vinculaciones declaradas con `let` y `const` son, de hecho, locales al -_((bloque))_ donde esten declarados, así que si creas uno de esas +_((bloque))_ donde estén declarados, así que si creas uno de esas dentro de un ciclo, el código antes y después del ciclo no puede "verlas". En JavaScript anterior a 2015, solo las funciones creaban nuevos alcances, por lo que las vinculaciones de estilo-antiguo, creadas con la palabra clave `var`, son visibles a lo largo de toda la función en la que -aparecen—o en todo el alcance global, si no están dentro de una función. +aparecen, o en todo el alcance global, si no están dentro de una función. ``` let x = 10; @@ -153,7 +153,7 @@ console.log(x + z); Cada ((alcance)) puede "mirar afuera" hacia al alcance que lo rodee, por lo que `x` es visible dentro del bloque en el ejemplo. La excepción es cuando -vinculaciones múltiples tienen el mismo nombre—en ese caso, el código solo +vinculaciones múltiples tienen el mismo nombre, en ese caso, el código solo puede ver a la vinculación más interna. Por ejemplo, cuando el código dentro de la función `dividirEnDos` se refiera a `numero`, estara viendo su _propio_ `numero`, no el `numero` en el alcance global. @@ -182,8 +182,8 @@ funciones, produciendo múltiples grados de localidad. {{index "landscape example"}} -Por ejemplo, esta función—que muestra los ingredientes necesarios para -hacer un lote de humus—tiene otra función dentro de ella: +Por ejemplo, esta función, que muestra los ingredientes necesarios para +hacer un lote de humus, tiene otra función dentro de ella: ``` const humus = function(factor) { @@ -212,7 +212,7 @@ de la función externa. Pero sus vinculaciones locales, como `unidad` o En resumen, cada alcance local puede ver también todos los alcances locales que lo contengan. El conjunto de vinculaciones visibles dentro de un bloque está determinado por el lugar de ese bloque en el texto del programa. -Cada alcance local puede tambien ver todos los alcances locales que lo +Cada alcance local puede también ver todos los alcances locales que lo contengan, y todos los alcances pueden ver el alcance global. Este enfoque para la visibilidad de vinculaciones es llamado _((alcance léxico))_. @@ -228,7 +228,7 @@ y nunca cambia. Esto hace que sea fácil confundir la función con su nombre. {{index [binding, assignment]}} Pero los dos son diferentes. Un valor de función puede hacer todas las cosas que -otros valores pueden hacer—puedes usarlo en ((expresion))es arbitrarias, no +otros valores pueden hacer, puedes usarlo en ((expresion))es arbitrarias, no solo llamarlo. Es posible almacenar un valor de función en una nueva vinculación, pasarla como argumento a una función, y así sucesivamente. Del mismo modo, una vinculación que contenga una función sigue siendo solo @@ -443,7 +443,7 @@ que puedes pasar a una función. Si pasa demasiados, los adicionales son ignorados. Si pasas muy pocos, a los parámetros faltantes se les asigna el valor `undefined`. -La desventaja de esto es que es posible—incluso probable—que +La desventaja de esto es que es posible, incluso probable, que accidentalmente pases la cantidad incorrecta de argumentos a las funciones. Y nadie te dira nada acerca de eso. @@ -497,7 +497,7 @@ En el [próximo capítulo](datos#parametros_rest), veremos una forma en el que el cuerpo de una función puede obtener una lista de todos los argumentos que son pasados. Esto es útil porque hace posible que una función acepte cualquier cantidad de argumentos. Por ejemplo, `console.log` hace -esto—muetra en la consola todos los valores que se le den. +esto, muetra en la consola todos los valores que se le den. ``` console.log("C", "O", 2); @@ -532,14 +532,14 @@ console.log(envolver2()); // → 2 ``` -Esto está permitido y funciona como es de esperar—ambas instancias de +Esto está permitido y funciona como es de esperar, ambas instancias de las vinculaciones todavía pueden ser accedidas. Esta situación es una buena demostración del hecho de que las vinculaciones locales se crean de nuevo para cada llamada, y que las diferentes llamadas no pueden pisotear las distintas vinculaciones locales entre sí. -Esta característica—poder hacer referencia a una instancia específica -de una vinculación local en un alcance encerrado—se llama _((cierre))_. +Esta característica, poder hacer referencia a una instancia específica +de una vinculación local en un alcance encerrado, se llama _((cierre))_. Una función que que hace referencia a vinculaciones de alcances locales alrededor de ella es llamada _un_ cierre. Este comportamiento no solo te libera de tener que preocuparte @@ -640,10 +640,10 @@ paralizante. {{index "premature optimization"}} Por lo tanto, siempre comienza escribiendo algo que sea correcto y fácil de -comprender. Si te preocupa que sea demasiado lento—lo que generalmente +comprender. Si te preocupa que sea demasiado lento, lo que generalmente no sucede, ya que la mayoría del código simplemente no se ejecuta con la suficiente frecuencia como para tomar cantidades significativas de -tiempo—puedes medir luego y mejorar si es necesario. +tiempo, puedes medir luego y mejorar si es necesario. {{index "branching recursion"}} @@ -743,7 +743,7 @@ aún más para explorar _cada_ solución continuada que produzca un número meno o igual a el número objetivo. Como no encuentra uno que llegue al objetivo, retorna `null` a la primera llamada. Ahí el operador `||` genera la llamada que explora `(1 * 3)` para que esta suceda. Esta búsqueda tiene más -suerte—su primera llamada recursiva, a través de _otra_ llamada recursiva, +suerte, su primera llamada recursiva, a través de _otra_ llamada recursiva, encuentra al número objetivo. Esa llamada más interna retorna un string, y cada uno de los operadores `||` en las llamadas intermedias pasa ese string a lo largo, en última instancia retornando la solución. @@ -787,7 +787,7 @@ siempre tengan tres dígitos de largo. 011 Pollos ``` -Esto pide una función de dos argumentos—el numero de vacas y el numero +Esto pide una función de dos argumentos, el numero de vacas y el numero de pollos. Vamos a programar. ``` @@ -844,8 +844,8 @@ imprimirInventarioGranja(7, 11, 3); {{index [function, naming]}} Funciona! Pero ese nombre, `imprimirEtiquetaAlcochadaConCeros`, es un poco -incómodo. Combina tres cosas—impresión, alcochar con ceros y añadir -una etiqueta—en una sola función. +incómodo. Combina tres cosas, impresión, alcochar con ceros y añadir +una etiqueta, en una sola función. {{index "zeroPad function"}} @@ -890,7 +890,7 @@ relleno con diferentes caracteres, y así sucesivamente. Un principio útil es no agregar mucho ingenio a menos que estes absolutamente seguro de que lo vas a necesitar. Puede ser tentador escribir "((framework))s" generalizados para cada funcionalidad que encuentres. -Resiste ese impulso. No realizarás ningún trabajo real de esta manera—solo +Resiste ese impulso. No realizarás ningún trabajo real de esta manera, solo estarás escribiendo código que nunca usarás. {{id pure}} @@ -917,7 +917,7 @@ secundarios. Una función _pura_ es un tipo específico de función de producción-de-valores que no solo no tiene efectos secundarios pero que tampoco depende de los -efectos secundarios de otro código—por ejemplo, no lee vinculaciones globales +efectos secundarios de otro código, por ejemplo, no lee vinculaciones globales cuyos valores puedan cambiar. Una función pura tiene la propiedad agradable de que cuando se le llama con los mismos argumentos, siempre produce el mismo valor (y no hace nada más). Una llamada a tal función puede ser @@ -964,7 +964,7 @@ Un aspecto clave en para comprender a las funciones es comprender los alcances. bloque crea un nuevo alcance. Los parámetros y vinculaciones declaradas en un determinado alcance son locales y no son visibles desde el exterior. Vinculaciones declaradas con `var` se comportan de manera -diferente—terminan en el alcance de la función más cercana o en el alcance global. +diferente, terminan en el alcance de la función más cercana o en el alcance global. Separar las tareas que realiza tu programa en diferentes funciones es util. No tendrás que repetirte tanto, y las funciones pueden diff --git a/05_higher_order.md b/05_higher_order.md index 014e73c8f..df3f68a03 100644 --- a/05_higher_order.md +++ b/05_higher_order.md @@ -203,7 +203,7 @@ tipo de ciclo, y luego provee un cuerpo. Sin embargo, el cuerpo ahora está escr como un valor de función, que está envuelto en el ((paréntesis)) de la llamada a `repetir`. Por eso es que tiene que cerrarse con el corchete de cierre _y_ paréntesis de cierre. En casos como este ejemplo, donde el -cuerpo es una expresión pequeña y única, podrias tambien omitir las +cuerpo es una expresión pequeña y única, podrias también omitir las llaves y escribir el ciclo en una sola línea. ## Funciones de orden superior diff --git a/07_robot.md b/07_robot.md index add4695ec..d3a707ab9 100644 --- a/07_robot.md +++ b/07_robot.md @@ -162,7 +162,7 @@ si no, retorna el estado anterior, ya que este no es un movimiento válido. Luego crea un nuevo estado con el destino como el nuevo lugar del robot. Pero también necesita crear un nuevo conjunto de paquetes—los paquetes que el robot esta llevando (que están en el lugar actual del robot) necesitan de -moverse tambien al nuevo lugar. Y paquetes que están dirigidos al nuevo +moverse también al nuevo lugar. Y paquetes que están dirigidos al nuevo lugar donde deben de ser entregados—es decir, deben de eliminarse del conjunto de paquetes no entregados. La llamada a `map` se encarga de mover los paquetes, y la llamada a `filter` hace la entrega. diff --git a/09_regexp.md b/09_regexp.md index a6dd4981a..0cd045d0f 100644 --- a/09_regexp.md +++ b/09_regexp.md @@ -1427,7 +1427,7 @@ delante del ((exponente)), se puede hacer con `[+\-]?` o `(\+|-|)` {{index "pipe character"}} La parte más complicada del ejercicio es el problema hacer coincidir -ambos `"5."` y `".5"` sin tambien coincidir coincidir con `"."`. Para esto, +ambos `"5."` y `".5"` sin también coincidir coincidir con `"."`. Para esto, una buena solución es usar el operador `|` para separar los dos casos—ya sea uno o más dígitos seguidos opcionalmente por un punto y cero o más dígitos _o_ un punto seguido de uno o más dígitos. diff --git a/10_modules_original.md b/10_modules_original.md new file mode 100644 index 000000000..58cf6f584 --- /dev/null +++ b/10_modules_original.md @@ -0,0 +1,864 @@ +{{meta {load_files: ["code/packages_chapter_10.js", "code/chapter/07_robot.js"]}}} + +# Módulos + +{{quote {author: "Tef", title: "Programming is Terrible", chapter: true} + +Escriba código que sea fácil de borrar, no fácil de extender. + +quote}} + +{{index "Yuan-Ma", "Book of Programming"}} + +{{figure {url: "img/chapter_picture_10.jpg", alt: "Picture of a building built from modular pieces", chapter: framed}}} + +{{index organization, "code structure"}} + +El programa ideal tiene una estructura cristalina. La forma en que funciona es +fácil de explicar, y cada parte juega un papel bien definido. + +{{index "organic growth"}} + +Un típico programa real crece orgánicamente. Nuevas piezas de funcionalidad +se agregan a medida que surgen nuevas necesidades. Estructurar—y preservar la +estructura—es trabajo adicional, trabajo que solo valdra la pena en el +futuro, la _siguiente_ vez que alguien trabaje en el programa. Así que es +tentador descuidarlo, y permitir que las partes del programa se vuelvan +profundamente enredadas. + +{{index readability, reuse, isolation}} + +Esto causa dos problemas prácticos. En primer lugar, entender tal sistema +es difícil. Si todo puede tocar todo lo demás, es difícil ver a cualquier +pieza dada de forma aislada. Estas obligado a construir un +entendimiento holístico de todo el asunto. En segundo lugar, si quieres +usar cualquiera de las funcionalidades de dicho programa en otra situación, +reescribirla podria resultar más fácil que tratar de desenredarla de su +contexto. + +El término "((gran bola de barro))" se usa a menudo para tales programas +grandes, sin estructura. Todo se mantiene pegado, y cuando intentas +sacar una pieza, todo se desarma y tus manos se ensucian. + +## Módulos + +{{index dependency}} + +Los _módulos_ son un intento de evitar estos problemas. Un ((módulo)) es una +pieza del programa que especifica en qué otras piezas este depende ( +sus _dependencias_) y qué funcionalidad proporciona para que otros módulos +usen (su _((interfaz))_). + +{{index "big ball of mud"}} + +Las interfaces de los módulos tienen mucho en común con las interfaces de +objetos, como las vimos en el [Capítulo 6](objeto#interface). Estas hacen parte +del módulo disponible para el mundo exterior y mantienen el resto privado. Al +restringir las formas en que los módulos interactúan entre sí, el +el sistema se parece más a un juego de ((LEGOS)), donde las piezas interactúan +a través de conectores bien definidos, y menos como barro, donde todo se mezcla +con todo. + +{{index dependency}} + +Las relaciones entre los módulos se llaman _dependencias_. Cuando un módulo +necesita una pieza de otro módulo, se dice que depende de ese +módulo. Cuando este hecho está claramente especificado en el módulo en sí, +puede usarse para descubrir qué otros módulos deben estar presentes para poder +ser capaces de usar un módulo dado y cargar dependencias automáticamente. + +Para separar módulos de esa manera, cada uno necesita su propio alcance privado. + +Simplemente poner todo tu código JavaScript en diferentes ((archivo))s no +satisface estos requisitos. Los archivos aún comparten el mismo espacio de +nombres global. Pueden, intencionalmente o accidentalmente, interferir con +las vinculaciones de cada uno. Y la estructura de dependencia sigue sin +estar clara. Podemos hacerlo mejor, como veremos más adelante en el capítulo. + +{{index design}} + +Diseñar una estructura de módulo ajustada para un programa puede ser difícil. +En la fase en la que todavía estás explorando el problema, intentando +cosas diferentes para ver que funciona, es posible que desees no preocuparte +demasiado por eso, ya que puede ser una gran distracción. Una vez que tengas +algo que se sienta sólido, es un buen momento para dar un paso atrás y +organizarlo. + +## Paquetes + +{{index bug, dependency, structure, reuse}} + +Una de las ventajas de construir un programa a partir de piezas separadas, +y ser capaces de ejecutar esas piezas por si mismas, es que tú +podrías ser capaz de aplicar la misma pieza en diferentes programas. + +{{index "parseINI function"}} + +Pero cómo se configura esto? Digamos que quiero usar la función `analizarINI` +del [Capítulo 9](regexp#ini) en otro programa. Si está claro de qué +depende la función (en este caso, nada), puedo copiar todo el +código necesario en mi nuevo proyecto y usarlo. Pero luego, si encuentro un +error en ese código, probablemente lo solucione en el programa +en el que estoy trabajando en ese momento y me olvido de arreglarlo en el otro +programa. + +{{index duplication, "copy-paste programming"}} + +Una vez que comience a duplicar código, rápidamente te encontraras perdiendo +tiempo y energía moviendo las copias alrededor y manteniéndolas actualizadas. + +Ahí es donde los _((paquete))s_ entran. Un paquete es un pedazo de código que +puede ser distribuido (copiado e instalado). Puede contener uno o más +módulos, y tiene información acerca de qué otros paquetes depende. +Un paquete también suele venir con documentación que explica qué es +lo que hace, para que las personas que no lo escribieron todavía puedan hacer +uso de el. + +Cuando se encuentra un problema en un paquete, o se agrega una nueva +característica, el el paquete es actualizado. Ahora los programas que dependen +de él (que también pueden ser otros paquetes) pueden actualizar a la +nueva ((versión)). + +{{id modules_npm}} + +{{index installation, upgrading, "package manager", download, reuse}} + +Trabajar de esta manera requiere ((infraestructura)). Necesitamos un lugar para +almacenar y encontrar paquetes, y una forma conveniente de instalar y +actualizarlos. En el mundo de JavaScript, esta infraestructura es provista por +((NPM)) ([_npmjs.org_](https://npmjs.org)). + +NPM es dos cosas: un servicio en línea donde uno puede descargar (y +subir) paquetes, y un programa (incluido con Node.js) que te ayuda a +instalar y administrarlos. + +{{index "ini package"}} + +Al momento de escribir esto, hay más de medio millón de paquetes diferentes +disponibles en NPM. Una gran parte de ellos son basura, debería mencionar, +pero casi todos los paquetes útiles, disponibles públicamente, +se puede encontrar allí. Por ejemplo, un analizador de archivos INI, similar al +uno que construimos en el [Capítulo 9](regexp), está disponible bajo el +nombre de paquete `ini`. + +{{index "command line"}} + +En el [Capítulo 20](node) veremos cómo instalar dichos paquetes de forma local +utilizando el programa de línea de comandos `npm`. + +Tener paquetes de calidad disponibles para descargar es extremadamente valioso. +Significa que a menudo podemos evitar tener que reinventar un programa que cien +personas han escrito antes, y obtener una implementación sólida y bien probado +con solo presionar algunas teclas. + +{{index maintenance}} + +El software es barato de copiar, por lo que una vez lo haya escrito alguien, +distribuirlo a otras personas es un proceso eficiente. Pero escribirlo +en el primer lugar, _es_ trabajo y responder a las personas que han +encontrado problemas en el código, o que quieren proponer nuevas +características, es aún más trabajo. + +Por defecto, tu posees el ((copyright)) del código que escribes, y otras +personas solo pueden usarlo con tu permiso. Pero ya que algunas personas +son simplemente agradables, y porque la publicación de un buen software +puede ayudarte a hacerte un poco famoso entre los programadores, se publican +muchos paquetes bajo una ((licencia)) que explícitamente permite a otras +personas usarlos. + +La mayoría del código en ((NPM)) esta licenciado de esta manera. Algunas +licencias requieren que tu publiques también el código bajo la +misma licencia del paquete que estas usando. Otras son menos exigentes, +solo requieren que guardes la licencia con el código cuando lo distribuyas. +La comunidad de JavaScript principalmente usa ese último tipo de licencia. +Al usar paquetes de otras personas, asegúrete de conocer su licencia. + +## Módulos improvisados + +Hasta 2015, el lenguaje JavaScript no tenía un sistema de módulos incorporado. +Sin embargo, la gente había estado construyendo sistemas grandes en JavaScript +durante más de una década y ellos _necesitaban_ ((módulos)). + +{{index [function, scope]}} + +Así que diseñaron sus propios ((sistema de módulos)) arriba del lenguaje. +Puedes usar funciones de JavaScript para crear alcances locales, y +((objeto))s para representar las ((interfaces)) de los módulos. + +{{index "Date class", "weekDay module"}} + +Este es un módulo para ir entre los nombres de los días y números (como son +retornados por el método `getDay` de `Date`). Su interfaz consiste en +`diaDeLaSemana.nombre` y `diaDeLaSemana.numero`, y oculta su vinculación +local `nombres` dentro del alcance de una expresión de función que se invoca +inmediatamente. + +``` +const diaDeLaSemana = function() { + const nombres = ["Domingo", "Lunes", "Martes", "Miercoles", + "Jueves", "Viernes", "Sabado"]; + return { + nombre(numero) { return nombres[numero]; }, + numero(nombre) { return nombres.indexOf(nombre); } + }; +}(); + +console.log(diaDeLaSemana.nombre(diaDeLaSemana.numero("Domingo"))); +// → Domingo +``` + +{{index dependency}} + +Este estilo de módulos proporciona ((aislamiento)), hasta cierto punto, pero +no declara dependencias. En cambio, simplemente pone su ((interfaz)) en el +((alcance global)) y espera que sus dependencias, si hay alguna, hagan lo mismo. +Durante mucho tiempo, este fue el enfoque principal utilizado en la +programación web, pero ahora está mayormente obsoleto. + +Si queremos hacer que las relaciones de dependencia sean parte del código, +tendremos que tomar el control de las dependencias que deben ser cargadas. +Hacer eso requiere que seamos capaces de ejecutar strings como código. +JavaScript puede hacer esto. + +{{id eval}} + +## Evaluando datos como código + +{{index evaluation, interpretation}} + +Hay varias maneras de tomar datos (un string de código) y ejecutarlos como +una parte del programa actual. + +{{index isolation, eval}} + +La forma más obvia es usar el operador especial `eval`, que ejecuta un +string en el ((alcance)) _actual_. Esto usualmente es una mala idea +porque rompe algunas de las propiedades que normalmente tienen los alcances, +tal como fácilmente predecir a qué vinculación se refiere un nombre dado. + +``` +const x = 1; +function evaluarYRetornarX(codigo) { + eval(codigo); + return x; +} + +console.log(evaluarYRetornarX("var x = 2")); +// → 2 +``` + +{{index "Function constructor"}} + +Una forma menos aterradora de interpretar datos como código es usar el +constructor `Function`. Este toma dos argumentos: un string que contiene una +lista de nombres de argumentos separados por comas y un string que contiene el +cuerpo de la función. + +``` +let masUno = Function("n", "return n + 1;"); +console.log(masUno(4)); +// → 5 +``` + +Esto es precisamente lo que necesitamos para un sistema de módulos. Podemos +envolver el código del módulo en una función y usar el alcance de esa función +como el ((alcance)) del módulo. + +## CommonJS + +{{id commonjs}} + +{{index "CommonJS modules"}} + +El enfoque más utilizado para incluir módulos en JavaScript es +llamado _módulos CommonJS_. ((Node.js)) lo usa, y es el sistema utilizado +por la mayoría de los paquetes en ((NPM)). + +{{index "require function"}} + +El concepto principal en los módulos CommonJS es una función llamada `require` +("requerir"). Cuando la llamas con el nombre del módulo de una dependencia, +esta se asegura de que el módulo sea cargado y retorna su ((interfaz)). + +{{index "exports object"}} + +Debido a que el cargador envuelve el código del módulo en una función, los +módulos obtienen automáticamente su propio alcance local. Todo lo que tienen que +hacer es llamar a `require` para acceder a sus dependencias, y poner su +interfaz en el objeto vinculado a `exports` ("exportaciones"). + +{{index "formatDate module", "Date class", "ordinal package", "date-names package"}} + +Este módulo de ejemplo proporciona una función de formateo de fecha. Utiliza dos +((paquete))s de NPM—`ordinal` para convertir números a strings como +`"1st"` y `"2nd"`, y `date-names` para obtener los nombres en inglés de los +días de la semana y meses. Este exporta una sola función, `formatDate`, que +toma un objeto `Date` y un string ((plantilla)). + +El string de plantilla puede contener códigos que dirigen el formato, como +`YYYY` para todo el año y `Do` para el día ordinal del mes. +Podrías darle un string como `"MMMM Do YYYY"` para obtener resultados como +"November 22nd 2017". + +``` +const ordinal = require("ordinal"); +const {days, months} = require("date-names"); + +exports.formatDate = function(date, format) { + return format.replace(/YYYY|M(MMM)?|Do?|dddd/g, tag => { + if (tag == "YYYY") return date.getFullYear(); + if (tag == "M") return date.getMonth(); + if (tag == "MMMM") return months[date.getMonth()]; + if (tag == "D") return date.getDate(); + if (tag == "Do") return ordinal(date.getDate()); + if (tag == "dddd") return days[date.getDay()]; + }); +}; +``` + +{{index "destructuring binding"}} + +La interfaz de `ordinal` es una función única, mientras que `date-names` +exporta un objeto que contiene varias cosas—los dos valores que usamos son +arrays de nombres. La desestructuración es muy conveniente cuando creamos +vinculaciones para interfaces importadas. + +El módulo agrega su función de interfaz a `exports`, de modo que los módulos +que dependen de el tengan acceso a el. Podríamos usar el módulo de esta +manera: + +``` +const {formatDate} = require("./format-date"); + +console.log(formatDate(new Date(2017, 9, 13), + "dddd the Do")); +// → Friday the 13th +``` + +{{index "require function", "CommonJS modules", "readFile function"}} + +{{id require}} + +Podemos definir `require`, en su forma más mínima, así: + +```{test: wrap, sandbox: require} +require.cache = Object.create(null); + +function require(nombre) { + if (!(nombre in require.cache)) { + let codigo = leerArchivo(nombre); + let modulo = {exportaciones: {}}; + require.cache[nombre] = modulo; + let envolvedor = Function("require, exportaciones, modulo", codigo); + envolvedor(require, modulo.exportaciones, modulo); + } + return require.cache[nombre].exportaciones; +} +``` + +En este código, `leerArchivo` es una función inventada que lee un archivo y +retorna su contenido como un string. El estándar de JavaScript no ofrece tal +funcionalidad—pero diferentes entornos de JavaScript, como el +navegador y Node.js, proporcionan sus propias formas de acceder a ((archivos)). +El ejemplo solo pretende que `leerArchivo` existe. + +{{index cache, "Function constructor"}} + +Para evitar cargar el mismo módulo varias veces, `require` mantiene un +(caché) almacenado de módulos que ya han sido cargados. Cuando se llama, +primero verifica si el módulo solicitado ya ha sido cargado y, si no, lo carga. +Esto implica leer el código del módulo, envolverlo en una función y +llamárla. + +{{index "ordinal package", "exports object", "module object"}} + +La ((interfaz)) del paquete `ordinal` que vimos antes no es un +objeto, sino una función. Una peculiaridad de los módulos CommonJS es que, +aunque el sistema de módulos creará un objeto de interfaz vacío para ti +(vinculado a `exports`), puedes reemplazarlo con cualquier valor al +sobrescribir `module.exports`. Esto lo hacen muchos módulos para exportar un +valor único en lugar de un objeto de interfaz. + +Al definir `require`, `exportaciones` y `modulo` como ((parametros)) para +la función de envoltura generada (y pasando los valores apropiados +al llamarla), el cargador se asegura de que estas vinculaciones esten +disponibles en el ((alcance)) del módulo. + +{{index resolution, "relative path"}} + +La forma en que el string dado a `require` se traduce a un nombre de archivo +real o dirección web difiere en diferentes sistemas. Cuando comienza con +`"./"` o `"../"`, generalmente se interpreta como relativo al +nombre del archivo actual. Entonces `"./format-date"` sería el archivo +llamado `format-date.js` en el mismo directorio. + +Cuando el nombre no es relativo, Node.js buscará por un paquete instalado +con ese nombre. En el código de ejemplo de este capítulo, +interpretaremos esos nombres como referencias a paquetes de NPM. +Entraremos en más detalles sobre cómo instalar y usar los módulos de NPM en +el [Capítulo 20](node). + +{{id modules_ini}} + +{{index "ini package"}} + +Ahora, en lugar de escribir nuestro propio analizador de archivos INI, +podemos usar uno de ((NPM)): + +``` +const {parse} = require("ini"); + +console.log(parse("x = 10\ny = 20")); +// → {x: "10", y: "20"} +``` + +## Módulos ECMAScript + +Los ((módulos CommonJS)) funcionan bastante bien y, en combinación con NPM, +han permitido que la comunidad de JavaScript comience a compartir +código en una gran escala. + +{{index "exports object", linter}} + +Pero siguen siendo un poco de un ((truco)) con cinta adhesiva. La ((notación)) es +ligeramente incomoda—las cosas que agregas a `exports` no están disponibles en +el ((alcance)) local, por ejemplo. Y ya que `require` es una llamada de función +normal tomando cualquier tipo de argumento, no solo un string literal, +puede ser difícil de determinar las dependencias de un módulo sin +correr su código primero. + +{{index "import keyword", dependency, "ES modules"}} + +{{id es}} + +Esta es la razón por la cual el estándar de JavaScript introdujo su propio, +sistema de módulos diferente a partir de 2015. Por lo general es llamado +_((módulos ES))_, donde _ES_ significa ((ECMAScript)). Los principales +conceptos de dependencias e interfaces siguen siendo los mismos, +pero los detalles difieren. Por un lado, +la notación está ahora integrada en el lenguaje. En lugar de llamar a una +función para acceder a una dependencia, utilizas una palabra clave +`import` ("importar") especial. + +``` +import ordinal from "ordinal"; +import {days, months} from "date-names"; + +export function formatDate(date, format) { /* ... */ } +``` + +{{index "export keyword", "formatDate module"}} + +Similarmente, la palabra clave `export` se usa para exportar cosas. Puede +aparecer delante de una función, clase o definición de vinculación (`let`, +`const`, o `var`). + +La interfaz de un módulo ES no es un valor único, sino un conjunto de +((vinculaciones)) con nombres. El módulo anterior vincula `formatDate` +a una función. Cuando importas desde otro módulo, importas la _vinculación_, +no el valor, lo que significa que un módulo exportado puede cambiar el +valor de la vinculación en cualquier momento, y que los módulos que la importen +verán su nuevo valor. + +{{index "default export"}} + +Cuando hay una vinculación llamada `default`, esta se trata como el +principal valor del módulo exportado. Si importas un módulo como `ordinal` en +el ejemplo, sin llaves alrededor del nombre de la vinculación, obtienes su +vinculación `default`. Dichos módulos aún pueden exportar otras vinculaciones +bajo diferentes nombres ademas de su exportación por `default`. + +Para crear una exportación por default, escribe `export default` antes de una +expresión, una declaración de función o una declaración de clase. + +``` +export default ["Invierno", "Primavera", "Verano", "Otoño"]; +``` + +Es posible renombrar la vinculación importada usando la palabra `as` ("como"). + +``` +import {days as nombresDias} from "date-names"; + +console.log(nombresDias.length); +// → 7 +``` + +Al momento de escribir esto, la comunidad de JavaScript está en proceso de +adoptar este estilo de módulos. Pero ha sido un proceso lento. Tomó +algunos años, después de que se haya especificado el formato paraq que los +navegadores y Node.js comenzaran a soportarlo. Y a pesar de que lo soportan +mayormente ahora, este soporte todavía tiene problemas, y la discusión sobre +cómo dichos módulos deberían distribuirse a través de ((NPM)) todavía está en +curso. + +Muchos proyectos se escriben usando módulos ES y luego se convierten +automáticamente a algún otro formato cuando son publicados. Estamos en +período de transición en el que se utilizan dos sistemas de módulos diferentes +uno al lado del otro, y es útil poder leer y escribir código en +cualquiera de ellos. + +## Construyendo y empaquetando + +{{index compilation, "type checking"}} + +De hecho, muchos proyectos de JavaScript ni siquiera están, técnicamente, +escritos en JavaScript. Hay extensiones, como el ((dialecto)) de comprobación +de tipos mencionado en el [Capítulo 7](error#typing), que son ampliamente +usados. Las personas también suelen comenzar a usar extensiones planificadas +para el lenguaje mucho antes de que estas hayan sido agregadas a las plataformas +que realmente corren JavaScript. + +Para que esto sea posible, ellos _compilan_ su código, traduciéndolo del +dialecto de JavaScript que eligieron a JavaScript simple y antiguo—o incluso a +una versión anterior de JavaScript, para que ((navegadores)) antiguos puedan +ejecutarlo. + +{{index latency, performance}} + +Incluir un programa modular que consiste de 200 archivos diferentes +en una ((página web)) produce sus propios problemas. Si buscar un +solo ((archivo)) sobre la ((red)) tarda 50 milisegundos, cargar +todo el programa tardaria 10 segundos, o tal vez la mitad si puedes +cargar varios archivos simultáneamente. Eso es mucho tiempo perdido. +Ya que buscar un solo archivo grande tiende a ser más rápido que buscar muchos +archivos pequeños, los programadores web han comenzado a usar herramientas que +convierten sus programas (los cuales cuidadosamente estan dividos en módulos) +de nuevo en un único archivo grande antes de publicarlo en la Web. Tales +herramientas son llamado _((empaquetadores))_. + +{{index "file size"}} + +Y podemos ir más allá. Además de la cantidad de archivos, el _tamaño_ de +los archivos también determina qué tan rápido se pueden transferir a través de la +red. Por lo tanto, la comunidad de JavaScript ha inventado _((minificadores))_. +Estas son herramientas que toman un programa de JavaScript y lo hacen más pequeño +al eliminar automáticamente los comentarios y espacios en blanco, cambia el nombre +de las vinculaciones, y reemplaza piezas de código con código equivalente +que ocupa menos espacio. + +{{index pipeline, tool}} + +Por lo tanto, no es raro que el código que encuentres en un paquete de NPM o +que se ejecute en una página web haya pasado por _multiples_ etapas de +transformación: conversión de JavaScript moderno a JavaScript histórico, +del formato de módulos ES a CommonJS, empaquetado y minificado. +No vamos a entrar en los detalles de estas herramientas en este libro, ya que +tienden a ser aburridos y cambian rápidamente. Solo ten en cuenta que +el código JavaScript que ejecutas a menudo no es el código tal y como fue +escrito. + +## Diseño de módulos + +{{index [module, design], interface, "code structure"}} + +La estructuración de programas es uno de los aspectos más sutiles de la +programación. Cualquier pieza de funcionalidad no trivial se puede modelar de +varias maneras. + +Un buen diseño de programa es subjetivo—hay ventajas/desventajas involucradas, y +cuestiones de gusto. La mejor manera de aprender el valor de una buena +estructura de diseño es leer o trabajar en muchos programas y notar lo que +funciona y lo qué no. No asumas que un desastroso doloroso es +"solo la forma en que las cosas son ". Puedes mejorar la estructura de casi +todo al ponerle mas pensamiento. + +Un aspecto del diseño de módulos es la facilidad de uso. Si estás diseñando +algo que está destinado a ser utilizado por varias personas—o incluso por +ti mismo, en tres meses cuando ya no recuerdes los detalles de +lo que hiciste—es útil si tu ((interfaz)) es simple y predicible. + +{{index "ini package", JSON}} + +Eso puede significar seguir convenciones existentes. Un buen ejemplo es el +paquete `ini`. Este módulo imita el objeto estándar `JSON` al +proporcionar las funciones `parse` y `stringify` (para escribir un archivo INI), +y, como `JSON`, convierte entre strings y objetos simples. Entonces +la interfaz es pequeña y familiar, y después de haber trabajado con ella una +vez, es probable que recuerdes cómo usarla. + +{{index "side effect", "hard disk", composability}} + +Incluso si no hay una función estándar o un paquete ampliamente utilizado para +imitar, puedes mantener tus módulos predecibles mediante el uso de +estructuras de datos simples y haciendo una cosa única y enfocada. Muchos de +los módulos de análisis de archivos INI en NPM proporcionan una función que +lee directamente tal archivo del disco duro y lo analiza, por ejemplo. Esto +hace que sea imposible de usar tales módulos en el navegador, donde no tenemos +acceso directo al sistema de archivos, y agrega una complejidad que habría sido +mejor abordada al _componer_ el módulo con alguna función de lectura de +archivos. + +{{index "pure function"}} + +Lo que apunta a otro aspecto útil del diseño de módulos—la facilidad con la +qué algo se puede componer con otro código. Módulos enfocados que +que computan valores son aplicables en una gama más amplia de programas +que módulos mas grandes que realizan acciones complicadas con efectos +secundarios. Un lector de archivos INI que insista en leer el archivo desde el +disco es inútil en un escenario donde el contenido del archivo provenga de +alguna otra fuente. + +{{index "object-oriented programming"}} + +Relacionadamente, los objetos con estado son a veces útiles e incluso necesarios, +pero si se puede hacer algo con una función, usa una función. Varios +de los lectores de archivos INI en NPM proporcionan un estilo de interfaz que +requiere que primero debes crear un objeto, luego cargar el archivo en tu objeto, +y finalmente usar métodos especializados para obtener los resultados. Este tipo +de cosas es común en la tradición orientada a objetos, y es terrible. +En lugar de hacer una sola llamada de función y seguir adelante, +tienes que realizar el ritual de mover tu objeto a través de diversos +estados. Y ya que los datos ahora están envueltos en un objeto de +tipo especializado, todo el código que interactúa con él tiene que saber +sobre ese tipo, creando interdependencias innecesarias. + +A menudo no se puede evitar la definición de nuevas estructuras de datos—solo +unas pocas básicas son provistos por el estándar de lenguaje, y muchos +tipos de datos tienen que ser más complejos que un ((array)) o un mapa. +Pero cuando el array es suficiente, usa un array. + +Un ejemplo de una estructura de datos un poco más compleja es el grafo de el +[Capítulo 7](robot). No hay una sola manera obvia de representar un +((grafo)) en JavaScript. En ese capítulo, usamos un objeto cuya propiedades +contenian arrays de strings—los otros nodos accesibles desde ese +nodo. + +Hay varios paquetes de busqueda de rutas diferentes en ((NPM)), pero ninguno +de ellos usa este formato de grafo. Por lo general, estos permiten que los +bordes del grafo tengan un peso, el costo o la distancia asociada a ellos, +lo que no es posible en nuestra representación. + +{{index "Dijkstra, Edsger", pathfinding, "Dijkstra's algorithm", "dijkstrajs package"}} + +Por ejemplo, está el paquete `dijkstrajs`. Un enfoque bien conocido +par la busqueda de rutas, bastante similar a nuestra función `encontrarRuta`, +se llama el _algoritmo de Dijkstra_, después de Edsger Dijkstra, quien fue +el primero que lo escribió. El sufijo `js` a menudo se agrega a los nombres de +los paquetes para indicar el hecho de que están escritos en JavaScript. +Este paquete `dijkstrajs` utiliza un formato de grafo similar al nuestro, +pero en lugar de arrays, utiliza objetos cuyos valores de propiedad son +números—los pesos de los bordes. + +Si quisiéramos usar ese paquete, tendríamos que asegurarnos de que nuestro +grafo fue almacenado en el formato que este espera. + +``` +const {find_path} = require("dijkstrajs"); + +let grafo = {}; +for (let node of Object.keys(roadGraph)) { + let edges = graph[node] = {}; + for (let dest of roadGraph[node]) { + edges[dest] = 1; + } +} + +console.log(find_path(grafo, "Oficina de Correos", "Cabaña")); +// → ["Oficina de Correos", "Casa de Alice", "Cabaña"] +``` + +Esto puede ser una barrera para la composición—cuando varios paquetes están +usando diferentes estructuras de datos para describir cosas similares, +combinarlos es difícil. Por lo tanto, si deseas diseñar para la compibilidad, +averigua qué ((estructura de datos)) están usando otras personas y, cuando +sea posible, sigue su ejemplo. + +## Resumen + +Los módulos proporcionan de estructura a programas más grandes al separar el +código en piezas con interfaces y dependencias claras. La interfaz es +la parte del módulo que es visible desde otros módulos, y +las dependencias son los otros módulos este que utiliza. + +Debido a que históricamente JavaScript no proporcionó un sistema de módulos, +el sistema CommonJS fue construido encima. Entonces, en algún momento, +_consiguio_ un sistema incorporado, que ahora coexiste incomodamente +con el sistema CommonJS. + +Un paquete es una porción de código que se puede distribuir por sí misma. NPM +es un repositorio de paquetes de JavaScript. Puedes descargar todo tipo de +paquetes útiles (e inútiles) de él. + +## Ejercicios + +### Un robot modular + +{{index "modular robot (exercise)", module, robot, NPM}} + +{{id modular_robot}} + +Estas son las vinculaciones que el proyecto del [Capítulo 7](robot) crea: + +```{lang: "text/plain"} +caminos +construirGrafo +grafoCamino +EstadoPueblo +correrRobot +eleccionAleatoria +robotAleatorio +rutaCorreo +robotRuta +encontrarRuta +robotOrientadoAMetas +``` + +Si tuvieras que escribir ese proyecto como un programa modular, qué módulos +crearías? Qué módulo dependería de qué otro módulo, y +cómo se verían sus interfaces? + +Qué piezas es probable que estén disponibles pre-escritas en NPM? +Preferirias usar un paquete de NPM o escribirlas tu mismo? + +{{hint + +{{index "modular robot (exercise)"}} + +Aqui esta lo que habría hecho (pero, una vez más, no hay una sola forma +_correcta_ de diseñar un módulo dado): + +{{index "dijkstrajs package"}} + +El código usado para construir el camino de grafo vive en el módulo `grafo`. +Ya que prefiero usar `dijkstrajs` de NPM en lugar de nuestro propio código de busqueda +de rutas, haremos que este construya el tipo de datos de grafos que `dijkstajs` +espera. Este módulo exporta una sola función, `construirGrafo`. Haria que +`construirGrafo` acepte un array de arrays de dos elementos, en lugar de +strings que contengan guiones, para hacer que el módulo sea menos +dependiente del formato de entrada. + +El módulo `caminos` contiene los datos en bruto del camino (el array `caminos`) +y la vinculación `grafoCamino`. Este módulo depende de `./grafo` y exporta +el grafo del camino. + +{{index "random-item package"}} + +La clase `EstadoPueblo` vive en el módulo `estado`. Depende del +módulo `./caminos`, porque necesita poder verificar que un +camino dado existe. También necesita `eleccionAleatoria`. Dado que eso es una +función de tres líneas, podríamos simplemente ponerla en el módulo `estado` +como una función auxiliar interna. Pero `robotAleatorio` también la necesita. +Entonces tendriamos que duplicarla o ponerla en su propio módulo. +Dado que esta función existe en NPM en el paquete `random-item`, una buena +solución es hacer que ambos módulos dependan de el. Podemos agregar +la función `correrRobot` a este módulo también, ya que es pequeña y +estrechamente relacionada con la gestión de estado. El módulo exporta tanto +la clase `EstadoPueblo` como la función `correrRobot`. + +Finalmente, los robots, junto con los valores de los que dependen, como +`mailRoute`, podrían ir en un módulo `robots-ejemplo`, que depende +de `./caminos` y exporta las funciones de robot. Para que sea posible +que el `robotOrientadoAMetas` haga busqueda de rutas, este módulo también +depende de `dijkstrajs`. + +Al descargar algo de trabajo a los módulos de ((NPM)), el código se volvió un +poco mas pequeño. Cada módulo individual hace algo bastante simple, y puede +ser leído por sí mismo. La división del código en módulos también sugiere +a menudo otras mejoras para el diseño del programa. En este caso, parece un +poco extraño que `EstadoPueblo` y los robots dependan de un grafo de caminos. +Podría ser una mejor idea hacer del grafo un argumento para +el constructor del estado y hacer que los robots lo lean del objeto +estado—esto reduce las dependencias (lo que siempre es bueno) y hace +posible ejecutar simulaciones en diferentes mapas (lo cual es aún mejor). + +Es una buena idea usar módulos de NPM para cosas que podríamos haber +escrito nosotros mismos? En principio, sí—para cosas no triviales como la +función de busqueda de rutas es probable que cometas errores y pierdas el tiempo +escribiendola tú mismo. Para pequeñas funciones como `eleccionAleatoria`, +escribirla por ti mismo es lo suficiente fácil. Pero agregarlas +donde las necesites tiende a desordenar tus módulos. + +Sin embargo, tampoco debes subestimar el trabajo involucrado en +_encontrar_ un paquete apropiado de NPM. E incluso si encuentras uno, este +podría no funcionar bien o faltarle alguna característica que necesitas. +Ademas de eso, depender de los paquetes de NPM, significa que debes asegurarte +de que están instalados, tienes que distribuirlos con tu +programa, y podrías tener que actualizarlos periódicamente. + +Entonces, de nuevo, esta es una solución con compromisos, y tu puedes decidir de +una u otra manera dependiendo sobre cuánto te ayuden los paquetes. + +hint}} + +### Módulo de Caminos + +{{index "roads module (exercise)"}} + +Escribe un ((módulo CommonJS)), basado en el ejemplo del [Capítulo 7](robot), +que contenga el array de caminos y exporte la estructura de datos +grafo que los representa como `grafoCamino`. Debería depender de un +modulo `./grafo`, que exporta una función `construirGrafo` que se usa +para construir el grafo. Esta función espera un array de arrays de +dos elementos (los puntos de inicio y final de los caminos). + +{{if interactive + +```{test: no} +// Añadir dependencias y exportaciones + +const caminos = [ + "Casa de Alicia-Casa de Bob", "Casa de Alicia-Cabaña", + "Casa de Alicia-Oficina de Correos", "Casa de Bob-Ayuntamiento", + "Casa de Daria-Casa de Ernie", "Casa de Daria-Ayuntamiento", + "Casa de Ernie-Casa de Grete", "Casa de Grete-Granja", + "Casa de Grete-Tienda", "Mercado-Granja", + "Mercado-Oficina de Correos", "Mercado-Tienda", + "Mercado-Ayuntamiento", "Tienda-Ayuntamiento" +]; +``` + +if}} + +{{hint + +{{index "roads module (exercise)", "destructuring binding", "exports object"}} + +Como este es un ((módulo CommonJS)), debes usar `require` para +importar el módulo grafo. Eso fue descrito como exportar una +función `construirGrafo`, que puedes sacar de su objeto de interfaz +con una declaración `const` de desestructuración. + +Para exportar `grafoCamino`, agrega una propiedad al objeto `exports`. +Ya que `construirGrafo` toma una estructura de datos que no empareja +precisamente `caminos`, la división de los strings de los caminis +debe ocurrir en tu módulo. + +hint}} + +### Dependencias circulares + +{{index dependency, "circular dependency", "require function"}} + +Una dependencia circular es una situación en donde el módulo A depende de B, y +B también, directa o indirectamente, depende de A. Muchos sistemas de módulos +simplemente prohíbne esto porque cualquiera que sea el orden que elijas +para cargar tales módulos, no puedes asegurarse de que las dependencias de +cada módulo han sido cargadas antes de que se ejecuten. + +Los ((modulos CommonJS)) permiten una forma limitada de dependencias cíclicas. +Siempre que los módulos no reemplacen a su objeto `exports` predeterminado, y +no accedan a la interfaz de las demás hasta que terminen de cargar, +las dependencias cíclicas están bien. + +La función `require` dada [anteriormente en este capítulo](modulos#require) +es compatible con este tipo de ciclo de dependencias. Puedes +ver cómo maneja los ciclos? Qué iría mal cuando un módulo en un +ciclo _reemplace_ su objeto `exports` por defecto? + +{{hint + +{{index overriding, "circular dependency", "exports object"}} + +El truco es que `require` agrega módulos a su caché _antes_ de +comenzar a cargar el módulo. De esa forma, si se realiza una llamada `require` +mientras está ejecutando el intento de cargarlo, ya es conocido y la +interfaz actual sera retornada, en lugar de comenzar a cargar el módulo +una vez más (lo que eventualmente desbordaría la pila). + +Si un módulo sobrescribe su valor `module.exports`, cualquier otro módulo +que haya recibido su valor de interfaz antes de que termine de cargarse +ha conseguido el objeto de interfaz predeterminado (que es probable que este +vacío), en lugar del valor de interfaz previsto. + +hint}} diff --git a/11_async.md b/11_async.md index 2302a12c1..7d028beb7 100644 --- a/11_async.md +++ b/11_async.md @@ -466,7 +466,7 @@ por vencida. {{index "Promise class", "callback function", interface}} -Y, como hemos establecido que las promesas son algo bueno, tambien haremos +Y, como hemos establecido que las promesas son algo bueno, también haremos que nuestra función de solicitud retorne una promesa. En términos de lo que pueden expresar, las devoluciones de llamada y las promesas son equivalentes. Las funciones basadas en devoluciones de llamadas se pueden From 1147d3a631b5268859b5ed38bda6cf6d47714a98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Iv=C3=A1n=20Patricio=20Moreno?= Date: Fri, 6 May 2022 22:30:53 -0500 Subject: [PATCH 2/7] avances en lenguaje --- 12_language.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/12_language.md b/12_language.md index e09488cb5..afa4be9c5 100644 --- a/12_language.md +++ b/12_language.md @@ -268,20 +268,19 @@ console.log(analizar("+(a, 10)")); {{index "error message"}} -Funciona! No nos da información muy útil cuando falla +¡Funciona! No nos da información muy útil cuando falla y no almacena la línea y la columna en que comienza cada expresión, lo que podría ser útil al informar errores más tarde, pero es lo suficientemente bueno para nuestros propósitos. -## The evaluator +## El evaluador -{{index "evaluate function", evaluation, interpretation, "syntax tree", "Egg language"}} +{{index "evaluar función", evaluación, interpretación, "árbol sintáctico", "lenguaje huevo"}} -What can we do with the syntax tree for a program? Run it, of course! -And that is what the evaluator does. You give it a syntax tree and a -scope object that associates names with values, and it will evaluate -the expression that the tree represents and return the value that this -produces. +¿Qué poddemos hacer son el árrbol sintáctico de un programa? ¡Correrlo, por supuesto! +Y eso es lo que el evaluador hace. Le das un árbol sintáctico y un objeto de ámbito +que asocie nombres con valores, y evaluará la expresión que el árbol representa y regresará +el valor que el árbol produce. ```{includeCode: true} const specialForms = Object.create(null); @@ -313,8 +312,9 @@ function evaluate(expresion, scope) { } ``` -{{index "literal expression", scope}} +{{index "expresión literal", ámbito}} +El evaluador tiene código para cada uno de los tipos de ((exprresión)). The evaluator has code for each of the ((expression)) types. A literal value expression produces its value. (For example, the expression `100` just evaluates to the number 100.) For a binding, we must check From 92893b7d3315e4bd6f1c658d17ae29b4c7e61943 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Iv=C3=A1n=20Patricio=20Moreno?= Date: Sat, 7 May 2022 15:14:52 -0500 Subject: [PATCH 3/7] Advancing chapter 12 --- 10_modules_original.md | 864 ----------------------------------------- 12_language.md | 12 +- 2 files changed, 6 insertions(+), 870 deletions(-) delete mode 100644 10_modules_original.md diff --git a/10_modules_original.md b/10_modules_original.md deleted file mode 100644 index 58cf6f584..000000000 --- a/10_modules_original.md +++ /dev/null @@ -1,864 +0,0 @@ -{{meta {load_files: ["code/packages_chapter_10.js", "code/chapter/07_robot.js"]}}} - -# Módulos - -{{quote {author: "Tef", title: "Programming is Terrible", chapter: true} - -Escriba código que sea fácil de borrar, no fácil de extender. - -quote}} - -{{index "Yuan-Ma", "Book of Programming"}} - -{{figure {url: "img/chapter_picture_10.jpg", alt: "Picture of a building built from modular pieces", chapter: framed}}} - -{{index organization, "code structure"}} - -El programa ideal tiene una estructura cristalina. La forma en que funciona es -fácil de explicar, y cada parte juega un papel bien definido. - -{{index "organic growth"}} - -Un típico programa real crece orgánicamente. Nuevas piezas de funcionalidad -se agregan a medida que surgen nuevas necesidades. Estructurar—y preservar la -estructura—es trabajo adicional, trabajo que solo valdra la pena en el -futuro, la _siguiente_ vez que alguien trabaje en el programa. Así que es -tentador descuidarlo, y permitir que las partes del programa se vuelvan -profundamente enredadas. - -{{index readability, reuse, isolation}} - -Esto causa dos problemas prácticos. En primer lugar, entender tal sistema -es difícil. Si todo puede tocar todo lo demás, es difícil ver a cualquier -pieza dada de forma aislada. Estas obligado a construir un -entendimiento holístico de todo el asunto. En segundo lugar, si quieres -usar cualquiera de las funcionalidades de dicho programa en otra situación, -reescribirla podria resultar más fácil que tratar de desenredarla de su -contexto. - -El término "((gran bola de barro))" se usa a menudo para tales programas -grandes, sin estructura. Todo se mantiene pegado, y cuando intentas -sacar una pieza, todo se desarma y tus manos se ensucian. - -## Módulos - -{{index dependency}} - -Los _módulos_ son un intento de evitar estos problemas. Un ((módulo)) es una -pieza del programa que especifica en qué otras piezas este depende ( -sus _dependencias_) y qué funcionalidad proporciona para que otros módulos -usen (su _((interfaz))_). - -{{index "big ball of mud"}} - -Las interfaces de los módulos tienen mucho en común con las interfaces de -objetos, como las vimos en el [Capítulo 6](objeto#interface). Estas hacen parte -del módulo disponible para el mundo exterior y mantienen el resto privado. Al -restringir las formas en que los módulos interactúan entre sí, el -el sistema se parece más a un juego de ((LEGOS)), donde las piezas interactúan -a través de conectores bien definidos, y menos como barro, donde todo se mezcla -con todo. - -{{index dependency}} - -Las relaciones entre los módulos se llaman _dependencias_. Cuando un módulo -necesita una pieza de otro módulo, se dice que depende de ese -módulo. Cuando este hecho está claramente especificado en el módulo en sí, -puede usarse para descubrir qué otros módulos deben estar presentes para poder -ser capaces de usar un módulo dado y cargar dependencias automáticamente. - -Para separar módulos de esa manera, cada uno necesita su propio alcance privado. - -Simplemente poner todo tu código JavaScript en diferentes ((archivo))s no -satisface estos requisitos. Los archivos aún comparten el mismo espacio de -nombres global. Pueden, intencionalmente o accidentalmente, interferir con -las vinculaciones de cada uno. Y la estructura de dependencia sigue sin -estar clara. Podemos hacerlo mejor, como veremos más adelante en el capítulo. - -{{index design}} - -Diseñar una estructura de módulo ajustada para un programa puede ser difícil. -En la fase en la que todavía estás explorando el problema, intentando -cosas diferentes para ver que funciona, es posible que desees no preocuparte -demasiado por eso, ya que puede ser una gran distracción. Una vez que tengas -algo que se sienta sólido, es un buen momento para dar un paso atrás y -organizarlo. - -## Paquetes - -{{index bug, dependency, structure, reuse}} - -Una de las ventajas de construir un programa a partir de piezas separadas, -y ser capaces de ejecutar esas piezas por si mismas, es que tú -podrías ser capaz de aplicar la misma pieza en diferentes programas. - -{{index "parseINI function"}} - -Pero cómo se configura esto? Digamos que quiero usar la función `analizarINI` -del [Capítulo 9](regexp#ini) en otro programa. Si está claro de qué -depende la función (en este caso, nada), puedo copiar todo el -código necesario en mi nuevo proyecto y usarlo. Pero luego, si encuentro un -error en ese código, probablemente lo solucione en el programa -en el que estoy trabajando en ese momento y me olvido de arreglarlo en el otro -programa. - -{{index duplication, "copy-paste programming"}} - -Una vez que comience a duplicar código, rápidamente te encontraras perdiendo -tiempo y energía moviendo las copias alrededor y manteniéndolas actualizadas. - -Ahí es donde los _((paquete))s_ entran. Un paquete es un pedazo de código que -puede ser distribuido (copiado e instalado). Puede contener uno o más -módulos, y tiene información acerca de qué otros paquetes depende. -Un paquete también suele venir con documentación que explica qué es -lo que hace, para que las personas que no lo escribieron todavía puedan hacer -uso de el. - -Cuando se encuentra un problema en un paquete, o se agrega una nueva -característica, el el paquete es actualizado. Ahora los programas que dependen -de él (que también pueden ser otros paquetes) pueden actualizar a la -nueva ((versión)). - -{{id modules_npm}} - -{{index installation, upgrading, "package manager", download, reuse}} - -Trabajar de esta manera requiere ((infraestructura)). Necesitamos un lugar para -almacenar y encontrar paquetes, y una forma conveniente de instalar y -actualizarlos. En el mundo de JavaScript, esta infraestructura es provista por -((NPM)) ([_npmjs.org_](https://npmjs.org)). - -NPM es dos cosas: un servicio en línea donde uno puede descargar (y -subir) paquetes, y un programa (incluido con Node.js) que te ayuda a -instalar y administrarlos. - -{{index "ini package"}} - -Al momento de escribir esto, hay más de medio millón de paquetes diferentes -disponibles en NPM. Una gran parte de ellos son basura, debería mencionar, -pero casi todos los paquetes útiles, disponibles públicamente, -se puede encontrar allí. Por ejemplo, un analizador de archivos INI, similar al -uno que construimos en el [Capítulo 9](regexp), está disponible bajo el -nombre de paquete `ini`. - -{{index "command line"}} - -En el [Capítulo 20](node) veremos cómo instalar dichos paquetes de forma local -utilizando el programa de línea de comandos `npm`. - -Tener paquetes de calidad disponibles para descargar es extremadamente valioso. -Significa que a menudo podemos evitar tener que reinventar un programa que cien -personas han escrito antes, y obtener una implementación sólida y bien probado -con solo presionar algunas teclas. - -{{index maintenance}} - -El software es barato de copiar, por lo que una vez lo haya escrito alguien, -distribuirlo a otras personas es un proceso eficiente. Pero escribirlo -en el primer lugar, _es_ trabajo y responder a las personas que han -encontrado problemas en el código, o que quieren proponer nuevas -características, es aún más trabajo. - -Por defecto, tu posees el ((copyright)) del código que escribes, y otras -personas solo pueden usarlo con tu permiso. Pero ya que algunas personas -son simplemente agradables, y porque la publicación de un buen software -puede ayudarte a hacerte un poco famoso entre los programadores, se publican -muchos paquetes bajo una ((licencia)) que explícitamente permite a otras -personas usarlos. - -La mayoría del código en ((NPM)) esta licenciado de esta manera. Algunas -licencias requieren que tu publiques también el código bajo la -misma licencia del paquete que estas usando. Otras son menos exigentes, -solo requieren que guardes la licencia con el código cuando lo distribuyas. -La comunidad de JavaScript principalmente usa ese último tipo de licencia. -Al usar paquetes de otras personas, asegúrete de conocer su licencia. - -## Módulos improvisados - -Hasta 2015, el lenguaje JavaScript no tenía un sistema de módulos incorporado. -Sin embargo, la gente había estado construyendo sistemas grandes en JavaScript -durante más de una década y ellos _necesitaban_ ((módulos)). - -{{index [function, scope]}} - -Así que diseñaron sus propios ((sistema de módulos)) arriba del lenguaje. -Puedes usar funciones de JavaScript para crear alcances locales, y -((objeto))s para representar las ((interfaces)) de los módulos. - -{{index "Date class", "weekDay module"}} - -Este es un módulo para ir entre los nombres de los días y números (como son -retornados por el método `getDay` de `Date`). Su interfaz consiste en -`diaDeLaSemana.nombre` y `diaDeLaSemana.numero`, y oculta su vinculación -local `nombres` dentro del alcance de una expresión de función que se invoca -inmediatamente. - -``` -const diaDeLaSemana = function() { - const nombres = ["Domingo", "Lunes", "Martes", "Miercoles", - "Jueves", "Viernes", "Sabado"]; - return { - nombre(numero) { return nombres[numero]; }, - numero(nombre) { return nombres.indexOf(nombre); } - }; -}(); - -console.log(diaDeLaSemana.nombre(diaDeLaSemana.numero("Domingo"))); -// → Domingo -``` - -{{index dependency}} - -Este estilo de módulos proporciona ((aislamiento)), hasta cierto punto, pero -no declara dependencias. En cambio, simplemente pone su ((interfaz)) en el -((alcance global)) y espera que sus dependencias, si hay alguna, hagan lo mismo. -Durante mucho tiempo, este fue el enfoque principal utilizado en la -programación web, pero ahora está mayormente obsoleto. - -Si queremos hacer que las relaciones de dependencia sean parte del código, -tendremos que tomar el control de las dependencias que deben ser cargadas. -Hacer eso requiere que seamos capaces de ejecutar strings como código. -JavaScript puede hacer esto. - -{{id eval}} - -## Evaluando datos como código - -{{index evaluation, interpretation}} - -Hay varias maneras de tomar datos (un string de código) y ejecutarlos como -una parte del programa actual. - -{{index isolation, eval}} - -La forma más obvia es usar el operador especial `eval`, que ejecuta un -string en el ((alcance)) _actual_. Esto usualmente es una mala idea -porque rompe algunas de las propiedades que normalmente tienen los alcances, -tal como fácilmente predecir a qué vinculación se refiere un nombre dado. - -``` -const x = 1; -function evaluarYRetornarX(codigo) { - eval(codigo); - return x; -} - -console.log(evaluarYRetornarX("var x = 2")); -// → 2 -``` - -{{index "Function constructor"}} - -Una forma menos aterradora de interpretar datos como código es usar el -constructor `Function`. Este toma dos argumentos: un string que contiene una -lista de nombres de argumentos separados por comas y un string que contiene el -cuerpo de la función. - -``` -let masUno = Function("n", "return n + 1;"); -console.log(masUno(4)); -// → 5 -``` - -Esto es precisamente lo que necesitamos para un sistema de módulos. Podemos -envolver el código del módulo en una función y usar el alcance de esa función -como el ((alcance)) del módulo. - -## CommonJS - -{{id commonjs}} - -{{index "CommonJS modules"}} - -El enfoque más utilizado para incluir módulos en JavaScript es -llamado _módulos CommonJS_. ((Node.js)) lo usa, y es el sistema utilizado -por la mayoría de los paquetes en ((NPM)). - -{{index "require function"}} - -El concepto principal en los módulos CommonJS es una función llamada `require` -("requerir"). Cuando la llamas con el nombre del módulo de una dependencia, -esta se asegura de que el módulo sea cargado y retorna su ((interfaz)). - -{{index "exports object"}} - -Debido a que el cargador envuelve el código del módulo en una función, los -módulos obtienen automáticamente su propio alcance local. Todo lo que tienen que -hacer es llamar a `require` para acceder a sus dependencias, y poner su -interfaz en el objeto vinculado a `exports` ("exportaciones"). - -{{index "formatDate module", "Date class", "ordinal package", "date-names package"}} - -Este módulo de ejemplo proporciona una función de formateo de fecha. Utiliza dos -((paquete))s de NPM—`ordinal` para convertir números a strings como -`"1st"` y `"2nd"`, y `date-names` para obtener los nombres en inglés de los -días de la semana y meses. Este exporta una sola función, `formatDate`, que -toma un objeto `Date` y un string ((plantilla)). - -El string de plantilla puede contener códigos que dirigen el formato, como -`YYYY` para todo el año y `Do` para el día ordinal del mes. -Podrías darle un string como `"MMMM Do YYYY"` para obtener resultados como -"November 22nd 2017". - -``` -const ordinal = require("ordinal"); -const {days, months} = require("date-names"); - -exports.formatDate = function(date, format) { - return format.replace(/YYYY|M(MMM)?|Do?|dddd/g, tag => { - if (tag == "YYYY") return date.getFullYear(); - if (tag == "M") return date.getMonth(); - if (tag == "MMMM") return months[date.getMonth()]; - if (tag == "D") return date.getDate(); - if (tag == "Do") return ordinal(date.getDate()); - if (tag == "dddd") return days[date.getDay()]; - }); -}; -``` - -{{index "destructuring binding"}} - -La interfaz de `ordinal` es una función única, mientras que `date-names` -exporta un objeto que contiene varias cosas—los dos valores que usamos son -arrays de nombres. La desestructuración es muy conveniente cuando creamos -vinculaciones para interfaces importadas. - -El módulo agrega su función de interfaz a `exports`, de modo que los módulos -que dependen de el tengan acceso a el. Podríamos usar el módulo de esta -manera: - -``` -const {formatDate} = require("./format-date"); - -console.log(formatDate(new Date(2017, 9, 13), - "dddd the Do")); -// → Friday the 13th -``` - -{{index "require function", "CommonJS modules", "readFile function"}} - -{{id require}} - -Podemos definir `require`, en su forma más mínima, así: - -```{test: wrap, sandbox: require} -require.cache = Object.create(null); - -function require(nombre) { - if (!(nombre in require.cache)) { - let codigo = leerArchivo(nombre); - let modulo = {exportaciones: {}}; - require.cache[nombre] = modulo; - let envolvedor = Function("require, exportaciones, modulo", codigo); - envolvedor(require, modulo.exportaciones, modulo); - } - return require.cache[nombre].exportaciones; -} -``` - -En este código, `leerArchivo` es una función inventada que lee un archivo y -retorna su contenido como un string. El estándar de JavaScript no ofrece tal -funcionalidad—pero diferentes entornos de JavaScript, como el -navegador y Node.js, proporcionan sus propias formas de acceder a ((archivos)). -El ejemplo solo pretende que `leerArchivo` existe. - -{{index cache, "Function constructor"}} - -Para evitar cargar el mismo módulo varias veces, `require` mantiene un -(caché) almacenado de módulos que ya han sido cargados. Cuando se llama, -primero verifica si el módulo solicitado ya ha sido cargado y, si no, lo carga. -Esto implica leer el código del módulo, envolverlo en una función y -llamárla. - -{{index "ordinal package", "exports object", "module object"}} - -La ((interfaz)) del paquete `ordinal` que vimos antes no es un -objeto, sino una función. Una peculiaridad de los módulos CommonJS es que, -aunque el sistema de módulos creará un objeto de interfaz vacío para ti -(vinculado a `exports`), puedes reemplazarlo con cualquier valor al -sobrescribir `module.exports`. Esto lo hacen muchos módulos para exportar un -valor único en lugar de un objeto de interfaz. - -Al definir `require`, `exportaciones` y `modulo` como ((parametros)) para -la función de envoltura generada (y pasando los valores apropiados -al llamarla), el cargador se asegura de que estas vinculaciones esten -disponibles en el ((alcance)) del módulo. - -{{index resolution, "relative path"}} - -La forma en que el string dado a `require` se traduce a un nombre de archivo -real o dirección web difiere en diferentes sistemas. Cuando comienza con -`"./"` o `"../"`, generalmente se interpreta como relativo al -nombre del archivo actual. Entonces `"./format-date"` sería el archivo -llamado `format-date.js` en el mismo directorio. - -Cuando el nombre no es relativo, Node.js buscará por un paquete instalado -con ese nombre. En el código de ejemplo de este capítulo, -interpretaremos esos nombres como referencias a paquetes de NPM. -Entraremos en más detalles sobre cómo instalar y usar los módulos de NPM en -el [Capítulo 20](node). - -{{id modules_ini}} - -{{index "ini package"}} - -Ahora, en lugar de escribir nuestro propio analizador de archivos INI, -podemos usar uno de ((NPM)): - -``` -const {parse} = require("ini"); - -console.log(parse("x = 10\ny = 20")); -// → {x: "10", y: "20"} -``` - -## Módulos ECMAScript - -Los ((módulos CommonJS)) funcionan bastante bien y, en combinación con NPM, -han permitido que la comunidad de JavaScript comience a compartir -código en una gran escala. - -{{index "exports object", linter}} - -Pero siguen siendo un poco de un ((truco)) con cinta adhesiva. La ((notación)) es -ligeramente incomoda—las cosas que agregas a `exports` no están disponibles en -el ((alcance)) local, por ejemplo. Y ya que `require` es una llamada de función -normal tomando cualquier tipo de argumento, no solo un string literal, -puede ser difícil de determinar las dependencias de un módulo sin -correr su código primero. - -{{index "import keyword", dependency, "ES modules"}} - -{{id es}} - -Esta es la razón por la cual el estándar de JavaScript introdujo su propio, -sistema de módulos diferente a partir de 2015. Por lo general es llamado -_((módulos ES))_, donde _ES_ significa ((ECMAScript)). Los principales -conceptos de dependencias e interfaces siguen siendo los mismos, -pero los detalles difieren. Por un lado, -la notación está ahora integrada en el lenguaje. En lugar de llamar a una -función para acceder a una dependencia, utilizas una palabra clave -`import` ("importar") especial. - -``` -import ordinal from "ordinal"; -import {days, months} from "date-names"; - -export function formatDate(date, format) { /* ... */ } -``` - -{{index "export keyword", "formatDate module"}} - -Similarmente, la palabra clave `export` se usa para exportar cosas. Puede -aparecer delante de una función, clase o definición de vinculación (`let`, -`const`, o `var`). - -La interfaz de un módulo ES no es un valor único, sino un conjunto de -((vinculaciones)) con nombres. El módulo anterior vincula `formatDate` -a una función. Cuando importas desde otro módulo, importas la _vinculación_, -no el valor, lo que significa que un módulo exportado puede cambiar el -valor de la vinculación en cualquier momento, y que los módulos que la importen -verán su nuevo valor. - -{{index "default export"}} - -Cuando hay una vinculación llamada `default`, esta se trata como el -principal valor del módulo exportado. Si importas un módulo como `ordinal` en -el ejemplo, sin llaves alrededor del nombre de la vinculación, obtienes su -vinculación `default`. Dichos módulos aún pueden exportar otras vinculaciones -bajo diferentes nombres ademas de su exportación por `default`. - -Para crear una exportación por default, escribe `export default` antes de una -expresión, una declaración de función o una declaración de clase. - -``` -export default ["Invierno", "Primavera", "Verano", "Otoño"]; -``` - -Es posible renombrar la vinculación importada usando la palabra `as` ("como"). - -``` -import {days as nombresDias} from "date-names"; - -console.log(nombresDias.length); -// → 7 -``` - -Al momento de escribir esto, la comunidad de JavaScript está en proceso de -adoptar este estilo de módulos. Pero ha sido un proceso lento. Tomó -algunos años, después de que se haya especificado el formato paraq que los -navegadores y Node.js comenzaran a soportarlo. Y a pesar de que lo soportan -mayormente ahora, este soporte todavía tiene problemas, y la discusión sobre -cómo dichos módulos deberían distribuirse a través de ((NPM)) todavía está en -curso. - -Muchos proyectos se escriben usando módulos ES y luego se convierten -automáticamente a algún otro formato cuando son publicados. Estamos en -período de transición en el que se utilizan dos sistemas de módulos diferentes -uno al lado del otro, y es útil poder leer y escribir código en -cualquiera de ellos. - -## Construyendo y empaquetando - -{{index compilation, "type checking"}} - -De hecho, muchos proyectos de JavaScript ni siquiera están, técnicamente, -escritos en JavaScript. Hay extensiones, como el ((dialecto)) de comprobación -de tipos mencionado en el [Capítulo 7](error#typing), que son ampliamente -usados. Las personas también suelen comenzar a usar extensiones planificadas -para el lenguaje mucho antes de que estas hayan sido agregadas a las plataformas -que realmente corren JavaScript. - -Para que esto sea posible, ellos _compilan_ su código, traduciéndolo del -dialecto de JavaScript que eligieron a JavaScript simple y antiguo—o incluso a -una versión anterior de JavaScript, para que ((navegadores)) antiguos puedan -ejecutarlo. - -{{index latency, performance}} - -Incluir un programa modular que consiste de 200 archivos diferentes -en una ((página web)) produce sus propios problemas. Si buscar un -solo ((archivo)) sobre la ((red)) tarda 50 milisegundos, cargar -todo el programa tardaria 10 segundos, o tal vez la mitad si puedes -cargar varios archivos simultáneamente. Eso es mucho tiempo perdido. -Ya que buscar un solo archivo grande tiende a ser más rápido que buscar muchos -archivos pequeños, los programadores web han comenzado a usar herramientas que -convierten sus programas (los cuales cuidadosamente estan dividos en módulos) -de nuevo en un único archivo grande antes de publicarlo en la Web. Tales -herramientas son llamado _((empaquetadores))_. - -{{index "file size"}} - -Y podemos ir más allá. Además de la cantidad de archivos, el _tamaño_ de -los archivos también determina qué tan rápido se pueden transferir a través de la -red. Por lo tanto, la comunidad de JavaScript ha inventado _((minificadores))_. -Estas son herramientas que toman un programa de JavaScript y lo hacen más pequeño -al eliminar automáticamente los comentarios y espacios en blanco, cambia el nombre -de las vinculaciones, y reemplaza piezas de código con código equivalente -que ocupa menos espacio. - -{{index pipeline, tool}} - -Por lo tanto, no es raro que el código que encuentres en un paquete de NPM o -que se ejecute en una página web haya pasado por _multiples_ etapas de -transformación: conversión de JavaScript moderno a JavaScript histórico, -del formato de módulos ES a CommonJS, empaquetado y minificado. -No vamos a entrar en los detalles de estas herramientas en este libro, ya que -tienden a ser aburridos y cambian rápidamente. Solo ten en cuenta que -el código JavaScript que ejecutas a menudo no es el código tal y como fue -escrito. - -## Diseño de módulos - -{{index [module, design], interface, "code structure"}} - -La estructuración de programas es uno de los aspectos más sutiles de la -programación. Cualquier pieza de funcionalidad no trivial se puede modelar de -varias maneras. - -Un buen diseño de programa es subjetivo—hay ventajas/desventajas involucradas, y -cuestiones de gusto. La mejor manera de aprender el valor de una buena -estructura de diseño es leer o trabajar en muchos programas y notar lo que -funciona y lo qué no. No asumas que un desastroso doloroso es -"solo la forma en que las cosas son ". Puedes mejorar la estructura de casi -todo al ponerle mas pensamiento. - -Un aspecto del diseño de módulos es la facilidad de uso. Si estás diseñando -algo que está destinado a ser utilizado por varias personas—o incluso por -ti mismo, en tres meses cuando ya no recuerdes los detalles de -lo que hiciste—es útil si tu ((interfaz)) es simple y predicible. - -{{index "ini package", JSON}} - -Eso puede significar seguir convenciones existentes. Un buen ejemplo es el -paquete `ini`. Este módulo imita el objeto estándar `JSON` al -proporcionar las funciones `parse` y `stringify` (para escribir un archivo INI), -y, como `JSON`, convierte entre strings y objetos simples. Entonces -la interfaz es pequeña y familiar, y después de haber trabajado con ella una -vez, es probable que recuerdes cómo usarla. - -{{index "side effect", "hard disk", composability}} - -Incluso si no hay una función estándar o un paquete ampliamente utilizado para -imitar, puedes mantener tus módulos predecibles mediante el uso de -estructuras de datos simples y haciendo una cosa única y enfocada. Muchos de -los módulos de análisis de archivos INI en NPM proporcionan una función que -lee directamente tal archivo del disco duro y lo analiza, por ejemplo. Esto -hace que sea imposible de usar tales módulos en el navegador, donde no tenemos -acceso directo al sistema de archivos, y agrega una complejidad que habría sido -mejor abordada al _componer_ el módulo con alguna función de lectura de -archivos. - -{{index "pure function"}} - -Lo que apunta a otro aspecto útil del diseño de módulos—la facilidad con la -qué algo se puede componer con otro código. Módulos enfocados que -que computan valores son aplicables en una gama más amplia de programas -que módulos mas grandes que realizan acciones complicadas con efectos -secundarios. Un lector de archivos INI que insista en leer el archivo desde el -disco es inútil en un escenario donde el contenido del archivo provenga de -alguna otra fuente. - -{{index "object-oriented programming"}} - -Relacionadamente, los objetos con estado son a veces útiles e incluso necesarios, -pero si se puede hacer algo con una función, usa una función. Varios -de los lectores de archivos INI en NPM proporcionan un estilo de interfaz que -requiere que primero debes crear un objeto, luego cargar el archivo en tu objeto, -y finalmente usar métodos especializados para obtener los resultados. Este tipo -de cosas es común en la tradición orientada a objetos, y es terrible. -En lugar de hacer una sola llamada de función y seguir adelante, -tienes que realizar el ritual de mover tu objeto a través de diversos -estados. Y ya que los datos ahora están envueltos en un objeto de -tipo especializado, todo el código que interactúa con él tiene que saber -sobre ese tipo, creando interdependencias innecesarias. - -A menudo no se puede evitar la definición de nuevas estructuras de datos—solo -unas pocas básicas son provistos por el estándar de lenguaje, y muchos -tipos de datos tienen que ser más complejos que un ((array)) o un mapa. -Pero cuando el array es suficiente, usa un array. - -Un ejemplo de una estructura de datos un poco más compleja es el grafo de el -[Capítulo 7](robot). No hay una sola manera obvia de representar un -((grafo)) en JavaScript. En ese capítulo, usamos un objeto cuya propiedades -contenian arrays de strings—los otros nodos accesibles desde ese -nodo. - -Hay varios paquetes de busqueda de rutas diferentes en ((NPM)), pero ninguno -de ellos usa este formato de grafo. Por lo general, estos permiten que los -bordes del grafo tengan un peso, el costo o la distancia asociada a ellos, -lo que no es posible en nuestra representación. - -{{index "Dijkstra, Edsger", pathfinding, "Dijkstra's algorithm", "dijkstrajs package"}} - -Por ejemplo, está el paquete `dijkstrajs`. Un enfoque bien conocido -par la busqueda de rutas, bastante similar a nuestra función `encontrarRuta`, -se llama el _algoritmo de Dijkstra_, después de Edsger Dijkstra, quien fue -el primero que lo escribió. El sufijo `js` a menudo se agrega a los nombres de -los paquetes para indicar el hecho de que están escritos en JavaScript. -Este paquete `dijkstrajs` utiliza un formato de grafo similar al nuestro, -pero en lugar de arrays, utiliza objetos cuyos valores de propiedad son -números—los pesos de los bordes. - -Si quisiéramos usar ese paquete, tendríamos que asegurarnos de que nuestro -grafo fue almacenado en el formato que este espera. - -``` -const {find_path} = require("dijkstrajs"); - -let grafo = {}; -for (let node of Object.keys(roadGraph)) { - let edges = graph[node] = {}; - for (let dest of roadGraph[node]) { - edges[dest] = 1; - } -} - -console.log(find_path(grafo, "Oficina de Correos", "Cabaña")); -// → ["Oficina de Correos", "Casa de Alice", "Cabaña"] -``` - -Esto puede ser una barrera para la composición—cuando varios paquetes están -usando diferentes estructuras de datos para describir cosas similares, -combinarlos es difícil. Por lo tanto, si deseas diseñar para la compibilidad, -averigua qué ((estructura de datos)) están usando otras personas y, cuando -sea posible, sigue su ejemplo. - -## Resumen - -Los módulos proporcionan de estructura a programas más grandes al separar el -código en piezas con interfaces y dependencias claras. La interfaz es -la parte del módulo que es visible desde otros módulos, y -las dependencias son los otros módulos este que utiliza. - -Debido a que históricamente JavaScript no proporcionó un sistema de módulos, -el sistema CommonJS fue construido encima. Entonces, en algún momento, -_consiguio_ un sistema incorporado, que ahora coexiste incomodamente -con el sistema CommonJS. - -Un paquete es una porción de código que se puede distribuir por sí misma. NPM -es un repositorio de paquetes de JavaScript. Puedes descargar todo tipo de -paquetes útiles (e inútiles) de él. - -## Ejercicios - -### Un robot modular - -{{index "modular robot (exercise)", module, robot, NPM}} - -{{id modular_robot}} - -Estas son las vinculaciones que el proyecto del [Capítulo 7](robot) crea: - -```{lang: "text/plain"} -caminos -construirGrafo -grafoCamino -EstadoPueblo -correrRobot -eleccionAleatoria -robotAleatorio -rutaCorreo -robotRuta -encontrarRuta -robotOrientadoAMetas -``` - -Si tuvieras que escribir ese proyecto como un programa modular, qué módulos -crearías? Qué módulo dependería de qué otro módulo, y -cómo se verían sus interfaces? - -Qué piezas es probable que estén disponibles pre-escritas en NPM? -Preferirias usar un paquete de NPM o escribirlas tu mismo? - -{{hint - -{{index "modular robot (exercise)"}} - -Aqui esta lo que habría hecho (pero, una vez más, no hay una sola forma -_correcta_ de diseñar un módulo dado): - -{{index "dijkstrajs package"}} - -El código usado para construir el camino de grafo vive en el módulo `grafo`. -Ya que prefiero usar `dijkstrajs` de NPM en lugar de nuestro propio código de busqueda -de rutas, haremos que este construya el tipo de datos de grafos que `dijkstajs` -espera. Este módulo exporta una sola función, `construirGrafo`. Haria que -`construirGrafo` acepte un array de arrays de dos elementos, en lugar de -strings que contengan guiones, para hacer que el módulo sea menos -dependiente del formato de entrada. - -El módulo `caminos` contiene los datos en bruto del camino (el array `caminos`) -y la vinculación `grafoCamino`. Este módulo depende de `./grafo` y exporta -el grafo del camino. - -{{index "random-item package"}} - -La clase `EstadoPueblo` vive en el módulo `estado`. Depende del -módulo `./caminos`, porque necesita poder verificar que un -camino dado existe. También necesita `eleccionAleatoria`. Dado que eso es una -función de tres líneas, podríamos simplemente ponerla en el módulo `estado` -como una función auxiliar interna. Pero `robotAleatorio` también la necesita. -Entonces tendriamos que duplicarla o ponerla en su propio módulo. -Dado que esta función existe en NPM en el paquete `random-item`, una buena -solución es hacer que ambos módulos dependan de el. Podemos agregar -la función `correrRobot` a este módulo también, ya que es pequeña y -estrechamente relacionada con la gestión de estado. El módulo exporta tanto -la clase `EstadoPueblo` como la función `correrRobot`. - -Finalmente, los robots, junto con los valores de los que dependen, como -`mailRoute`, podrían ir en un módulo `robots-ejemplo`, que depende -de `./caminos` y exporta las funciones de robot. Para que sea posible -que el `robotOrientadoAMetas` haga busqueda de rutas, este módulo también -depende de `dijkstrajs`. - -Al descargar algo de trabajo a los módulos de ((NPM)), el código se volvió un -poco mas pequeño. Cada módulo individual hace algo bastante simple, y puede -ser leído por sí mismo. La división del código en módulos también sugiere -a menudo otras mejoras para el diseño del programa. En este caso, parece un -poco extraño que `EstadoPueblo` y los robots dependan de un grafo de caminos. -Podría ser una mejor idea hacer del grafo un argumento para -el constructor del estado y hacer que los robots lo lean del objeto -estado—esto reduce las dependencias (lo que siempre es bueno) y hace -posible ejecutar simulaciones en diferentes mapas (lo cual es aún mejor). - -Es una buena idea usar módulos de NPM para cosas que podríamos haber -escrito nosotros mismos? En principio, sí—para cosas no triviales como la -función de busqueda de rutas es probable que cometas errores y pierdas el tiempo -escribiendola tú mismo. Para pequeñas funciones como `eleccionAleatoria`, -escribirla por ti mismo es lo suficiente fácil. Pero agregarlas -donde las necesites tiende a desordenar tus módulos. - -Sin embargo, tampoco debes subestimar el trabajo involucrado en -_encontrar_ un paquete apropiado de NPM. E incluso si encuentras uno, este -podría no funcionar bien o faltarle alguna característica que necesitas. -Ademas de eso, depender de los paquetes de NPM, significa que debes asegurarte -de que están instalados, tienes que distribuirlos con tu -programa, y podrías tener que actualizarlos periódicamente. - -Entonces, de nuevo, esta es una solución con compromisos, y tu puedes decidir de -una u otra manera dependiendo sobre cuánto te ayuden los paquetes. - -hint}} - -### Módulo de Caminos - -{{index "roads module (exercise)"}} - -Escribe un ((módulo CommonJS)), basado en el ejemplo del [Capítulo 7](robot), -que contenga el array de caminos y exporte la estructura de datos -grafo que los representa como `grafoCamino`. Debería depender de un -modulo `./grafo`, que exporta una función `construirGrafo` que se usa -para construir el grafo. Esta función espera un array de arrays de -dos elementos (los puntos de inicio y final de los caminos). - -{{if interactive - -```{test: no} -// Añadir dependencias y exportaciones - -const caminos = [ - "Casa de Alicia-Casa de Bob", "Casa de Alicia-Cabaña", - "Casa de Alicia-Oficina de Correos", "Casa de Bob-Ayuntamiento", - "Casa de Daria-Casa de Ernie", "Casa de Daria-Ayuntamiento", - "Casa de Ernie-Casa de Grete", "Casa de Grete-Granja", - "Casa de Grete-Tienda", "Mercado-Granja", - "Mercado-Oficina de Correos", "Mercado-Tienda", - "Mercado-Ayuntamiento", "Tienda-Ayuntamiento" -]; -``` - -if}} - -{{hint - -{{index "roads module (exercise)", "destructuring binding", "exports object"}} - -Como este es un ((módulo CommonJS)), debes usar `require` para -importar el módulo grafo. Eso fue descrito como exportar una -función `construirGrafo`, que puedes sacar de su objeto de interfaz -con una declaración `const` de desestructuración. - -Para exportar `grafoCamino`, agrega una propiedad al objeto `exports`. -Ya que `construirGrafo` toma una estructura de datos que no empareja -precisamente `caminos`, la división de los strings de los caminis -debe ocurrir en tu módulo. - -hint}} - -### Dependencias circulares - -{{index dependency, "circular dependency", "require function"}} - -Una dependencia circular es una situación en donde el módulo A depende de B, y -B también, directa o indirectamente, depende de A. Muchos sistemas de módulos -simplemente prohíbne esto porque cualquiera que sea el orden que elijas -para cargar tales módulos, no puedes asegurarse de que las dependencias de -cada módulo han sido cargadas antes de que se ejecuten. - -Los ((modulos CommonJS)) permiten una forma limitada de dependencias cíclicas. -Siempre que los módulos no reemplacen a su objeto `exports` predeterminado, y -no accedan a la interfaz de las demás hasta que terminen de cargar, -las dependencias cíclicas están bien. - -La función `require` dada [anteriormente en este capítulo](modulos#require) -es compatible con este tipo de ciclo de dependencias. Puedes -ver cómo maneja los ciclos? Qué iría mal cuando un módulo en un -ciclo _reemplace_ su objeto `exports` por defecto? - -{{hint - -{{index overriding, "circular dependency", "exports object"}} - -El truco es que `require` agrega módulos a su caché _antes_ de -comenzar a cargar el módulo. De esa forma, si se realiza una llamada `require` -mientras está ejecutando el intento de cargarlo, ya es conocido y la -interfaz actual sera retornada, en lugar de comenzar a cargar el módulo -una vez más (lo que eventualmente desbordaría la pila). - -Si un módulo sobrescribe su valor `module.exports`, cualquier otro módulo -que haya recibido su valor de interfaz antes de que termine de cargarse -ha conseguido el objeto de interfaz predeterminado (que es probable que este -vacío), en lugar del valor de interfaz previsto. - -hint}} diff --git a/12_language.md b/12_language.md index afa4be9c5..a46777942 100644 --- a/12_language.md +++ b/12_language.md @@ -314,12 +314,10 @@ function evaluate(expresion, scope) { {{index "expresión literal", ámbito}} -El evaluador tiene código para cada uno de los tipos de ((exprresión)). -The evaluator has code for each of the ((expression)) types. A literal -value expression produces its value. (For example, the expression -`100` just evaluates to the number 100.) For a binding, we must check -whether it is actually defined in the scope and, if it is, fetch the -binding's value. +El evaluador tiene código para cada uno de los tipos de ((exprresión)). Un +expresión literal de un valor produce este valor. (Por ejemplo, la expresión `100` evalúa +al número 100.) Para un vínculo de valor, debemos verificar si está definido en el ámbito, +y si lo está, traer el valor vinculado. {{index [function, application]}} @@ -819,6 +817,7 @@ console.log(parse("a # one\n # two\n()")); // operator: {type: "word", name: "a"}, // args: []} ``` + if}} {{hint @@ -888,6 +887,7 @@ hacer(definir(x, 4), run(`set(quux, true)`); // → Some kind of ReferenceError ``` + if}} {{hint From 4eb039f347537de433d695b0111d8b15c25717a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Iv=C3=A1n=20Patricio=20Moreno?= Date: Sat, 7 May 2022 16:17:09 -0500 Subject: [PATCH 4/7] =?UTF-8?q?avanzando=20cap=C3=ADtulo=2012?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 12_language.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/12_language.md b/12_language.md index a46777942..71dff08a9 100644 --- a/12_language.md +++ b/12_language.md @@ -275,7 +275,7 @@ suficientemente bueno para nuestros propósitos. ## El evaluador -{{index "evaluar función", evaluación, interpretación, "árbol sintáctico", "lenguaje huevo"}} +{{index "evaluar función", evaluación, interpretación, "árbol sintáctico", "lenguaje de Egg"}} ¿Qué poddemos hacer son el árrbol sintáctico de un programa? ¡Correrlo, por supuesto! Y eso es lo que el evaluador hace. Le das un árbol sintáctico y un objeto de ámbito @@ -319,17 +319,17 @@ expresión literal de un valor produce este valor. (Por ejemplo, la expresión ` al número 100.) Para un vínculo de valor, debemos verificar si está definido en el ámbito, y si lo está, traer el valor vinculado. -{{index [function, application]}} +{{index [función, aplicación]}} -Applications are more involved. If they are a ((special form)), like -`if`, we do not evaluate anything and pass the argument expressions, -along with the scope, to the function that handles this form. If it is -a normal call, we evaluate the operator, verify that it is a function, -and call it with the evaluated arguments. +La apliación implica más cosas. Si son una ((forma especial)), como +`if`, no evaluamos nada y pasamos las expresiones argumento, junto con el +ámbito, a la función que maneja esta forma. Si es una llamada normal, evaluamos el operador, +verificamos que sea una función, y la llamamos con los argumentos evaluados. + +Usamos valores función planos de JavaScript para representar los valores función de Egg. +Regresaremos a esto [más tardde](language#egg_fun), cuando forma especial llamada +`fun` está definida. -We use plain JavaScript function values to represent Egg's function -values. We will come back to this [later](language#egg_fun), when the -special form called `fun` is defined. {{index readability, "evaluate function", recursion, parsing}} From 08b8a2d2e5a6b71d7de28ee8c17d52d16d88f5db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Iv=C3=A1n=20Patricio=20Moreno?= Date: Sat, 7 May 2022 18:25:00 -0500 Subject: [PATCH 5/7] Work on chapter 12 --- 12_language.md | 100 +++++++++++++++++++++++++------------------------ 1 file changed, 52 insertions(+), 48 deletions(-) diff --git a/12_language.md b/12_language.md index 71dff08a9..8798927c8 100644 --- a/12_language.md +++ b/12_language.md @@ -331,32 +331,34 @@ Regresaremos a esto [más tardde](language#egg_fun), cuando forma especial llama `fun` está definida. -{{index readability, "evaluate function", recursion, parsing}} +{{index legibilidad, "evaluar función", recursión, parseo}} -The recursive structure of `evaluate` resembles the similar structure -of the parser, and both mirror the structure of the language itself. -It would also be possible to integrate the parser with the evaluator -and evaluate during parsing, but splitting them up this way makes the -program clearer. +La estructura recursiva de `evaluate` se parece a la estructura del parser, y +ambos reflejan la estructura del lenguaje mismo. También sería posible +integrar el parser con el evaluador y evaluar durante el parseo, +pero separarlos de esta manera hace el programa más claro. -{{index "Egg language", interpretation}} -This is really all that is needed to interpret Egg. It is that simple. -But without defining a few special forms and adding some useful values -to the ((environment)), you can't do much with this language yet. +{{index "lenguaje Egg", interpretación}} -## Special forms +Esto es todo lo que se necesita para interpretar Egg. Es así de simple. +Pero si no definimos unas cuantas formas especiales y agregamos algunos +valores útiles para el ((entorno)), todavía no podemos hacer mucho con +el lenguaje. -{{index "special form", "specialForms object"}} +## Formas especiales + +{{index "formas especiales", "objeto specialForms"}} + +El objeto `specialForms` es usado para definir una sintaxis especial en +Egg. Asocia palabras con funciones que evalúan estas formas. Ahora mismo está +vacío. Agreguemos `if`. -The `specialForms` object is used to define special syntax in Egg. It -associates words with functions that evaluate such forms. It is -currently empty. Let's add `if`. ```{includeCode: true} -specialForms.si = (args, scope) => { +specialForms.if = (args, scope) => { if (args.length != 3) { - throw new SyntaxError("Wrong number of args to si"); + throw new SyntaxError("Wrong number of args to if"); } else if (evaluate(args[0], scope) !== false) { return evaluate(args[1], scope); } else { @@ -365,30 +367,30 @@ specialForms.si = (args, scope) => { }; ``` -{{index "conditional execution", "ternary operator", "?: operator", "conditional operator"}} +{{index "ejecución condicional", "operador ternario", "operador ?:", "operador condicional"}} + +El constructo `if` de Egg espera exactamente tres arrgumentos. Evaluará el primero, +y si el resultado no es `false`, evaluará el segundo. En otro caso, el +tercero será evaluado. Esta forma `if` es más parecidda al operaddor +ternario `?:` que al `if` if de JavaScript. Es una expresión, no una +sentencia, y produce un valor: el resultado del segundo o tercer argumento. -Egg's `si` construct expects exactly three arguments. It will evaluate -the first, and if the result isn't the value `false`, it will evaluate -the second. Otherwise, the third gets evaluated. This `if` form is -more similar to JavaScript's ternary `?:` operator than to -JavaScript's `if`. It is an expression, not a statement, and it -produces a value, namely the result of the second or third argument. +{{index Booleano}} -{{index Boolean}} +Egg también se diferencia de JavaScript en como maneja el valor de la +condición para `if`. No tratará cosas como cero o la cadena vacía como +falso, solamente el valor exacto `false`. -Egg also differs from JavaScript in how it handles the condition value -to `if`. It will not treat things like zero or the empty string as -false, only the precise value `false`. +{{index "evaluación en corto circuito"}} -{{index "short-circuit evaluation"}} +La razón por la que necesitamos representar `if` como una forma especial, +en vez de como una función regular, es que todos los argumentos de las +funciones son evaluados antes de que la función sea llamada, mientras +que el `if` debe evaluar solo el segundo _o_ el tercer argumento, +dependiendo del valor del primero. -The reason we need to represent `if` as a special form, rather than a -regular function, is that all arguments to functions are evaluated -before the function is called, whereas `if` should evaluate only -_either_ its second or its third argument, depending on the value of -the first. +La forma `while` es similar. -The `while` form is similar. ```{includeCode: true} specialForms.while = (args, scope) => { @@ -405,12 +407,13 @@ specialForms.while = (args, scope) => { }; ``` -Another basic building block is `hacer`, which executes all its arguments -from top to bottom. Its value is the value produced by the last -argument. +Otro bloque de construcción principal es `do`, que ejecuta todos sus argumentos +de arriba hacia abajo. Su valos es el valorr producido por el último +argumento. + ```{includeCode: true} -specialForms.hacer = (args, scope) => { +specialForms.do = (args, scope) => { let value = false; for (let arg of args) { value = evaluate(arg, scope); @@ -419,19 +422,20 @@ specialForms.hacer = (args, scope) => { }; ``` -{{index "= operator"}} +{{index "operador ="}} + +Para ser capaces de crear ((asignaciones)) y darle nuevos valores, +además necesitamos crear una forma llamada `define`. Espera una palabra +como prime argumento y una expresión producciendo un valor para asignar +a esa palabra como segundo argumento. Ya que `define`, como todo, +es una expresión, debe regresar un valor. Haremos que regrese el valor que +fue asignado (justo como el operador `=` de JavaScript). -To be able to create ((binding))s and give them new values, we also -create a form called `definir`. It expects a word as its first argument -and an expression producing the value to assign to that word as its -second argument. Since `definir`, like everything, is an expression, it -must return a value. We'll make it return the value that was assigned -(just like JavaScript's `=` operator). ```{includeCode: true} -specialForms.definir = (args, scope) => { +specialForms.define = (args, scope) => { if (args.length != 2 || args[0].type != "word") { - throw new SyntaxError("Incorrect use of definir"); + throw new SyntaxError("Incorrect use of define"); } let value = evaluate(args[1], scope); scope[args[0].name] = value; From 18ebd21f842bc18f8924ac326ece7094ab08ef86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Iv=C3=A1n=20Patricio=20Moreno?= Date: Mon, 9 May 2022 15:09:24 -0500 Subject: [PATCH 6/7] El entorno --- 12_language.md | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/12_language.md b/12_language.md index 8798927c8..5f6b5a23c 100644 --- a/12_language.md +++ b/12_language.md @@ -196,7 +196,7 @@ JavaScript no válido. {{index "parseApply function"}} -Luego cortamos la parte que coincidio del string del programa y +Luego cortamos la parte que coincidió del string del programa y pasamos eso, junto con el objeto para la expresión, a `aplicarAnalisis`, el cual verifica si la expresión es una aplicación. Si es así, analiza una lista de los argumentos entre paréntesis. @@ -443,9 +443,9 @@ specialForms.define = (args, scope) => { }; ``` -## The environment +## El entorno -{{index "Egg language", "evaluate function"}} +{{index "lenguaje Egg", "evaluar función"}} The ((scope)) accepted by `evaluate` is an object with properties whose names correspond to binding names and whose values correspond to @@ -572,22 +572,22 @@ specialForms.fun = (args, scope) => { }; ``` -{{index "local scope"}} +{{index "ámbito local"}} -Functions in Egg get their own local scope. The function produced by -the `fun` form creates this local scope and adds the argument bindings -to it. It then evaluates the function body in this scope and returns -the result. +Las funciones en Egg tienen su propio ámbito local. La función producida +por la forma `fun` crea este entorno local y agrega las asociaciones de +argumentos a este. Entonces evalúa el cuerpo de la función en este ámbito +y regresa el resultado. ```{startCode: true} run(` -hacer(definir(plusOne, fun(a, +(a, 1))), +do(define(plusOne, fun(a, +(a, 1))), imprimir(plusOne(10))) `); // → 11 run(` -hacer(definir(pow, fun(base, exp, +do(define(pow, fun(base, exp, si(==(exp, 0), 1, *(base, pow(base, -(exp, 1)))))), @@ -713,14 +713,14 @@ topScope.length = "..."; topScope.element = "..."; run(` -hacer(definir(sum, fun(array, - hacer(definir(i, 0), - definir(sum, 0), +do(define(sum, fun(array, + do(define(i, 0), + define(sum, 0), while(<(i, length(array)), - hacer(definir(sum, +(sum, element(array, i))), - definir(i, +(i, 1)))), + do(define(sum, +(sum, element(array, i))), + define(i, +(i, 1)))), sum))), - imprimir(sum(array(1, 2, 3)))) + print(sum(array(1, 2, 3)))) `); // → 6 ``` @@ -758,8 +758,8 @@ binding `a`. ``` run(` -hacer(definir(f, fun(a, fun(b, +(a, b)))), - imprimir(f(4)(5))) +do(define(f, fun(a, fun(b, +(a, b)))), + print(f(4)(5))) `); // → 9 ``` From f58a736d26cef27d045c86e20603afc5d61d61f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Iv=C3=A1n=20Patricio=20Moreno?= Date: Sat, 14 May 2022 22:03:07 -0500 Subject: [PATCH 7/7] 65% of chapteerr 12 --- 12_language.md | 174 +++++++++++++++++++++++++------------------------ 1 file changed, 90 insertions(+), 84 deletions(-) diff --git a/12_language.md b/12_language.md index 5f6b5a23c..80d0ae3e6 100644 --- a/12_language.md +++ b/12_language.md @@ -447,15 +447,14 @@ specialForms.define = (args, scope) => { {{index "lenguaje Egg", "evaluar función"}} -The ((scope)) accepted by `evaluate` is an object with properties -whose names correspond to binding names and whose values correspond to -the values those ((binding))s are bound to. Let's define an object to -represent the ((global scope)). +El ((ámbito)) aceptado por `evaluate` es un objeto con propiedades +de las que sus nombres corresponden a los nombres de las vinculaciones en el ámbito +y sus valores corresnponden a los valores de esos vínculos. Ahora definamos un objeto +para representar el ((ámbito global)). -To be able to use the `if` construct we just defined, we must have -access to ((Boolean)) values. Since there are only two Boolean values, -we do not need special syntax for them. We simply bind two names to -the values `true` and `false` and use those. +Para poder usar el constructo `if` que acabamos de definir, necesitamos acceder +a valores ((Booleano))s. Como solo hay dos valores Booleanos, no necesitamos una sintaxis +especial. Simplemente asociaremos dos nombres a los valores `true` y `false` y usaremos estos nombres. ```{includeCode: true} const topScope = Object.create(null); @@ -464,21 +463,21 @@ topScope.true = true; topScope.false = false; ``` -We can now evaluate a simple expression that negates a Boolean value. +Podemos evaluar una expresión simple que niegue un Booleano. ``` -let prog = parse(`si(true, false, true)`); +let prog = parse(`if(true, false, true)`); console.log(evaluate(prog, topScope)); // → false ``` -{{index arithmetic, "Function constructor"}} +{{index aritmética, "constructor de Función"}} + +Para proveer la ((aritmética)) y ((operador))es de ((comparación)), +necesitamos además añadir algunas funciones al ((ámbito)). Para mantener +el código peequeño, usaremos `Function` para sintetizar varias +funciones de operador en un ciclo, en vez de definirlas individualmente. -To supply basic ((arithmetic)) and ((comparison)) ((operator))s, we -will also add some function values to the ((scope)). In the interest -of keeping the code short, we'll use `Function` to synthesize a bunch -of operator functions in a loop, instead of defining them -individually. ```{includeCode: true} for (let op of ["+", "-", "*", "/", "==", "<", ">"]) { @@ -486,65 +485,69 @@ for (let op of ["+", "-", "*", "/", "==", "<", ">"]) { } ``` -A way to ((output)) values is also very useful, so we'll wrap -`console.log` in a function and call it `imprimir`. +Tener una manera de ((devolver)) valores tambbién es muy útil, +así que envolveremos `console.log` en una función y la llamaremos +`print`. ```{includeCode: true} -topScope.imprimir = value => { +topScope.print = value => { console.log(value); return value; }; ``` -{{index parsing, "run function"}} +{{index parseo, "función run"}} + +Esto nos da suficientes herramientas elementales para escribir +programas simples. La siguiente función provee una forma conveniente +de leer un programa y correrlo en un ámbito nuevo. -That gives us enough elementary tools to write simple programs. The -following function provides a convenient way to parse a program and -run it in a fresh scope. ```{includeCode: true} -function run(programa) { - return evaluate(parse(programa), Object.create(topScope)); +function run(program) { + return evaluate(parse(program), Object.create(topScope)); } ``` -{{index "Object.create function", prototype}} +{{index "función Object.create", prototipo}} -We'll use object prototype chains to represent nested scopes, so that -the program can add bindings to its local scope without changing the -top-level scope. +Usareomos las cadenas de prototipos de los objetos para representar +ámbitos anidados, para que el programa pueda añadir asignaciones a su +ámbito local sin cambiar el ámbito superior. ``` run(` -hacer(definir(total, 0), - definir(count, 1), +do(define(total, 0), + define(count, 1), while(<(count, 11), - hacer(definir(total, +(total, count)), - definir(count, +(count, 1)))), - imprimir(total)) + do(define(total, +(total, count)), + define(count, +(count, 1)))), + print(total)) `); // → 55 ``` -{{index "summing example", "Egg language"}} +{{index "ejemplo de suma", "lenguaje Egg"}} + +Este es el programa que hemos visto varias veces antes, que calcula la suma +de los números del 1 a 10, expresado en Egg. Es claramente más +feo que su equivalente en JavaScript, peror no está tan mal para +un lenguaje implementado en menos de 150 ((líneas de código)). -This is the program we've seen several times before, which computes -the sum of the numbers 1 to 10, expressed in Egg. It is clearly uglier -than the equivalent JavaScript program—but not bad for a language -implemented in less than 150 ((lines of code)). {{id egg_fun}} -## Functions +## Funciones + +{{index función, "lenguaje Egg"}} -{{index function, "Egg language"}} +Un lenguaje de programación sin funciones es un lenguaje de +programación pobre, en efecto. -A programming language without functions is a poor programming -language indeed. +Afortunadamente, no es difícil agregar un constructo `fun`, +que trate su último argumento como el cuerpo de la función y use +todos los argumentos antes de ese como los parámetros de la función. -Fortunately, it isn't hard to add a `fun` construct, which treats its -last argument as the function's body and uses all arguments before -that as the names of the function's parameters. ```{includeCode: true} specialForms.fun = (args, scope) => { @@ -596,56 +599,59 @@ do(define(pow, fun(base, exp, // → 1024 ``` -## Compilation +## Compilación -{{index interpretation, compilation}} +{{index interpretación, compilación}} -What we have built is an interpreter. During evaluation, it acts -directly on the representation of the program produced by the parser. +Lo que hemos construido es un intérprete. Durante la evaluación, actúa +directamente en la representación del programa producido por el +parseador. -{{index efficiency, performance}} -_Compilation_ is the process of adding another step between the -parsing and the running of a program, which transforms the program -into something that can be evaluated more efficiently by doing as much -work as possible in advance. For example, in well-designed languages -it is obvious, for each use of a ((binding)), which binding is being -referred to, without actually running the program. This can be used to -avoid looking up the binding by name every time it is accessed, -instead directly fetching it from some predetermined ((memory)) -location. +{{index eficiencia, rendimiento}} -Traditionally, ((compilation)) involves converting the program to -((machine code)), the raw format that a computer's processor can -execute. But any process that converts a program to a different -representation can be thought of as compilation. +La _Compilación_ es el proceso de añadir otro paso entre la lectura +del programa y su ejecución, que transforma el programa en algo que +pueda ser evaluado más eficientemente, haciendo la mayor cantidad +de trabajo posible por adelantado. Por ejemplo, en lenguajes bien +diseñados es obvio, para cada uso de una ((asignación)), a qué asignación +te estás refiriendo, sin correr el programa. Esto puede ser usado para +evitar buscar la asociación por nombbre cada vez que se usa, y, en vez de eso, +traer directamente el valor de una locación predeterminada de +((memoria)). -{{index simplicity, "Function constructor", transpilation}} +Tradicionalmente, la ((compilación)) implica convertir el programa +en ((código máquina)), el formato crudo que el procesador de la computadora +puede ejecutar. Pero puedes pensar en compilación como cualquier proceso +que convierta un programa en una representación diferente. -It would be possible to write an alternative ((evaluation)) strategy -for Egg, one that first converts the program to a JavaScript program, -uses `Function` to invoke the JavaScript compiler on it, and then runs -the result. When done right, this would make Egg run very fast while -still being quite simple to implement. +{{index simplicidad, "función constructor", transpilación}} -If you are interested in this topic and willing to spend some time on -it, I encourage you to try to implement such a compiler as an -exercise. +Sería posible escribir una estrategia de ((evaluación)) alternativa +para Egg, una que primero convierrta el programa a uno de JavaScript, +usa `Function` para invocar el compilador de JavaSctipt, y después correr +el resultado. Cuando se hace correctamente, esto haría que Egg +corrra muy rápiddo mientras todavía sigue siendo simple implementarlo. -## Cheating +Si estás interesado en este tema y dispuesto a gastar algo de tiempo, +te animo a que trates de implementar el compilador como ejercicio. -{{index "Egg language"}} +## Haciendo trampa + +{{index "lenguaje Egg"}} + +Cuando definimos `id` y `while`, probablemente te diste cuenta de que son +más o menos envoltorios triviales alrededor de los `if` y `while` de +JavaScript. Similarmente, los valores en Egg son solo valores de +JavaScript comunes. -When we defined `if` and `while`, you probably noticed that they were -more or less trivial wrappers around JavaScript's own `if` and -`while`. Similarly, the values in Egg are just regular old JavaScript -values. +Si comparas la implementación de Egg, construida soobre JavaScript, +con la cantidad de trabajo y complejidad requerida para +construir un lenguaje de programación directamente en la funcionalidad +pura y cruda provista por una máquina, la diferencia es grande. Sin embargo, +con suerte este ejemplo te dio un vistazo de la forma en +que un ((lenguaje de programación)) funciona. -If you compare the implementation of Egg, built on top of JavaScript, -with the amount of work and complexity required to build a programming -language directly on the raw functionality provided by a machine, the -difference is huge. Regardless, this example hopefully gave you an -impression of the way ((programming language))s work. And when it comes to getting something done, cheating is more effective than doing everything yourself. Though the toy language in