1 puntos por GN⁺ 1 일 전 | 1 comentarios | Compartir por WhatsApp
  • Lone Lisp es un Lisp que se ejecuta directamente sobre llamadas al sistema de Linux sin libc; Matheus Moreira lo empezó para comprobar si era posible crear un lenguaje completo y programas de espacio de usuario usando solo C freestanding
  • Al usar directamente la ABI estable del espacio de usuario de Linux, elimina el estado global y las API heredadas de las bibliotecas C, como errno, la configuración regional y el buffering implícito de archivos, e implementa por su cuenta el runtime, el asignador de memoria y las herramientas de prueba
  • Soporta FEXPR y optimización adecuada de llamadas de cola, además de generadores, continuaciones delimitadas y manejo reanudable de errores; también trata vectores, tablas y continuaciones con la misma convención de llamada
  • Arranca rápido, pero en benchmarks provisionales es un intérprete a nivel de listas 10 a 100 veces más lento que Python; planea dejar el intérprete en C como base final de bootstrap e implementar a largo plazo un compilador JIT en el propio Lone
  • Moreira usa Claude para revisión de código y gestión del proyecto, pero escribe y revisa el código de Lone por sí mismo, y quiere reconstruir el espacio de usuario tradicional de Linux creando un generador de sitios estáticos, un shell y utilidades

De los videojuegos a C++, Ruby y Lisp

  • Su interés por la programación empezó con Mega Man Battle Network, y aprendió inglés para participar en foros relacionados; también aprendió C++, su primer lenguaje
  • Empezó un programa de informática en una escuela técnica de Brasil alrededor de los 13 o 14 años
    • Usaba los tutoriales de Dev-C++ y cplusplus.com
    • En seis meses aprendió todo el plan de estudios y luego lo contrataron como asistente para ayudar a otros estudiantes
    • También intentó hacer videojuegos, pero no tuvo éxito por falta de conocimientos de física
  • Después de C++ pasó por Java, Ruby y Python antes de conocer Lisp y Scheme, y le atrajo la elegancia lingüística de Scheme
    • Leyó casi todos los tutoriales de Java de Sun para aprender programación orientada a objetos
    • Le gustaban Java Swing y el diseño Nimbus, e incluso hizo una app para un profesor de matemáticas que dibujaba círculos y arcos
    • En Ruby escribió gems, incluida Acclaim, un parser de argumentos basado en comandos al estilo Git
  • El impulso inicial para explorar varios lenguajes vino de proyectos escolares, pero después pasó a estar centrado en la curiosidad y en encontrar el lenguaje correcto para él

La expresividad de Ruby y la transparencia de C

  • Ruby encajaba bien con su forma de pensar porque muchas veces bastaba con adivinar el nombre de un método, y además admite formas singulares, plurales y sinónimos
    • Con combinaciones como items.each.with_index.map, cercanas a los pipelines del shell, se puede hacer mucho con poco código
    • Hasta hoy siempre tiene Ruby instalado y recientemente también empezó un proyecto con Rails
  • C le resultaba fácil de entender porque las funciones se corresponden directamente con símbolos ELF y código ensamblador
    • Considera que el C++ moderno se convirtió, en la práctica, en un lenguaje distinto al de antes, y ya no dice que sabe C++
    • Intentó mudarse a Rust pero no se asentó en él, y también le interesan Zig y su creador, por la atención que ponen en velocidad y ABI como razones para reescribir en C
  • De adolescente leyó el código fuente de la VM de CRuby y buscaba en la implementación respuestas a preguntas de Stack Overflow como “¿por qué Ruby funciona así?”
  • Después exploró código de CRuby, CPython, la JVM, Guile y V8, y en especial considera el código fuente del kernel de Linux como referencia de programación profesional en C

Cómo lee código ajeno

  • No se limita a usar bibliotecas: lee código constantemente para entender la implementación interna, cómo se comunican drivers y hardware, y qué capas ocultas hay debajo
  • Incluso cuando construía aplicaciones reales, a menudo terminaba desviándose hacia la exploración de las tecnologías base, y desarrolló la tendencia a rehacer mejor problemas que ya estaban resueltos
  • Concluyó que esa inclinación no encajaba bien con una carrera típica de desarrollo de software, así que decidió no hacer de la programación su profesión y mantenerla como hobby
  • No entendió el código de drivers desde el principio; siguió leyendo pese a la frustración y con el tiempo logró comprender parte de su funcionamiento

Ingeniería inversa de un teclado RGB

  • La app de Windows para controlar el teclado RGB de una laptop barebone Clevo era tan lenta que tardaba más de un minuto en abrir una ventana, así que la reemplazó él mismo
  • Capturó con Wireshark los paquetes USB que la app enviaba al teclado, analizó su estructura y escribió un driver en C para espacio de usuario de Linux
  • Lo subió a GitHub y luego se olvidó del proyecto, pero aparecieron usuarios reales y uno de ellos incluso hizo una GUI
  • Después hizo ingeniería inversa a otras funciones de la laptop, como la gestión de energía vía ACPI, aunque no logró descubrir todas

libc evitada con liblinux

  • Las llamadas al sistema de Linux son lo bastante simples en código como para parecerse a llamadas normales a funciones, y es posible apuntar directamente a Linux sin pasar por libc
  • Linux es conocido por la estabilidad de su ABI en espacio de usuario, pero él considera que los componentes del espacio de usuario no lo son tanto
    • En los manuales encontró notas que decían que glibc no soportaba ciertas llamadas al sistema
    • También le influyó un caso citado por LWN donde getrandom tardó unos dos años en entrar a glibc
  • A partir de la idea de crear un liblinux.a que contuviera solo llamadas al sistema y enlazarlo con -llinux, desarrolló liblinux
    • Era una colección de wrappers de llamadas al sistema de Linux para C freestanding
    • Con runtime de C (CRT) se refiere a bibliotecas C estándar como glibc, musl y uclibc

Los problemas que desaparecen en C freestanding

  • Linux devuelve los errores como valores de retorno normales, pero las bibliotecas C los guardan en la variable global errno
  • Al quitar libc, también evita varios estados globales y API antiguas
    • errno, rand/srand, estado de locale
    • strtok, que no es reentrante
    • buffering implícito de archivos, que puede causar problemas inesperados
    • gets, atexit, signal, malloc
    • math.h, que se complica al combinarse con NaN y errno
  • En cambio, las reglas de promoción de enteros y la falta de verificación de overflow siguen siendo problemas propios de C
    • Para el overflow usa funciones integradas del compilador
    • También se cuida de no introducir él mismo defectos nuevos
  • Aunque sin biblioteca estándar tuvo que escribir por su cuenta desde copiado de bytes hasta conversión de enteros a texto, descubrió que no era tan imposible como había imaginado
    • El repositorio de glibc era difícil de interpretar, pero el código de musl libc era limpio y fácil de recorrer, y eso le ayudó mucho
    • También implementó en Lone una suite de pruebas automática que usa solo memoria estática, sin bibliotecas C ni asignación dinámica

Descubrir nolibc y el inicio de Lone Lisp

  • Le preguntó a Greg Kroah-Hartman por qué Linux no tenía una biblioteca C oficial, y recibió como respuesta que en el pasado existió el proyecto klibc
  • Después descubrió que desarrolladores del kernel de Linux habían creado nolibc.h para herramientas internas
    • Consideró que estaba más pulido y era más usable que su propio liblinux, así que archivó ese proyecto
    • Hoy nolibc.h ha crecido dentro del árbol del kernel hasta parecerse más a un libc temporal, aunque su estabilidad externa no está claramente garantizada
  • Como siguiente proyecto empezó Lone Lisp
    • Pensó que si podía crear un Lisp freestanding que corriera directamente sobre Linux, entonces cualquier programa podría escribirse del mismo modo
    • Es un experimento para apartar la complejidad del espacio de usuario moderno en Linux y volver a empezar sobre una interfaz binaria estable de llamadas al sistema
  • También envió parches a Cosmopolitan, por ejemplo para la implementación de parsing de punto flotante
    • Valora mucho el formato APE, que puede interpretarse simultáneamente como ejecutables de varias plataformas, y la capacidad de incrustar archivos dentro del ejecutable

Manejo de errores y continuaciones delimitadas

  • Al principio, todas las rutas de error de Lone terminaban con exit(255) desde el intérprete, pero luego implementó un sistema reanudable de condiciones al estilo Common Lisp
  • La base de su manejo de errores son las continuaciones delimitadas
    • Entiende las continuaciones como copiar frames de pila y reproducirlos en otro lugar
    • También ve las excepciones como una forma de continuación que no captura valores
  • Mapea tipos de error, como 'not-found, a lambdas
    • (lambda (error) ...) con un argumento funciona como un manejador de excepciones normal
    • (lambda (error continuation) ...) con dos argumentos puede invocar la continuación para reanudar con un valor el código donde ocurrió el error
  • Deliberadamente no adoptó una API de restart donde el código que señala el error enumera opciones de reinicio, como use-value en Common Lisp
    • En cambio, lo armó como un mecanismo básico dentro del evaluador para que signal, throw y transfer actúen como si simplemente devolvieran valores

FEXPR y elementos invocables del lenguaje

  • También evitó la forma tradicional en que los evaluadores Lisp manejan directamente formas especiales como if
  • Lone soporta FEXPR, equivalentes a plugins del evaluador, para sacar las formas especiales fuera del evaluador
    • Con este diseño primero logró la optimización de llamadas de cola para funciones Lisp puras, pero durante un tiempo hubo bugs con las primitivas
    • Lo resolvió añadiendo un mecanismo aparte para que las primitivas pudieran devolver en posición de cola
  • Los vectores y las tablas también son invocables
    • ([1 2 3] 0) devuelve 1
    • ([1 2 3] 0 4) modifica el vector a [4 2 3]
  • Las continuaciones y los generadores siguen la misma convención de llamada
    • control es parecido a un begin con soporte para continuaciones
    • transfer sale del bloque control más cercano y devuelve una continuación invocable que representa el punto de interrupción
    • Si se invoca esa continuación con un valor, se reanuda el cálculo interrumpido con un valor de retorno diferente

Llamadas al sistema y estructura de ejecución

  • Antes de que inicie Lone, el proceso padre llama a execve, y Lone realiza unas 12 llamadas a mmap para su gestión interna de memoria
  • Lee un script desde la entrada estándar, lo ejecuta y sale; durante la ejecución también pueden ocurrir mmap, munmap y mremap adicionales
  • El sistema de módulos usa openat y close
  • De las más de 300 llamadas al sistema de Linux, solo usa internamente algunas, pero ofrece una primitiva system-call a nivel Lisp para que los programas puedan invocar las demás
  • Para llamadas como ioctl, que requieren estructuras C del kernel, necesita añadir capacidades para crear y manipular estructuras
    • Consultar el tamaño de la terminal es un caso que requiere ese soporte

El espacio de usuario que quiere construir con Lone

  • A nivel de lenguaje ya implementó manejo de errores, optimización adecuada de llamadas de cola, generadores y continuaciones delimitadas, y a largo plazo quiere incorporar ideas de Erlang/BEAM
  • Preparó la estructura para que varios intérpretes puedan ejecutar código Lisp en paralelo
  • El primer programa que hará con Lone será un reemplazo del generador de sitios estáticos que usa hoy, basado en un fork de PugJS
  • Después planea escribir su propio shell y utilidades para rehacer a su manera el espacio de usuario tradicional de Linux
  • También hizo una implementación que exporta env como tabla de Lone Lisp; en ese momento no había iteración, pero ahora ya soporta generadores
  • El intérprete también puede leer y ejecutar código Lisp desde un segmento ELF especial
    • Si se copia el binario del intérprete y se inserta código Lisp, se puede crear un ejecutable independiente

Revisión y gestión del proyecto con Claude

  • No ve un límite claro en la cantidad total de trabajo, pero cree que el conjunto de funciones del lenguaje ya se acerca a estar completo
  • Usa Claude como una especie de gerente, y le ayuda sobre todo en revisión de código y gestión del proyecto
    • Le pide que encuentre de forma rigurosa problemas, áreas sin desarrollar y direcciones futuras en todo el codebase
    • Gestiona enfoques rechazados y funciones para más adelante mediante documentación y memoria del proyecto
    • El recolector de basura y el asignador de memoria mejoraron mucho porque Claude le sugirió algoritmos mejores
  • Revisa personalmente cada línea que entra en Lone y, como considera que escribir código es en sí mismo algo educativo, no confía en que una IA escriba el código de Lone por él
  • En cambio, en su generador de sitios estáticos en JavaScript sí dejó que Claude escribiera funciones completas
  • Entre las funciones planeadas están soporte para la API de compiladores GCC/Clang y para funciones de protección de pila

Sintaxis tomada de Ruby y Scheme

  • Quiso combinar la expresividad de Ruby con la concisión de Scheme; como el parser de Lisp es simple, pudo escribirlo él mismo y trabajar directamente con listas anidadas
  • Dice que entendió Lisp de verdad cuando implementó FEXPR, donde las funciones reciben la lista misma y no el valor evaluado
  • Los arreglos usan la sintaxis [1 2 3] y los diccionarios la sintaxis {k v}
    • Además de Clojure y newLisp, esto resulta familiar para usuarios de Ruby, Python y JavaScript, y reduce barreras de entrada innecesarias
  • Su estilo de C se basa en el del kernel de Linux, aunque prefiere más alineación y decoración
  • En Ruby prefiere reducir la puntuación y escribir algo más cercano a una oración en inglés, como method 'arg' en vez de method('arg')
  • En Lisp también reduce el anidamiento sintáctico
    • En Lone, let tiene la forma (let (a b c d ...) body...)
    • Le parece más natural para la aplicación de funciones que la forma tradicional (let ((a b) (c d) ...) body...), porque los nombres y valores aparecen en orden

Rendimiento y plan de bootstrap

  • El benchmark donde Lone destaca es la velocidad de arranque: puede iniciar y terminar un programa más rápido de lo que Ruby tarda en leer Rubygems
  • En mediciones provisionales es unas 10 a 100 veces más lento que Python
    • Lone es un intérprete a nivel de listas, parecido al del capítulo 5.4 de SICP
    • Python y Ruby son intérpretes de bytecode, así que pertenecen a otra categoría de rendimiento
  • Evita compilar a bytecode porque siente que convertir listas en bytecode le haría perder a Lisp su esencia
  • A largo plazo, el intérprete en C quedará como referencia final de bootstrap, y planea escribir dentro de Lone un compilador JIT
    • La idea es reservar páginas ejecutables con mmap y luego generar instrucciones ahí
  • En una comparación provisional de Fibonacci recursivo con Bash, Lone fue mucho más rápido, aunque reconoce sesgos de medición, como la posible creación de procesos en cada iteración por parte de Bash
  • Como en los scripts el tiempo de arranque importa, cree que el bootstrap rápido de Lone puede ser útil
    • Si las tablas hash se precalculan durante el build y se convierten en datos de inicialización en C, el costo de inicialización en ejecución podría bajar todavía más

Memoria y entorno mínimo

  • mmap pide páginas de memoria al kernel, y el tamaño de página típico es de 4,096 bytes
  • Se pueden especificar permisos de lectura, escritura y ejecución, y el JIT usa páginas ejecutables
  • Si solo se necesitan 16 bytes, pedir 4 KiB es muy ineficiente, por eso un asignador normal recibe primero regiones grandes, como 128 MB, y luego las divide en partes pequeñas
  • El entorno de nivel superior de Lone solo tiene dos símbolos: import y export
    • Fuera de listas, literales y el mecanismo de llamada de funciones, hay que importar todo, incluso if y quote
    • Es un diseño pensado para que el usuario controle directamente todos los nombres del entorno
  • Actualmente hay unas 50 funciones, y entre las más importantes están if, let, begin, control/transfer, intercept/signal y flatten

Biblioteca estándar y documentación

  • La documentación del lenguaje sigue incompleta, pero el trabajo en la biblioteca estándar está más cerca que las funciones del lenguaje
  • Planea tomar como referencia la biblioteca estándar de Ruby y el estilo de nombres de Scheme
  • Lo siguiente será el protocolo de iteración
    • Está considerando una forma como (for (each collection) (lambda (item) ...))
    • También piensa rediseñar las funciones each existentes para que todas devuelvan generadores
  • Después quiere implementar manipulación de strings y un string builder para concatenación eficiente
  • Cuando la iteración y las funciones de strings estén listas, cree que también podrá escribir un framework web con forma de Hiccup
  • Le preocupa perder concentración cuando termine el trabajo de bajo nivel y pase a tareas repetitivas de biblioteca estándar, por eso planea apoyarse en la ayuda de gestión de Claude

La meta de poseer toda la pila

  • Incluso funciones que parecen misteriosas, como las continuaciones, se vuelven problemas normales de implementación si se entienden como copia de frames de pila; con Lone quiere animar a otros a construir y explorar sus propios sistemas
  • No quiere quedarse como consumidor de bibliotecas, sino convertirse en alguien que crea bibliotecas, y también usar directamente llamadas al sistema que para muchos desarrolladores parecen estar prohibidas
  • Tener un sistema propio implica arreglarlo uno mismo cuando se rompe, pero espera que más personas elijan esa responsabilidad y esa recompensa
  • Cree que no hay que temerle al viejo código acumulado en la computación y que es posible quitar capas innecesarias
    • Quien reconstruya el espacio de usuario de Linux quizá no sea él, sino desarrolladores de Rust o Zig
    • Aunque Lone no tenga éxito, le bastaría con que inspire este tipo de intentos

Software libre y Android

  • Valora el empoderamiento del usuario que busca el software libre, y le preocupa que la computadora de propósito general se reduzca a un electrodoméstico controlado por empresas o gobiernos
  • Aunque systemd a veces haya abordado las cosas de forma pesada, lo considera un caso exitoso de reimaginar y reconstruir el espacio de usuario tradicional de Linux
  • Cree que no se puede construir cada capa por cuenta propia y que hay que elegir las batallas
    • Si hubiera hecho Lone como un Lisp bare-metal, quizá ni siquiera habría llegado a la etapa de arranque
    • Pone como ejemplo el Sector Lisp de jart, un Lisp de boot sector de menos de 512 bytes
  • Lone empezó dentro de Termux y una parte importante fue escrita desde el teléfono
  • Android bloquea algunas llamadas al sistema, entre ellas statx, pero Lone corre en Android y su suite de pruebas pasa al 100%

Médico y programador no profesional

  • Moreira es médico y se graduó de la facultad de medicina en 2019
    • En la preparatoria se interesó por biología mientras leía sobre programación de redes neuronales
    • Algunos días atiende consulta durante 10 a 14 horas y otros descansa, lo que le permite apartar tiempo para programar
  • En hospitales con historia clínica electrónica reduce mucho su trabajo gracias a plantillas y velocidad al teclear
    • Puede registrar información manteniendo contacto visual con el paciente
    • Su proyecto reciente en Rails es un sistema de registro de pacientes electrónicos para resolver sus propias necesidades
  • Cree que una de las razones por las que muchos desarrolladores profesionales no resuelven por sí mismos problemas personales es la falta de interés en la computadora como tal
  • Piensa que para profundizar en una implementación interna hace falta curiosidad, más que una actitud orientada solo a obtener resultados, aunque todavía no sabe cómo despertar esa curiosidad en otras personas

1 comentarios

 
GN⁺ 1 일 전
Opiniones en Lobste.rs
  • Me gustan tanto estas series de entrevistas que las leo sin falta, y es muy entretenido conocer la relación personal que cada quien ha tenido con la computación

    • En especial, me resulta interesante reunir y comparar los motivos de iniciación por los que uno termina apreciando a las computadoras como algo más que una simple herramienta
      El autor del emulador GBE+ GBA, Shonumi, también fue influenciado por Mega Man Battle Network, igual que yo, y algún día me gustaría leer una entrevista suya
  • Las excepciones reiniciables no requieren necesariamente continuaciones de primera clase (first-class continuations)
    Common Lisp ofrece excepciones reiniciables incluso sin continuaciones, y la clave está en no rebobinar la pila antes de invocar el manejador
    Así, la pila y el estado donde ocurrió la excepción se conservan tal cual, y el manejador puede devolver normalmente o saltar a un reinicio en algún punto de la pila, como en Common Lisp

    • Si di la impresión de que eran indispensables, fue porque me emocioné demasiado con mi descubrimiento
      Aun así, en Lone Lisp sí son necesarias, porque la continuación devuelta puede invocarse varias veces y puede escapar fuera del manejador de señales
      (import (lone print set quote lambda intercept signal))
      
      (set continuation  
        (intercept  
          (('return (lambda (value continuation) continuation)))  
          (signal 'return 'value)))
      
      (print (continuation 1)); 1  
      (print (continuation 2)); 2  
      (print (continuation 3)); 3  
      
      Como los generadores son la base del procesamiento iterativo, el rendimiento importa más, así que en lugar de continuaciones limitadas usé una pila separada para evitar copias
      El manejo de errores es una ruta relativamente poco frecuente, así que decidí ofrecer la funcionalidad completa
  • Para mucha gente, su trabajo o proyecto más importante quizá sea algo que hace en su tiempo libre, sin paga y por diversión
    Me hace imaginar que incluso mi médico podría tener una doble vida secreta creando su propio lenguaje Lisp

    • Al parecer, no soy el único
      El Dr. Cameron Kaiser también ha llevado adelante proyectos importantes, comparte incluso una filosofía sobre la propiedad de las máquinas y usa de forma cotidiana una workstation POWER9 totalmente abierta
  • Es la primera vez que escucho de Lone Lisp, y parece un gran proyecto
    Yo también empecé a aprender C++ con un viejo Dev-C++ en clase de informática cuando tenía unos 13 o 14 años, y de verdad me trajo mucha nostalgia

    • Me sorprende saber que Dev-C++ sigue existiendo y que incluso tuvo un lanzamiento en 2021
  • No esperaba para nada que fuera médico, y me hizo pensar en Con Kolivas
    Parece que en realidad hay bastantes médicos desarrolladores
    https://en.wikipedia.org/wiki/Con_Kolivas