1 points par GN⁺ 6 일 전 | 1 commentaires | Partager sur WhatsApp
  • Un compilateur AOT qui effectue une inférence de types à l’échelle du programme entier sur le code source Ruby, puis le convertit en code C afin de générer un binaire natif autonome
  • Développé directement par matz, le créateur de Ruby ; le backend du compilateur lui-même est écrit en Ruby dans une architecture auto-hébergée capable de se compiler lui-même
  • Des performances environ 11,6 fois supérieures à miniruby (Ruby 4.1.0dev) ; Conway's Game of Life est 86,7 fois plus rapide, ackermann 74,8 fois, et mandelbrot 58,1 fois
  • Le pipeline de compilation transforme d’abord Ruby en texte AST via un parseur basé sur Prism, puis le backend auto-hébergé réalise l’inférence de types et la génération de code C, avant qu’un compilateur C standard ne produise un binaire standalone
  • Prise en charge d’un large éventail de fonctionnalités Ruby : classes, héritage, blocs, gestion des exceptions, Fiber, moteur Regexp NFA intégré, Bigint à promotion automatique, pattern matching, etc.
  • Les petites classes avec 8 champs scalaires ou moins sont automatiquement allouées sur la pile comme value types, éliminant totalement la surcharge du GC (1 million d’allocations : 85 ms → 2 ms)
  • Les concaténations de chaînes a + b + c + d sont aplaties en un seul malloc, et split dans une boucle réutilise sp_StrArray pour supprimer les allocations inutiles
  • Nombreuses optimisations à la compilation : remontée hors boucle des longueurs invariantes, propagation de constantes, inlining automatique des méthodes de 3 instructions ou moins, arrêt anticipé de l’inférence répétée (~14 % de réduction du temps de bootstrap), etc.
  • Les binaires générés ont zéro dépendance d’exécution et peuvent fonctionner avec seulement libc + libm
  • eval, la métaprogrammation, ainsi que Thread et Mutex ne sont pas pris en charge (seul Fiber est supporté)
  • Licence MIT

1 commentaires

 
GN⁺ 6 일 전
Avis sur Hacker News
  • Si c’est Matz qui l’a fait, il connaît sûrement très bien les limites de la sémantique Ruby, donc ça inspire confiance
    Mon mémoire de master portait aussi sur un compilateur JS AOT ; ça fonctionnait, mais les contraintes sur les données d’entrée étaient trop fortes, donc j’ai fini par abandonner
    À l’époque, les développeurs JS n’étaient pas vraiment habitués à s’imposer ce genre de contraintes, et des entrées fondamentalement indéterminables comme JSON.parse posaient problème
    Aujourd’hui, avec TypeScript, ce serait peut-être bien plus réaliste qu’à l’époque
    Rien qu’avec le lambda calculus classique, les limites de l’inférence de types sont évidentes, et on retrouve des contraintes similaires dans les papiers de Matt Might ou dans le travail sur Shed-skin Python
    Je me demande à quel point eval, send, method_missing et define_method sont fréquents dans du vrai code Ruby, et aussi comment on gère en général du parsing non typé, par exemple des entrées JSON

    • Cette conception a l’air assez pragmatique
      Le parsing Ruby est presque plus difficile que la traduction elle-même, donc ils utilisent Prism, puis génèrent du C
      La sémantique Ruby de base n’est pas si difficile à implémenter
      À l’inverse, moi je m’accroche à un vieux compilateur AOT auto-hébergé écrit en Ruby pur, et comme j’ai insisté pour utiliser mon propre parseur, je me suis volontairement compliqué la vie
      J’ai appris assez tôt que les premiers 80 % peuvent être bricolés et permettent déjà d’exécuter une grosse partie du code Ruby, et que le vrai « deuxième 80 % » se concentre sur tout ce que Matz a laissé de côté ici et dans mruby, comme les encodings et toutes sortes de fonctionnalités périphériques
      Honnêtement, Ruby a aussi pas mal de fonctionnalités que je n’ai jamais vues une seule fois dans du vrai code, donc ça ne me choquerait pas que certaines soient dépréciées
      send, method_missing et define_method sont très courants
      Les contraintes ressemblent à celles de mruby, et il y a quand même des usages possibles dans ce cadre
      Le support de send, method_missing et define_method est relativement facile
      En revanche, le support de eval() est extrêmement pénible
      Cela dit, dans Ruby, une grande partie des usages de eval() peut être ramenée statiquement à la version bloc de instance_eval, et dans ces cas-là, la compilation AOT devient assez simple
      Par exemple, s’il est possible de connaître statiquement ou de décomposer la chaîne passée à eval(), il y a beaucoup plus de marge de manœuvre
      En pratique, beaucoup d’usages de eval() sont inutiles ou servent juste à contourner simplement l’introspection, donc une analyse statique peut les traiter
      Dans mon compilateur aussi, si ça devient le goulot d’étranglement, c’est par là que je commencerai
    • Il faut sans doute beaucoup de ce genre de fonctionnalités pour produire la magie façon Rails
      L’ingestion de JSON non typé utilisera probablement aussi ce type de mécanisme
      Si on enlève tout ça, il reste un langage petit et lisible, moins fortement typé que Crystal mais qui dépend aussi moins de la métaprogrammation que Ruby officiel
      Donc le potentiel a l’air réel, mais au final seul le temps le dira
    • Si l’approche consiste à compiler Ruby en Objective-C, on pourrait peut-être supporter toutes les fonctionnalités de Ruby tout en restant plus rapide que Ruby interprété
    • Moi, j’utilise souvent eval
      Je pourrais m’en passer, mais pour moi c’est plus ergonomic
    • Dans mon expérience, ce qui est intéressant, ce sont eval, exec, define_method, ainsi que le pattern qui consiste à créer de nouvelles classes avec Class.new et Struct.new
      La plupart de ces usages se concentrent au démarrage de l’application ou pendant le chargement de fichiers via require, ce qui ressemble déjà, d’une certaine façon, à une étape de compilation
  • C’est ce que Matz vient tout juste de présenter à RubyKaigi 2026
    C’est expérimental, mais ça a été construit en environ un mois avec l’aide de Claude, et la démo en direct a fonctionné
    Le nom vient du nouveau chat de Matz, lui-même nommé d’après le chat de Card Captor Sakura, qui forme à son tour une paire avec un personnage nommé Ruby

    • On entend souvent dire que l’IA va écrire des programmes de A à Z, mais le scénario le plus réaliste me semble plutôt être de transformer un programmeur 10x en programmeur 100x
      Et pour quelqu’un comme Matz, ça pourrait même faire passer de 100x à 500x
    • Dans ma tête, le Spinel le plus récent, c’est celui de Steven Universe, donc je n’avais absolument pas saisi le jeu de mots Spinel/Ruby (Moon), mais maintenant que je le sais, ma journée est meilleure
    • J’ai naturellement cru qu’il s’agissait du minéral spinelle :)
      https://en.wikipedia.org/wiki/Spinel
    • Merci
      La vidéo n’a pas l’air encore en ligne, on dirait qu’elles sont publiées une par une sur cette chaîne
      https://www.youtube.com/@rubykaigi4884/videos
    • Cette histoire sur l’origine du nom du chat paraît assez suspecte si l’on pense au drama Ruby Central et aux relations avec les fondateurs de Spinel.coop
      Le nom du projet donne aussi l’impression d’avoir été choisi de manière émotionnelle
  • C’est évidemment très impressionnant, mais sans agent IA, ça a l’air impossible à maintenir
    spinel_codegen.rb fait 21 000 lignes, et certaines méthodes sont imbriquées jusqu’à 15 niveaux
    Le code de compilateur est rarement joli au départ, mais même selon ce standard, celui-ci semble extrêmement difficile à maintenir humainement

    • Le code de compilateur peut tout à fait être propre, si on a le temps
      Les frontières entre sous-systèmes y sont nettes et les handoffs entre phases bien définis, donc c’est même l’un des domaines les plus faciles à rendre modulaires
      Le problème, en général, c’est qu’on trouve le temps de le faire marcher, mais pas de le refactorer ensuite, et la saleté s’accumule
    • spinel_codegen.rb est quasiment au niveau horreur indicible
      Avec Claude, je finis toujours avec ce genre de spaghetti code, donc je me demandais ce que je faisais mal
      Mais en voyant qu’un projet vraiment intéressant, écrit par quelqu’un que je considère comme un programmeur de tout premier plan, contient lui aussi du code d’assez mauvaise qualité par endroits, je me dis que ça ne m’arrive pas qu’à moi
      Par exemple, infer_comparison_type() n’est pas le pire cas et ce n’est pas illisible, mais il existe une implémentation bien plus simple et claire, et pourtant Claude n’y arrive pas
      Regrouper les opérateurs de comparaison dans un Set puis utiliser include? serait plus court, plus rapide, plus lisible et plus facile à maintenir
      Mais Claude dérive toujours vers des enchaînements de if-return, et on a presque l’impression que même le if-else lui est étranger
      Mon propre code généré avec Claude est lui aussi plein de ce genre de patterns, donc ça me rassure de voir que je ne suis pas le seul
      En revanche, les autres fichiers sont bien meilleurs, surtout le répertoire lib, qui semble correspondre au répertoire ext du dépôt Ruby principal et dont la qualité est correcte
      L’API porte clairement l’influence de MRI Ruby et, même si l’implémentation diffère beaucoup, Matz semble avoir orienté Claude pour qu’elle ressemble partiellement à l’API d’origine, ce qui rend le résultat plus cohérent
      [1] https://github.com/matz/spinel/blob/98d1179670e4d6486bbd1547...
    • À ce stade, la question de savoir si un humain peut le maintenir à la main ne me paraît pas si importante
      Si les tests et les benchmarks passent, ça me suffit pour l’instant
      En revanche, je me demande si un fichier gigantesque est facile à manipuler même pour une IA
      J’essaie de limiter mes fichiers à moins de 300 lignes, et je pense qu’un code facile à comprendre pour un humain le sera aussi pour les agents de code
  • D’après ce qui a été dit, les contraintes sont les suivantes
    No eval : eval, instance_eval, class_eval
    No metaprogramming : send, method_missing, define_method (dynamique)
    No threads : Thread, Mutex (Fiber est pris en charge)
    No encoding : hypothèse UTF-8/ASCII
    No general lambda calculus : -> x { } profondément imbriqué avec appels []
    L’hypothèse UTF-8/ASCII n’est pas une grosse contrainte pour moi, mais le reste risque d’être une vraie limitation pour beaucoup de programmes
    Et les réintroduire demanderait visiblement un travail conséquent

    • Dans ce cas, une bonne partie de la magie de Ruby disparaît
  • J’utilise Ruby depuis longtemps, et après avoir utilisé toutes les fonctionnalités listées, j’ai plutôt l’impression qu’au terme de cette évolution, c’est justement cette version de Ruby simplifié que j’ai fini par vouloir
    C’est plus simple, plus facile à comprendre, tout en conservant l’esthétique propre à Ruby
    Avec les LLM, la productivité en génération de code est maintenant si élevée qu’il est moins nécessaire qu’avant de réduire le boilerplate via la métaprogrammation pour améliorer la productivité des développeurs
    Parce que la part du code réellement écrite à la main par les développeurs diminue elle-même

    • Si ce qu’on veut, c’est simplement l’esthétique Ruby, alors Crystal pourrait aussi très bien convenir
      La syntaxe est proche et il y a un système de types statique, ce qui conduit à un code compilé plus efficace
    • L’absence de eval ne me gêne pas vraiment, mais ne pas avoir threads et mutexes, c’est dommage
      L’absence de define_method se comprend vu ses usages
      En revanche, send et method_missing sont fréquents dans les bibliothèques existantes, et leur implémentation ne semble pas si difficile si on construit à la compilation une table de lookup en mémoire
      Donc je ne sais pas si c’est un choix délibéré ou si ce n’est simplement pas encore fait
      J’espère que c’est le second cas, mais au moins à court terme, la compatibilité empêchera sans doute un usage en production
    • L’avantage de la métaprogrammation n’a jamais été au fond de réduire la quantité de code écrit
      C’était de réduire la quantité de code à lire
  • C’est vraiment formidable, et j’attends depuis longtemps un compilateur AOT pour Ruby
    Cela dit, c’est dommage qu’il n’y ait pas de fallback pour eval ou la métaprogrammation, même si ça semble découler d’un choix de se concentrer sur un petit sous-ensemble très performant
    J’aimerais que les gems produites avec ce compilateur AOT interagissent bien avec MRI et Ruby
    Pour empaqueter ou regrouper du Ruby standard et des gems, il faut toujours des outils comme tebako, kompo, ocran, et auparavant il y avait aussi des projets comme ruby-packer, traveling ruby ou jruby warbler
    C’est bien d’avoir une option de plus, mais j’espère toujours une solution définitive avec une meilleure UX développeur

    • Oui, j’ai d’ailleurs dû forker warbler récemment
      Ça faisait trop longtemps qu’il n’avait pas été mis à jour
  • Je me demande pourquoi pas de threads
    Le scheduler Ruby et l’implémentation pthread en dessous semblent pouvoir fonctionner aussi en C, donc je me demande s’il s’agit d’un choix visant le zéro dépendance
    S’il n’est ni prévu comme extension optionnelle plus tard, ni simplement pas encore implémenté, ce choix me paraît un peu étrange

    • Je n’ai encore rien vu qui montre qu’ils ont délibérément décidé de ne pas le prendre en charge
      À mon avis, ils n’en sont simplement pas encore là
      Le multithreading est de toute façon notoirement très difficile à bien faire
  • C’est surprenant que ça ait été fait en un peu plus d’un mois
    On peut dire ce qu’on veut sur l’IA, mais entre les mains d’un développeur compétent, elle apporte un gain de vitesse énorme

    • Toute l’industrie commence avec des agent harness, SOUL.md, réglages de permissions, skills, MCPs, hooks, env, etc.
      Matz, lui, donne l’impression que gem env|info et find lui suffisent largement
  • Vu que c’est Matz qui l’a fait, je me demande à quel point il est réaliste que ça devienne un jour une partie du Ruby core
    Et si c’était le cas, je me demande aussi à quel point ce serait une menace pour Crystal

    • Crystal a un système de types statique explicite et a été optimisé au niveau du langage pour la compilation AOT
      Ces caractéristiques sont pratiquement indispensables pour compiler et maintenir de gros programmes
      Ici, au contraire, on parle d’un sous-ensemble restreint de Ruby, donc la plupart des gems Ruby populaires ne fonctionneront probablement pas telles quelles
      En tant que sous-ensemble de langage visant la compilation en C, ça ressemble davantage à PreScheme
      À ce stade, je ne vois pas les deux en concurrence directe sur le même terrain
      Le Ruby complet aura presque certainement besoin d’un JIT
      [1]: https://prescheme.org/
    • Vu sous un autre angle, on finira peut-être par atteindre un point où les LLM produiront une spécification formelle dans n’importe quel langage qu’on souhaite
      Ce sera une sorte de revanche du Rational Unified Process et d’Enterprise Architect
      La différence, c’est qu’au lieu de diagrammes UML, on recevra des fichiers markdown
  • Ça semble utile du côté des outils d’infrastructure
    On peut imaginer, par exemple, un bundler écrit en Ruby mais compilé statiquement, qui ferait aussi office d’outil d’installation Ruby à la manière de RVM
    Les buildpacks Ruby existants sont écrits en Ruby, mais il faut les amorcer avec bash, ce qui est pénible et crée des cas limites
    CNB a été écrit en Rust pour éviter ce problème, et l’idée de pouvoir distribuer un binaire unique sans dépendance est vraiment puissante