1 puntos por GN⁺ 5 일 전 | 1 comentarios | Compartir por WhatsApp
  • Convierte código Ruby en binarios nativos independientes y apunta a una ejecución con media geométrica de aproximadamente 11.6 veces más rápida que el miniruby de CRuby actual, mediante inferencia de tipos a nivel de programa completo y generación de código C
  • El pipeline de compilación convierte Ruby en texto AST con un parser basado en Prism, luego un backend self-hosting realiza la inferencia de tipos y la generación de código C, y finalmente un compilador C estándar produce un binario standalone
  • El backend del compilador tiene una arquitectura self-hosting escrita en Ruby, y tras el proceso de bootstrap se cumple gen2.c == gen3.c, cerrando el ciclo de recompilarse a sí mismo
  • Incorpora optimizaciones en tiempo de compilación como aplanado de concatenación de strings, value-type promotion, loop-invariant length hoisting, static symbol interning y promoción automática a bigint, además de un motor regexp integrado, bigint y un runtime de un solo header para reducir dependencias externas
  • No soporta eval, metaprogramación, Thread ni manejo general de encodings, pero demuestra la practicidad de la compilación AOT para Ruby con una forma de distribución que se ejecuta sin Ruby y una gran diferencia de rendimiento en cargas de trabajo intensivas en cómputo

Cómo funciona

  • El pipeline de compilación consiste en parsear archivos Ruby, serializarlos como archivos de texto AST, y luego pasar por inferencia de tipos y generación de código C para producir un binario nativo con un compilador C estándar
  • spinel_parse usa Prism y libprism para parsear Ruby, y cuando no hay un binario C disponible utiliza una ruta alternativa con CRuby y el gem Prism
  • spinel_codegen funciona como un binario nativo self-hosted y, a partir del AST, realiza inferencia de tipos + generación de código C
  • La etapa final compila el código fuente C junto con el header del runtime usando cc -O2 -Ilib -lm, y el binario resultante se crea en formato standalone

Self-Hosting

  • La cadena de bootstrap se cierra creando AST con CRuby + spinel_parse.rb, generando gen1.c y bin1 con CRuby + spinel_codegen.rb, y luego usando otra vez el binario generado para crear gen2.c y gen3.c
  • Se indica que gen2.c == gen3.c, confirmando que el bootstrap loop quedó cerrado
  • El backend spinel_codegen.rb está escrito en un subconjunto de Ruby que Spinel puede compilar por sí mismo
    • classes, def, attr_accessor
    • if/case/while
    • each/map/select, yield
    • begin/rescue
    • operaciones con String, Array, Hash y File I/O
  • El backend no incluye metaprogramming, eval ni require

Rendimiento y benchmarks

  • Las pruebas están en 74 aprobadas y los benchmarks en 55 aprobados
  • En 28 benchmarks, la media geométrica es de aprox. 11.6x más rápido que el miniruby de CRuby actual
  • La comparación se hace contra un build reciente de CRuby miniruby sin gems empaquetados, y aun comparándolo con una referencia más rápida que ruby 3.2.3 del sistema, mantiene una clara ventaja en cargas intensivas en cómputo
  • Rendimiento de cómputo

    • life: 20ms vs 1,733ms, 86.7x más rápido
    • ackermann: 5ms vs 374ms, 74.8x más rápido
    • mandelbrot: 25ms vs 1,453ms, 58.1x más rápido
    • versión recursiva de fib: 17ms vs 581ms, 34.2x más rápido
    • nqueens: 10ms vs 304ms, 30.4x más rápido
    • tarai: 16ms vs 461ms, 28.8x más rápido
    • tak: 22ms vs 532ms, 24.2x más rápido
    • matmul: 13ms vs 313ms, 24.1x más rápido
    • sudoku: 6ms vs 102ms, 17.0x más rápido
    • partial_sums: 93ms vs 1,498ms, 16.1x más rápido
    • fannkuch: 2ms vs 19ms, 9.5x más rápido
    • sieve: 39ms vs 332ms, 8.5x más rápido
    • fasta: 3ms vs 21ms, 7.0x más rápido
  • Estructuras de datos y GC

    • rbtree: 24ms vs 543ms, 22.6x más rápido
    • splay tree: 14ms vs 195ms, 13.9x más rápido
    • huffman: 6ms vs 59ms, 9.8x más rápido
    • so_lists: 76ms vs 410ms, 5.4x más rápido
    • binary_trees: 11ms vs 40ms, 3.6x más rápido
    • linked_list: 136ms vs 388ms, 2.9x más rápido
    • gcbench: 1,845ms vs 3,641ms, 2.0x más rápido
  • Programas reales

    • json_parse: 39ms vs 394ms, 10.1x más rápido
    • cálculo de bigint_fib de 1000 dígitos: 2ms vs 16ms, 8.0x más rápido
    • ao_render: 417ms vs 3,334ms, 8.0x más rápido
    • pidigits: 2ms vs 13ms, 6.5x más rápido
    • str_concat: 2ms vs 13ms, 6.5x más rápido
    • template engine: 152ms vs 936ms, 6.2x más rápido
    • csv_process: 234ms vs 860ms, 3.7x más rápido
    • io_wordcount: 33ms vs 97ms, 2.9x más rápido

Funciones de Ruby soportadas

  • Como funciones Core, soporta classes, inheritance, super, mixins con include, attr_accessor, Struct.new, alias, constantes de módulo y open classes sobre tipos integrados
  • En Control Flow soporta if/elsif/else, unless, case/when, pattern matching con case/in, while, until, loop, for..in, break, next, return, catch/throw y &.
  • En Blocks soporta yield, block_given?, &block, proc {}, Proc.new, -> x { }, method(:name), e incluye métodos con bloques como each, map, select, reduce, sort_by, times, upto y downto
  • En Exceptions soporta begin/rescue/ensure/retry, raise y clases de excepción definidas por el usuario
  • Los Types incluyen Integer, Float, String, Array, Hash, Range, Time, StringIO, File, Regexp, Bigint y Fiber
    • Los valores polimórficos se manejan como tagged unions
    • Para estructuras de datos autorreferenciales existen tipos de objeto anulables T?
  • Las Global Variables compilan $name como variables C estáticas, y las incompatibilidades de tipo se detectan en tiempo de compilación
  • En I/O soporta puts, print, printf, p, gets, ARGV, ENV[], File.read/write/open, system() y backticks

Strings, expresiones regulares, symbols, Bigint y Fiber

  • Los Strings manejan tanto strings inmutables como mutables, y << promueve automáticamente a string mutable sp_String para hacer append in-place en O(n)
  • +, la interpolación, tr, ljust/rjust/center y métodos estándar funcionan sobre ambas representaciones de string
  • Comparaciones como s[i] == "c" se optimizan para acceder directamente al arreglo de chars y se resuelven sin asignaciones
  • Concatenaciones como a + b + c + d se aplanan en una sola llamada a sp_str_concat4 o sp_str_concat_arr, reduciendo a N-1 asignaciones menos
  • str.split(sep) dentro de un loop reutiliza el mismo sp_StrArray, y en csv_process elimina 4 millones de asignaciones
  • Regexp usa un motor regexp NFA integrado sin dependencias externas
    • Soporta =~, $1-$9, match?, gsub, sub, scan y split
  • Bigint usa enteros de precisión arbitraria basados en mruby-bigint
    • Se promueve automáticamente en patrones como multiplicaciones repetidas en loops, por ejemplo q = q * k
    • Se enlaza como librería estática y solo se incluye cuando realmente se usa
  • Fiber ofrece concurrencia cooperativa basada en ucontext_t
    • Soporta Fiber.new, Fiber#resume, Fiber.yield y paso de valores
    • Las variables libres se capturan como celdas promovidas al heap
  • Symbols se implementan con un tipo sp_sym separado de String
    • Conserva :a != "a"
    • Los literales symbol se internan en tiempo de compilación y se convierten en constantes SPS_name
    • String#to_sym usa un pool dinámico solo cuando hace falta
    • Los hashes con claves symbol usan sp_SymIntHash, almacenando claves enteras directas en lugar de strings, por lo que desaparecen strcmp y la asignación dinámica de strings

Gestión de memoria y value types

  • La gestión de memoria usa mark-and-sweep GC, con size-segregated free lists, marcado no recursivo y sticky mark bits
  • Las clases pequeñas y simples se promueven automáticamente a value types y se ubican en la pila
    • condición: hasta 8 campos escalares
    • sin herencia
    • sin modificaciones a través de parámetros
  • Asignar un millón de veces una clase de 5 campos baja de 85ms a 2ms
  • Los programas que solo usan value types no exportan en absoluto el runtime de GC

Optimizaciones

  • Realiza varias optimizaciones en tiempo de compilación basadas en inferencia de tipos a nivel de programa completo
  • Con Value-type promotion, las clases pequeñas e inmutables se convierten en objetos stack C struct y eliminan el overhead del GC
  • Con Constant propagation, constantes literales simples como N = 100 se insertan directamente en los sitios de uso sin consultar cst_N
  • Con Loop-invariant length hoisting, while i < arr.length calcula la longitud una sola vez antes del loop
    • Si en el cuerpo se modifica el receptor, como con arr.push, ese hoist se desactiva
  • Method inlining agrega static inline a métodos cortos, no recursivos y de hasta 3 sentencias para inducir el inlining de gcc
  • String concat chain flattening reduce cadenas de concatenación a una sola llamada y evita crear strings intermedios
  • Bigint auto-promotion promueve automáticamente a bigint patrones de suma autorreferencial o multiplicación repetida
  • Bigint to_s usa mpz_get_str de mruby-bigint con estrategia divide-and-conquer O(n log²n)
  • Static symbol interning convierte "literal".to_sym en constantes SPS_<name> en tiempo de compilación, e incorpora el pool de runtime solo cuando hace falta interning dinámico
  • En sub_range, strings con longitud previamente elevada usan sp_str_sub_range_len para saltarse la llamada interna a strlen
  • line.split(",") dentro de loops reutiliza el sp_StrArray existente
  • Dead-code elimination usa -ffunction-sections -fdata-sections y --gc-sections para eliminar del binario final funciones del runtime que no se usan
  • Iterative inference early exit detiene de inmediato el loop de punto fijo si tres arreglos de firmas de parámetros, retornos e ivars dejan de cambiar
    • La mayoría de los programas converge en 1 o 2 pasadas completas en lugar de 4
    • El tiempo de bootstrap baja alrededor de 14%
  • El byte walk de parse_id_list reemplaza s.split(",") por un recorrido manual con s.bytes[i] en el parser de listas de campos AST, llamado unas 120 mil veces durante la autocompilación, bajando las asignaciones por llamada de N+1 a 2
  • El código C generado mantiene un warning-free build con el nivel normal de warnings, y el harness usa -Werror para exponer regresiones de inmediato

Arquitectura

  • La estructura del repositorio se divide en los siguientes elementos
    • spinel: script wrapper de un solo comando basado en POSIX shell
    • spinel_parse.c: frontend C de 1,061 líneas desde libprism hasta AST textual
    • spinel_codegen.rb: backend del compilador de 21,109 líneas desde AST hasta código C
    • lib/sp_runtime.h: header de librería runtime de 581 líneas
    • lib/sp_bigint.c: enteros de precisión arbitraria, 5,394 líneas
    • lib/regexp/: motor regexp integrado, 1,759 líneas
    • test/: 74 pruebas funcionales
    • benchmark/: 55 benchmarks
    • Makefile: automatización de build
  • El runtime lib/sp_runtime.h concentra en un solo header el GC, la implementación de array/hash/string y otros soportes de runtime
  • El código C generado incluye este header, y el linker toma solo lo necesario desde libspinel_rt.a
    • bigint
    • regexp engine
  • El parser tiene dos implementaciones
    • spinel_parse.c enlaza libprism directamente y funciona sin CRuby
    • spinel_parse.rb es un fallback de CRuby que usa el gem Prism
  • Ambos parsers generan la misma salida AST, y el wrapper spinel prioriza el binario C cuando es posible
  • require_relative se resuelve en tiempo de parseo y los archivos referenciados se insertan inline

Limitaciones

  • No eval: eval, instance_eval y class_eval no están soportados
  • No metaprogramming: send, method_missing y define_method dinámico no están soportados
  • No threads: Thread y Mutex no están soportados; solo Fiber
  • No encoding: asume UTF-8 y ASCII
  • No general lambda calculus: no maneja llamadas [] ni -> x { } profundamente anidadas

Dependencias y modelo de ejecución

  • Las dependencias de build son la librería C libprism y CRuby para el bootstrap inicial
  • No hay dependencias en runtime, y los binarios generados solo requieren libc + libm
  • Las expresiones regulares usan el motor integrado, por lo que no hace falta ninguna librería externa
  • Bigint está integrado, pero solo se enlaza cuando realmente se usa
  • Prism es el parser de Ruby usado por spinel_parse
    • make deps descarga el tarball del gem prism desde rubygems.org y extrae el código C en vendor/prism
    • Si el gem prism ya está instalado, se detecta automáticamente
    • También se puede indicar una ruta personalizada con PRISM_DIR=/path/to/prism
  • CRuby solo se necesita para el bootstrap inicial, y después de make todo el pipeline corre sin Ruby

Historial del proyecto

  • Spinel fue implementado primero en C, con una escala de 18K lines, y permanece en la rama c-version
  • Luego pasó por la rama ruby-v1, reescrita en Ruby
  • El master actual es la versión reescrita otra vez en un subconjunto de Ruby capaz de self-hosting

Licencia

  • Usa la licencia MIT
  • Sigue el archivo LICENSE

1 comentarios

 
GN⁺ 5 일 전
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.parse eran un obstáculo
    Ahora, 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_missing y define_method en código Ruby real, y también cómo suelen manejar entradas sin tipo, por ejemplo datos JSON

    • Este diseño se ve bastante pragmático
      Parsear 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_missing y define_method son muy comunes
      Las restricciones son parecidas a las de mruby, y aun bajo esas restricciones sí hay casos de uso
      El soporte para send, method_missing y define_method es relativamente fácil
      En 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 sencilla
      Por ejemplo, si la cadena que entra a eval() se puede conocer o descomponer estáticamente, hay bastante margen para resolverlo
      De 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ático
      En mi compilador también pienso empezar por ahí si eso se vuelve el cuello de botella
    • Se necesitan bastantes funciones así para crear la magia al estilo Rails
      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
    • Si compila Ruby a Objective-C, parecería posible soportar todas las funciones de Ruby y aun así ser más rápido que Ruby interpretado
    • Yo sí uso eval con frecuencia
      Podría no usarlo, pero para mí así es más ergonómico
    • En mi experiencia, lo interesante es eval, exec, define_method y también el patrón de crear clases nuevas con Class.new y Struct.new
      La 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

    • La gente habla mucho de IA que escribe programas enteros de principio a fin, pero creo que un escenario más realista es que convierta a un programador 10x en un programador 100x
      En alguien como Matz, eso podría significar empujarlo de 100x a 500x
    • La referencia más reciente que tengo de Spinel es la de Steven Universe, así que no entendí para nada el juego de palabras Spinel/Ruby (Moon), pero ahora que lo sé me alegró el día
    • Yo naturalmente pensé que hablaban del mineral spinel :)
      https://en.wikipedia.org/wiki/Spinel
    • Gracias
      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
    • La historia del origen del nombre del gato se ve bastante sospechosa si se piensa en el drama de Ruby Central y la relación con los fundadores de Spinel.coop
      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.rb tiene 21 mil líneas, y algunos métodos llegan a 15 niveles de anidación
    El 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

    • El código de compiladores sí puede quedar bonito si se le dedica tiempo
      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.rb está casi al nivel de horror eldritch
      Cuando 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 Set y lo manejas con include?, queda más corto, más rápido, más legible y más fácil de mantener
      Pero 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 lib parece corresponder al directorio ext del repo principal de Ruby y tiene una calidad decente
      La 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...
    • En esta etapa no creo que sea tan importante si una persona puede mantenerlo a mano
      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_eval
    No 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

    • Con eso desaparece buena parte de la magia de Ruby
  • 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

    • Si lo que quieres es solo la estética de Ruby, Crystal también podría encajar bien
      La sintaxis es parecida y tiene un sistema de tipos estático, lo que lleva a código compilado más eficiente
    • Que no haya eval hasta me parece mejor, pero que tampoco haya threads ni mutexes sí me decepciona
      La ausencia de define_method se entiende por su caso de uso
      Pero send y method_missing son comunes en librerías existentes, y tampoco parecería tan difícil implementarlos construyendo en compilación una tabla de lookup en memoria
      Así 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
    • La ventaja de la metaprogramación nunca fue originalmente escribir menos código
      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 eval o metaprogramación, aunque parece que eligieron eso para concentrarse en un subconjunto pequeño y de alto rendimiento
    Ojalá 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

    • Sí, yo también hace poco tuve que hacer fork de warbler
      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

    • Aún no he visto evidencia de que hayan decidido no soportarlo de forma deliberada
      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

    • Toda la industria arranca instalando agent harness, SOUL.md, permisos, skills, MCPs, hooks y env
      Matz en cambio parece sentir que con gem env|info y find ya basta
  • Siendo 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

    • Crystal tiene un sistema de tipos estático explícito y está optimizado a nivel de lenguaje para compilación AOT
      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/
    • Visto desde otro ángulo, parece que eventualmente los LLM van a llegar al punto de producir una especificación formal en cualquier lenguaje que queramos
      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