Spinel - compilador nativo AOT para Ruby
(github.com/matz)- Compilador AOT que convierte código fuente Ruby a código C mediante inferencia de tipos de programa completo y genera binarios nativos independientes
- Desarrollado directamente por matz, creador de Ruby; el propio backend del compilador está escrito en Ruby y tiene una arquitectura self-hosting que se compila a sí misma
- Rendimiento aprox. 11.6 veces más rápido que miniruby (Ruby 4.1.0dev); Conway's Game of Life mejora 86.7 veces, ackermann 74.8 veces y mandelbrot 58.1 veces
- El pipeline de compilación usa un parser basado en Prism para convertir Ruby en texto AST, después el backend self-hosting realiza la inferencia de tipos y la generación de código C, y finalmente crea un binario standalone con un compilador C estándar
- Soporta una amplia gama de funciones de Ruby, incluyendo clases, herencia, bloques, manejo de excepciones, Fiber, motor Regexp NFA integrado, Bigint con promoción automática, pattern matching y más
- Las clases pequeñas con 8 campos escalares o menos se asignan automáticamente en la pila como value types, eliminando por completo el overhead del GC (1 millón de asignaciones: 85 ms → 2 ms)
- Aplana el encadenamiento de strings
a + b + c + den una sola llamada a malloc; en bucles,splitreutilizasp_StrArraypara eliminar asignaciones innecesarias - Incluye múltiples optimizaciones en tiempo de compilación, como hoisting de longitud invariante en bucles, propagación de constantes, inlining automático de métodos de 3 sentencias o menos y terminación anticipada de inferencia iterativa (~14% menos tiempo de bootstrap)
- Los binarios generados tienen cero dependencias de runtime y pueden ejecutarse solo con libc + libm
- No soporta
eval, metaprogramación, ni Thread o Mutex (solo Fiber) - Licencia MIT
1 comentarios
Comentarios en Hacker News
Si lo hizo Matz, entonces seguramente conoce bien incluso las limitaciones de la semántica de Ruby, así que inspira confianza
Mi tesis de maestría también fue sobre un compilador AOT de JS; sí funcionaba, pero las restricciones sobre los datos de entrada eran tan grandes que al final la abandoné
En ese tiempo los desarrolladores de JS no estaban tan acostumbrados a imponerse esas restricciones por su cuenta, y entradas intrínsecamente incognoscibles como
JSON.parseeran un obstáculoAhora, gracias a TypeScript, podría ser mucho más viable que en ese entonces
Incluso viendo el cálculo lambda general, los límites de la inferencia de tipos son claros, y se ven restricciones similares en los papers de Matt Might o en el trabajo de Shed-skin para Python
Me da curiosidad qué tan comunes son
eval,send,method_missingydefine_methoden código Ruby real, y también cómo suelen manejar entradas sin tipo, por ejemplo datos JSONParsear Ruby es casi más difícil que la propia traducción, así que usan Prism, y el resultado genera C
Implementar la semántica básica de Ruby en sí no es tan difícil
En cambio, yo sigo batallando con un viejo compilador AOT self-hosting hecho en Ruby puro, y por insistir en usar su propio parser tomé a propósito un camino mucho más difícil
Aprendí temprano que el primer 80% se puede hacer más o menos y aun así lograr que corra una buena parte del código Ruby; el verdadero “segundo 80%” difícil está concentrado en las cosas que Matz dejó fuera de este proyecto y de mruby, como la codificación y toda clase de funciones periféricas
Si soy honesto, Ruby también tiene varias funciones que nunca he visto una sola vez en código real, así que no me sorprendería que algunas quedaran deprecated
send,method_missingydefine_methodson muy comunesLas restricciones son parecidas a las de mruby, y aun bajo esas restricciones sí hay casos de uso
El soporte para
send,method_missingydefine_methodes relativamente fácilEn cambio, dar soporte a eval() es una pesadilla
Aun así, una gran parte del uso de
eval()en Ruby puede reducirse estáticamente a la versión con bloque de instance_eval, y en esos casos la compilación AOT se vuelve bastante sencillaPor ejemplo, si la cadena que entra a
eval()se puede conocer o descomponer estáticamente, hay bastante margen para resolverloDe hecho, mucho uso de
eval()es innecesario o se parece más a rodeos simples para introspección, así que puede tratarse con análisis estáticoEn mi compilador también pienso empezar por ahí si eso se vuelve el cuello de botella
La ingestión de JSON sin tipos probablemente también use mecanismos así
Si quitas eso, queda un lenguaje pequeño y fácil de leer que no es tan fuertemente tipado como Crystal, pero tampoco depende tanto de la metaprogramación como Ruby oficial
Por eso parece tener bastante potencial, pero al final habrá que ver con el tiempo
evalcon frecuenciaPodría no usarlo, pero para mí así es más ergonómico
eval,exec,define_methody también el patrón de crear clases nuevas conClass.newyStruct.newLa mayoría de esos usos se concentra en el arranque de la app o mientras se hace require de archivos, y en cierto sentido eso ya se parece a una etapa de compilación
Esto es lo que Matz acaba de presentar en RubyKaigi 2026
Es experimental, pero lo hizo en alrededor de un mes con ayuda de Claude, y la demo en vivo también salió bien
El nombre viene del nuevo gato de Matz, y el nombre del gato viene del gato de Card Captor Sakura, donde además hace pareja con un personaje llamado Ruby
En alguien como Matz, eso podría significar empujarlo de 100x a 500x
https://en.wikipedia.org/wiki/Spinel
Parece que el video todavía no está en vivo, y da la impresión de que los van subiendo uno por uno a este canal
https://www.youtube.com/@rubykaigi4884/videos
También da la impresión de que el nombre del proyecto se eligió de forma emocional
Sin duda es súper impresionante, pero parece imposible de mantener sin un agente de IA
spinel_codegen.rbtiene 21 mil líneas, y algunos métodos llegan a 15 niveles de anidaciónEl código de compiladores rara vez es bonito de por sí, pero incluso con ese estándar esto se ve muy difícil de mantener para una persona
Los compiladores tienen límites entre subsistemas muy claros y handoffs bien definidos entre etapas, así que en realidad están entre las cosas más fáciles de hacer de forma modular
El problema casi siempre es que primero se hace que funcione y luego ya no queda tiempo para refactorizar, y entonces el desorden sigue creciendo
spinel_codegen.rbestá casi al nivel de horror eldritchCuando uso Claude, a mí también siempre me sale este tipo de código espagueti, y pensé que tal vez yo estaba haciendo algo mal
Pero ver que incluso en un proyecto realmente interesante hecho por alguien a quien considero un programador de primer nivel la calidad del código es bastante mala en varias partes me hizo ver que no me pasa solo a mí
Por ejemplo,
infer_comparison_type()ni siquiera es el peor caso ni es ilegible, pero existe una implementación mucho más simple y clara y aun así Claude no llega ahíSi agrupas los operadores de comparación en un
Sety lo manejas coninclude?, queda más corto, más rápido, más legible y más fácil de mantenerPero Claude siempre termina cayendo en cadenas de if-return, e incluso da la impresión de que hasta los if-else le resultan ajenos
Mi codebase hecho con Claude también está lleno de ese patrón, así que ahora sé que no me pasa solo a mí
En cambio, otros archivos están bastante mejor, y en especial el directorio
libparece corresponder al directorioextdel repo principal de Ruby y tiene una calidad decenteLa API también está claramente influida por MRI Ruby, y aunque la implementación sea bastante distinta, parece que Matz guio el resultado para que se pareciera a parte de la API original y así quedara más ordenado
[1] https://github.com/matz/spinel/blob/98d1179670e4d6486bbd1547...
Si pasa las pruebas y los benchmarks, por ahora me doy por satisfecho
Aun así, sí me pregunto si un archivo gigante también es fácil de manejar para la IA
Yo intento limitar los archivos a menos de 300 líneas, y creo que el código fácil de entender para humanos también lo será para los agentes de programación
Dicen que las restricciones son estas
No eval:
eval,instance_eval,class_evalNo metaprogramming:
send,method_missing,define_method(dinámico)No threads:
Thread,Mutex(sí soporta Fiber)No encoding: asume UTF-8/ASCII
No general lambda calculus:
-> x { }profundamente anidado con llamadas[]En lo personal, asumir UTF-8/ASCII no me parece una restricción tan grave, pero el resto sí parece una limitación real para bastantes programas
Y volver a meter todo eso parece que requeriría bastante trabajo
Llevo mucho tiempo usando Ruby, y habiendo usado todas las funciones listadas, siento que al final de mi propia evolución justo terminé queriendo esta versión de Ruby simple
Es más simple y más fácil de entender, pero todavía conserva la estética propia de Ruby
Ahora, gracias a los LLM, la productividad para generar código es tan alta que ya no hace tanta falta reducir boilerplate con metaprogramación para mejorar la productividad del desarrollador como antes
Porque cada vez escribimos menos código directamente
La sintaxis es parecida y tiene un sistema de tipos estático, lo que lleva a código compilado más eficiente
evalhasta me parece mejor, pero que tampoco haya threads ni mutexes sí me decepcionaLa ausencia de
define_methodse entiende por su caso de usoPero
sendymethod_missingson comunes en librerías existentes, y tampoco parecería tan difícil implementarlos construyendo en compilación una tabla de lookup en memoriaAsí que no sé si lo dejaron fuera a propósito o si simplemente todavía no han llegado hasta ahí
Espero que sea lo segundo, pero al menos por ahora, por compatibilidad, parece difícil usarlo en producción
Fue reducir la cantidad de código que hay que leer
Esto está buenísimo, y yo llevaba mucho tiempo esperando un compilador AOT para Ruby
Sí da lástima que no haya fallback para
evalo metaprogramación, aunque parece que eligieron eso para concentrarse en un subconjunto pequeño y de alto rendimientoOjalá los gems construidos con este compilador AOT interactúen bien con MRI
Para empaquetar o bundlear Ruby estándar y gems todavía hacen falta tebako, kompo u ocran, y antes también existían proyectos como ruby-packer, traveling ruby y jruby warbler
Está bien tener una opción más, pero sigo esperando una versión definitiva con una mejor UX para desarrolladores
Porque llevaba demasiado tiempo sin actualizarse
Me pregunto por qué no threads
El scheduler de Ruby y la implementación subyacente con pthread parecerían funcionar bien también en el mundo de C, así que me pregunto si apuntan a zero dependency
Si no piensan agregarlo más adelante como extensión opcional, o si no es simplemente que todavía no lo implementan, esta decisión sí se siente un poco rara
Más bien sospecho que simplemente todavía no han llegado hasta ahí
El multithreading siempre ha sido muy difícil de hacer bien
Sorprende que lo hayan hecho en poco más de un mes
Se diga lo que se diga sobre la IA, en manos de desarrolladores con habilidad produce una aceleración enorme
Matz en cambio parece sentir que con
gem env|infoyfindya bastaSiendo algo hecho por Matz, me pregunto qué tan realista es que en el futuro pase a ser parte de Ruby core
Y si eso ocurriera, también me pregunto qué tanto amenazaría a Crystal
Esas características son prácticamente indispensables para compilar y mantener programas grandes
En cambio, esto es un subconjunto limitado de Ruby, así que la mayoría de los gems populares de Ruby no van a funcionar tal cual
Como subconjunto de lenguaje orientado a compilar a C, se parece más a PreScheme
En este punto no creo que ambos compitan directamente en el mismo espacio
Ruby completo casi con seguridad necesita JIT
[1]: https://prescheme.org/
Sería la revancha de herramientas como Rational Unified Process y Enterprise Architect
La diferencia es que, en vez de diagramas UML, llegarán archivos markdown
Esto parece útil para el lado de herramientas de infraestructura
Por ejemplo, se puede imaginar un bundler escrito en Ruby pero compilado de forma estática, que además cumpla el papel de herramienta de instalación de Ruby como RVM
El buildpack actual de Ruby está escrito en Ruby, pero obliga a hacer bootstrap con bash, lo cual es molesto y genera casos borde
CNB se escribió en Rust para evitar ese problema, y la idea de poder distribuir un binario único sin dependencias es realmente poderosa