- La sintaxis de JavaScript se vuelve compleja con facilidad por los paréntesis anidados y los callbacks, y se produce hinchazón al arrastrar muchas librerías incluso para una UI pequeña
- WebAssembly abre la puerta a ejecutar otros lenguajes en el navegador, pero el costo de conectarlo de forma asíncrona con el bucle de eventos de JavaScript, como en Pyodide, es alto
- Como los recursos del navegador y la memoria de WebAssembly son limitados, hace falta un enfoque de negociación en lugar de intentar reemplazar JavaScript
- LispE incluye más de 450 funciones en un solo binario WASM de 3.3 MB, y ofrece en conjunto cadenas, cálculo, matrices y expresiones regulares
- Con
evaljs y asyncjs, aprovecha funciones JavaScript y el DOM, mientras reduce la hinchazón del código con un único binario auditable en vez de varias librerías externas
La hinchazón de JavaScript y las limitaciones del navegador
- La sintaxis de JavaScript se complica con facilidad porque se superponen paréntesis, llaves y corchetes, y hay que acertar el orden de cierre
- Se muestra un ejemplo que asigna valores a varios objetos dentro de un callback de
forEach y actualiza una caché de forma condicional
newNames.forEach((name, i) => {
allAgentContents[name] = contents[i];
agentModes[name] = modes[i];
if (compiled[i]) agentCompiledCache[name] = compiled[i];
agentViewingCompiled[name] = viewing[i];
});
- Para tareas que un lenguaje de programación básico normalmente incluiría, en JavaScript se terminan descargando muchas librerías
- En C o C++ también se necesitan
include para trabajar, pero en el caso de las librerías de JavaScript muchas veces no queda claro quién las implementó ni quién las mantiene
- Hay páginas que parecen cargar “la mitad de internet” solo para mostrar hello en una ventanita
- Internet depende de JavaScript, y aunque TypeScript lo oculte, sigue siendo el portero por el que hay que pasar cada vez que se abre una página en el navegador
- Existió el sueño de que el navegador se convirtiera en el sistema operativo definitivo y volviera innecesarios a Windows o Mac OS, y JavaScript fue el lenguaje elegido para materializar ese sueño
La relación entre WebAssembly y JavaScript
- WebAssembly se parece más a una máquina virtual con su propio código máquina, y abre la posibilidad de programar de otra forma dentro del navegador
- Pyodide es un logro de ingeniería impresionante para ejecutar Python en el navegador, pero también deja ver el costo de operar dentro del territorio de JavaScript
- Los dos mundos asíncronos,
asyncio de Python y el bucle de eventos de JavaScript, tienen que comunicarse entre sí
- El puente entre ambos es frágil, y hay que recordar a qué mundo pertenece cada
await
- Los recursos del navegador son limitados, así que hace falta una forma de pensar parecida a la de los primeros tiempos de la informática, revisando cada instrucción y cada estructura a nivel de bits
- Se plantea como referencia haber empezado a programar en 1981 en una computadora con exactamente 15772 bytes disponibles
- La memoria de un navegador moderno no está tan limitada como aquella primera computadora, pero WebAssembly no puede poseer memoria libremente como un programa normal, sino que debe obtener permiso primero de forma restringida
- La actitud clave no es pelear con JavaScript, sino negociar con él
La alternativa que propone LispE
- LispE ofrece más de 450 funciones dentro de un solo binario
- El binario WASM tiene un tamaño de 3.3 MB
- Reúne en un mismo lugar funciones para cadenas, cálculo, matrices y expresiones regulares
- El binario WASM está en binaries/wasm
- Aunque es un intérprete pequeño, puede manejar como valores de retorno cadenas,
Float64Array, enteros, números reales y arreglos de cadenas
- Como LispE está basado en Lisp, tiene una estructura con AST viva
- Si la sintaxis de Lisp no encaja, se puede usar una sintaxis transpilada para un lenguaje difícil de distinguir de Python o Basic
- Se puede modificar grammar para crear un lenguaje con el estilo que se quiera
- Incluso puede hacerse en griego, y ya existe un ejemplo en griego
- LispE coopera con JavaScript sin enfrentarse a él, aprovechando funciones JavaScript y capacidades del DOM
Llamadas a JavaScript: evaljs y asyncjs
- Para ejecutar código JavaScript desde LispE, se pueden usar
evaljs y asyncjs
evaljs ejecuta código JavaScript y devuelve un valor
asyncjs conecta funciones JavaScript definidas por el usuario en la página con callbacks asíncronos
(setq a (evaljs "10 + 20 + 30")) ; execute some JS code
; call_llm is a user-defined JS function in the page
(asyncjs `call_llm("Implement a piece of code in Python to sort strings");` 'mycallback)
(defun mycallback(theresult) ...)
asyncjs funciona como una Promise de que regresará cuando la tarea termine
Reducir la hinchazón del código
- Una cita de Wirth de 1995 lleva a la conclusión de que ni computadoras más rápidas ni modelos más grandes resuelven el problema, y que hay que mantener el código liviano
- LispE expone en el navegador 450 funciones mediante un único binario auditable
- Para implementar cada capacidad por separado habría que cargar librerías como
mathjs para cálculo numérico, lodash para colecciones, voca para manipulación de cadenas o simple-statistics para distribuciones estadísticas
- Ese enfoque puede crecer hasta convertirse en cientos de MB de código con autores, errores y calendarios de mantenimiento distintos
- LispE ofrece estas capacidades como una sola base de código mantenida, y al ser open source se puede auditar el código completo
- Se considera que el Garbage Collector no destruye el rendimiento del código en el peor momento posible
- Se comunica con JavaScript de forma transparente mediante llamadas simples de API, y puede devolver estructuras predefinidas
// floats here is a Float64Array
const floats = callEvalLispEToFloats(0, `(normal_distribution 100)`);
2 comentarios
El artículo salió un poco de la nada, así que dudé de la fuente, pero resulta que era de Naver, jaja
Pero sí, la reacción no ha sido buena... Sinceramente, eso de exagerar JavaScript y hasta subir un WASM de 3.3 MB para usar Lisp es un nivel de sobreingeniería que no es fácil de entender jajaja
Sobre por qué era una cuenta de Naver, un desarrollador del proyecto llamado Claudius dejó un comentario diciendo que trabaja en Naver Labs Europe y que Naver aprobó publicarlo como proyecto de código abierto.
No parece tener mucho que ver con Naver en sí, más bien parece que simplemente son personas que de verdad aman Lisp...
Comentarios en Lobste.rs
Se supone que van a desinflar JavaScript y lo que hacen es agregar un bloque opaco de WASM de 3.3 MB y decirte que escribas la app en Lisp
Con JavaScript puro y cero dependencias adicionales todavía puedes construir bastantes cosas antes de siquiera acercarte a una décima parte de ese tamaño
No comparto este caso de uso, pero es un proyecto de pasión en Lisp interesante, con una rareza poco común hoy en día, y me gusta que toda la documentación tenga frases tan particulares que claramente se nota que fueron escritas por una persona
Siempre están aceptando propuestas de cosas nuevas para agregar a la biblioteca estándar
Entre las adiciones recientes están unión/intersección de conjuntos,
sum,base64, etc.Dicho eso, casi nunca he escuchado pedidos de funciones de cálculo, y en cadenas lo que se pide de forma constante es algo como
.reverseo.titleCase.reverseno tiene muchos usos realmente convincentes más allá de problemas de juguete, y.titleCasesí es posible, pero requiere datos de internacionalización, así que la discusión sigue en cursoAdemás, ninguna de las dos está en esta biblioteca
Sienten que no vale la pena intentarlo porque una propuesta tardaría 3 años en reflejarse de verdad, y que una carpeta
utils/no les encanta, pero al menos la pueden hacer ya mismoMi argumento va menos por que a JavaScript le falten funciones y más por el modelo de distribución
Aunque la biblioteca estándar estuviera llena, una app promedio igual seguiría cargando megabytes de dependencias transitivas de npm
LispE-as-WASM son 3.3 MB, unas 450 funciones documentadas, un núcleo en C++ y código fuente público en GitHub: es un experimento para ver cómo podría verse un runtime auditable
De hecho, ya construí un arnés de agentes encima de eso, y estoy ejecutando reglas de LispE al vuelo dentro del navegador
El event loop de JavaScript me encanta
Cuando alguien dice que la sintaxis está inflada, me pongo en guardia
Una sintaxis mínima puede ser mucho más difícil de leer que una que use caracteres distintos para diferenciar visualmente las cosas
Si ves lo que LispE propone como alternativa, es esto:
... !?
https://github.com/naver/lispe/wiki/5.3-A-la-APL
El punto de entrada a este proyecto es medio raro, y https://github.com/naver/lispe/wiki/1.-Introduction parece mejor
Es un proyecto de Lisp nativo que también apunta a WASM, y tiene cosas interesantes que seguramente les gustarán a los entusiastas de los lenguajes de programación
Mi primera impresión fue que era un ejemplo de JavaScript bastante forzado
Gran parte del odio a JavaScript parece venir de la incompatibilidad entre navegadores en sus primeros años, APIs del DOM dolorosas y trampas de sintaxis, y esas cosas prácticamente ya no afectan mi día a día
El JavaScript moderno, especialmente con TypeScript y reglas de lint razonables, como lenguaje está bastante bien
Siguen existiendo problemas como CJS vs ESM, riesgos de la cadena de suministro y volatilidad del ecosistema, pero la mayoría están fuera del lenguaje en sí
¿La experiencia excelente es esa misma que hizo que la gente hasta creara extensiones de editor porque equilibrar paréntesis era demasiado molesto?
Si quieres este estilo de programación, o sea, baja densidad sintáctica y enfoque funcional, conviene más ir en la dirección contraria y usar un lenguaje concatenativo
Me intriga cómo el resultado compilado de un intérprete de Lisp puede llegar a 3.3 MB
¿Eso es desinflar?
Aun así, coincido en que distribuir una biblioteca estándar grande junto con código que no se usa está bastante lejos de desinflar nada
¡Impacto! ¡Terror!
Qué sorpresa tener que cerrar los paréntesis en el orden correcto
Incluso los fans de Lisp que se hartan de lo verbosos que son lenguajes como JavaScript tienden a echarse para atrás frente a los lenguajes de la familia APL y empiezan a discutir que la claridad y la legibilidad importan mucho más que hacer todo lo más corto posible
El autor de LispE parece tener cierto cariño por las operaciones básicas de APL, pero aun así cuesta entender por qué, después de conocer un lenguaje con una notación tan concisa, preferiría una versión de Life de Conway en s-expresiones profundamente anidadas, más larga y más dispersa que las versiones en APL, J, K o incluso Q