1P by GN⁺ 8시간전 | ★ favorite | 댓글 1개
  • Ruby 코드를 독립 실행형 네이티브 바이너리로 바꾸고, 전체 프로그램 단위 타입 추론과 C 코드 생성을 통해 최신 CRuby miniruby 대비 기하평균 약 11.6배 빠른 실행을 노림
  • 컴파일 파이프라인은 Prism 기반 파서로 Ruby를 AST 텍스트로 바꾼 뒤 self-hosting 백엔드가 타입 추론과 C 코드 생성을 수행하고, 표준 C 컴파일러로 standalone 바이너리를 만듦
  • 컴파일러 백엔드는 Ruby로 작성된 self-hosting 구조를 갖고 있으며, 부트스트랩 과정을 거쳐 gen2.c == gen3.c가 성립해 자기 자신을 다시 컴파일하는 루프가 닫힘
  • 문자열 연결 평탄화, value-type promotion, loop-invariant length hoisting, static symbol interning, bigint 자동 승격 같은 컴파일 타임 최적화를 넣고, 내장 regexp 엔진과 bigint, 단일 헤더 런타임으로 외부 런타임 의존성을 줄임
  • eval, 메타프로그래밍, Thread, 일반적인 인코딩 처리는 지원하지 않지만, Ruby 없이 실행되는 배포 형태와 계산 집약적 워크로드에서의 큰 성능 차이로 Ruby AOT 컴파일의 실용성이 드러남

동작 방식

  • 컴파일 파이프라인은 Ruby 파일을 파싱해 AST 텍스트 파일로 직렬화한 뒤, 타입 추론과 C 코드 생성을 거쳐 표준 C 컴파일러로 네이티브 바이너리를 만드는 흐름으로 이루어짐
  • spinel_parse는 Prism과 libprism을 사용해 Ruby를 파싱하며, C 바이너리가 없을 때는 CRuby와 Prism gem을 사용하는 대체 경로를 사용함
  • spinel_codegen은 self-hosted 네이티브 바이너리로 동작하며, AST를 받아 타입 추론 + C 코드 생성을 수행함
  • 최종 단계는 cc -O2 -Ilib -lm으로 C 소스와 런타임 헤더를 함께 컴파일하며, 결과 바이너리는 standalone 형태로 만들어짐

Self-Hosting

  • 부트스트랩 체인은 CRuby + spinel_parse.rb로 AST를 만들고, CRuby + spinel_codegen.rbgen1.cbin1을 만든 뒤, 다시 생성된 바이너리로 gen2.c, gen3.c를 만드는 방식으로 닫힘
  • gen2.c == gen3.c가 성립해 bootstrap loop가 닫혔음을 명시함
  • 백엔드인 spinel_codegen.rb는 Spinel이 직접 컴파일 가능한 Ruby 부분집합으로 작성됨
    • classes, def, attr_accessor
    • if/case/while
    • each/map/select, yield
    • begin/rescue
    • String, Array, Hash 연산과 File I/O
  • 백엔드에는 metaprogramming, eval, require를 넣지 않음

성능과 벤치마크

  • 테스트는 74개 통과, 벤치마크는 55개 통과 상태임
  • 28개 벤치마크 기준 기하평균은 최신 CRuby miniruby 대비 약 11.6배 빠름
  • 비교 기준은 번들 gem이 없는 최신 CRuby miniruby 빌드이며, 시스템 ruby 3.2.3보다 더 빠른 기준과 비교했어도 계산 집약적 워크로드에서 우위가 큼
  • 계산 성능

    • life는 20ms 대 1,733ms로 86.7배 빠름
    • ackermann은 5ms 대 374ms로 74.8배 빠름
    • mandelbrot는 25ms 대 1,453ms로 58.1배 빠름
    • fib 재귀 버전은 17ms 대 581ms로 34.2배 빠름
    • nqueens는 10ms 대 304ms로 30.4배 빠름
    • tarai는 16ms 대 461ms로 28.8배 빠름
    • tak는 22ms 대 532ms로 24.2배 빠름
    • matmul은 13ms 대 313ms로 24.1배 빠름
    • sudoku는 6ms 대 102ms로 17.0배 빠름
    • partial_sums는 93ms 대 1,498ms로 16.1배 빠름
    • fannkuch는 2ms 대 19ms로 9.5배 빠름
    • sieve는 39ms 대 332ms로 8.5배 빠름
    • fasta는 3ms 대 21ms로 7.0배 빠름
  • 데이터 구조와 GC

    • rbtree는 24ms 대 543ms로 22.6배 빠름
    • splay tree는 14ms 대 195ms로 13.9배 빠름
    • huffman은 6ms 대 59ms로 9.8배 빠름
    • so_lists는 76ms 대 410ms로 5.4배 빠름
    • binary_trees는 11ms 대 40ms로 3.6배 빠름
    • linked_list는 136ms 대 388ms로 2.9배 빠름
    • gcbench는 1,845ms 대 3,641ms로 2.0배 빠름
  • 실제 프로그램

    • json_parse는 39ms 대 394ms로 10.1배 빠름
    • bigint_fib 1000자리 계산은 2ms 대 16ms로 8.0배 빠름
    • ao_render는 417ms 대 3,334ms로 8.0배 빠름
    • pidigits는 2ms 대 13ms로 6.5배 빠름
    • str_concat는 2ms 대 13ms로 6.5배 빠름
    • template engine은 152ms 대 936ms로 6.2배 빠름
    • csv_process는 234ms 대 860ms로 3.7배 빠름
    • io_wordcount는 33ms 대 97ms로 2.9배 빠름

지원하는 Ruby 기능

  • Core 기능으로 classes, inheritance, super, include 믹스인, attr_accessor, Struct.new, alias, module constants, 내장 타입에 대한 open classes를 지원함
  • Control Flowif/elsif/else, unless, case/when, case/in 패턴 매칭, while, until, loop, for..in, break, next, return, catch/throw, &.를 지원함
  • Blocksyield, block_given?, &block, proc {}, Proc.new, -> x { }, method(:name)를 지원하며, each, map, select, reduce, sort_by, times, upto, downto 같은 블록 메서드도 포함함
  • Exceptionsbegin/rescue/ensure/retry, raise, 사용자 정의 예외 클래스를 지원함
  • Types는 Integer, Float, String, Array, Hash, Range, Time, StringIO, File, Regexp, Bigint, Fiber를 포함함
    • 다형성 값은 tagged unions로 처리함
    • 자기 참조 데이터 구조를 위해 nullable object types T?를 둠
  • Global Variables$name을 정적 C 변수로 컴파일하며, 타입 불일치는 컴파일 시점에 검출함
  • I/Oputs, print, printf, p, gets, ARGV, ENV[], File.read/write/open, system(), 백틱을 지원함

문자열, 정규식, 심볼, Bigint, Fiber

  • Strings는 불변과 가변 문자열을 모두 다루며, <<는 자동으로 가변 문자열 sp_String으로 승격되어 O(n) 제자리 append를 수행함
  • +, interpolation, tr, ljust/rjust/center와 표준 메서드가 두 문자열 표현 모두에서 동작함
  • s[i] == "c" 같은 비교는 char 배열에 직접 접근하도록 최적화되어 할당 없이 처리됨
  • a + b + c + d 같은 연결은 sp_str_concat4 또는 sp_str_concat_arr 한 번으로 평탄화되어 N-1개 적은 할당으로 바뀜
  • 루프 안의 str.split(sep)는 같은 sp_StrArray를 반복 재사용하며, csv_process에서 400만 개 할당 제거가 일어남
  • Regexp는 외부 의존성 없는 내장 NFA regexp engine을 사용함
    • =~, $1-$9, match?, gsub, sub, scan, split를 지원함
  • Bigint는 mruby-bigint 기반 임의 정밀도 정수를 사용함
    • q = q * k 같은 루프 곱셈 패턴에서 자동 승격됨
    • 정적 라이브러리로 링크되며 실제 사용할 때만 포함됨
  • Fiberucontext_t 기반 협력적 동시성을 제공함
    • Fiber.new, Fiber#resume, Fiber.yield와 값 전달을 지원함
    • 자유 변수는 heap-promoted cells로 캡처함
  • Symbols는 문자열과 분리된 sp_sym 타입으로 구현됨
    • :a != "a"를 유지함
    • 심볼 리터럴은 컴파일 시점에 intern되어 SPS_name 상수가 됨
    • String#to_sym은 필요할 때만 동적 풀을 사용함
    • 심볼 키 해시는 sp_SymIntHash를 사용해 문자열 대신 정수 키를 직접 저장하므로 strcmp와 동적 문자열 할당이 사라짐

메모리 관리와 값 타입

  • 메모리 관리는 mark-and-sweep GC를 사용하며, size-segregated free lists, non-recursive marking, sticky mark bits를 포함함
  • 작고 단순한 클래스는 자동으로 value types로 승격되어 스택에 배치됨
    • 조건은 스칼라 필드 8개 이하
    • 상속 없음
    • 파라미터를 통한 변경 없음
  • 필드 5개짜리 클래스 100만 회 할당은 85ms에서 2ms로 줄어듦
  • 값 타입만 사용하는 프로그램은 GC 런타임 자체를 전혀 내보내지 않음

최적화

  • 전체 프로그램 단위 타입 추론을 바탕으로 여러 컴파일 타임 최적화를 수행함
  • Value-type promotion으로 작은 불변 클래스는 C struct 스택 객체가 되어 GC 오버헤드를 없앰
  • Constant propagation으로 N = 100 같은 단순 리터럴 상수는 cst_N 조회 없이 사용 지점에 직접 인라인됨
  • Loop-invariant length hoisting으로 while i < arr.length는 루프 전에 길이를 한 번만 계산함
    • 본문에서 arr.push처럼 수신 객체를 바꾸면 이 hoist는 비활성화됨
  • Method inlining은 짧고 재귀가 아닌 메서드 3문장 이하에 static inline을 붙여 gcc 인라이닝을 유도함
  • String concat chain flattening은 연결 체인을 단일 호출로 줄여 중간 문자열 생성을 없앰
  • Bigint auto-promotion은 자기 참조 덧셈이나 반복 곱셈 패턴을 bigint로 자동 승격함
  • Bigint to_s 는 mruby-bigint의 mpz_get_str을 써서 divide-and-conquer O(n log²n) 으로 처리함
  • Static symbol interning"literal".to_sym을 컴파일 타임 SPS_<name> 상수로 바꾸며, 동적 interning을 쓸 때만 런타임 풀을 넣음
  • sub_range에서 길이가 hoist된 문자열은 sp_str_sub_range_len을 사용해 내부 strlen 호출을 건너뜀
  • 루프 내부 line.split(",")는 기존 sp_StrArray를 재사용함
  • Dead-code elimination-ffunction-sections -fdata-sections--gc-sections를 사용해 쓰이지 않는 런타임 함수를 최종 바이너리에서 제거함
  • Iterative inference early exit는 param, return, ivar의 서명 배열 3개가 더 이상 바뀌지 않으면 고정점 루프를 즉시 중단함
    • 대부분 프로그램은 4회 전체 반복 대신 1~2회에 수렴함
    • bootstrap 시간은 약 14% 줄어듦
  • parse_id_list byte walk는 self-compile 동안 약 12만 회 호출되는 AST 필드 리스트 파서를 s.split(",") 대신 s.bytes[i] 수동 순회로 바꿔 호출당 할당을 N+1개에서 2개로 낮춤
  • 생성된 C 코드는 기본 경고 수준에서 warning-free build를 유지하며, 하니스는 -Werror를 사용해 회귀를 즉시 드러내게 함

아키텍처

  • 저장소 구조는 다음 요소로 나뉨
    • spinel: POSIX shell 기반 원커맨드 래퍼 스크립트
    • spinel_parse.c: libprism에서 텍스트 AST로 가는 C 프런트엔드 1,061줄
    • spinel_codegen.rb: AST에서 C 코드로 가는 컴파일러 백엔드 21,109줄
    • lib/sp_runtime.h: 런타임 라이브러리 헤더 581줄
    • lib/sp_bigint.c: 임의 정밀도 정수 5,394줄
    • lib/regexp/: 내장 regexp 엔진 1,759줄
    • test/: 기능 테스트 74개
    • benchmark/: 벤치마크 55개
    • Makefile: 빌드 자동화
  • 런타임 lib/sp_runtime.h는 GC, array/hash/string 구현과 기타 런타임 지원을 하나의 헤더 파일에 담음
  • 생성된 C 코드는 이 헤더를 include하며, 링커는 libspinel_rt.a에서 필요한 부분만 가져옴
    • bigint
    • regexp engine
  • 파서는 두 구현을 가짐
    • spinel_parse.c는 libprism을 직접 링크해 CRuby 없이 동작함
    • spinel_parse.rb는 Prism gem을 사용하는 CRuby fallback
  • 두 파서는 동일한 AST 출력을 만들며, spinel 래퍼는 가능하면 C 바이너리를 우선 사용함
  • require_relative는 파싱 시점에 해결되어 참조된 파일이 인라인됨

제약 사항

  • No eval: eval, instance_eval, class_eval은 지원하지 않음
  • No metaprogramming: send, method_missing, 동적 define_method는 지원하지 않음
  • No threads: Thread, Mutex는 지원하지 않으며 Fiber만 지원함
  • No encoding: UTF-8과 ASCII를 가정함
  • No general lambda calculus: 깊게 중첩된 -> x { }[] 호출은 다루지 않음

의존성과 실행 모델

  • 빌드 시 의존성은 libprism C 라이브러리와 초기 부트스트랩용 CRuby임
  • 런타임 의존성은 없으며, 생성된 바이너리는 libc + libm만 필요함
  • 정규식은 내장 엔진을 써서 외부 라이브러리가 필요 없음
  • Bigint는 내장되어 있지만 실제 사용할 때만 링크됨
  • Prismspinel_parse가 사용하는 Ruby 파서임
    • make deps는 rubygems.org의 prism gem tarball을 내려받아 C 소스를 vendor/prism에 풂
    • 이미 prism gem이 설치되어 있으면 자동 감지함
    • PRISM_DIR=/path/to/prism으로 사용자 경로를 지정할 수도 있음
  • CRuby는 초기 bootstrap에만 필요하며, make 이후에는 전체 파이프라인이 Ruby 없이 실행됨

프로젝트 이력

  • Spinel은 처음에 C로 구현되었고, 규모는 18K lines였으며 c-version 브랜치에 남아 있음
  • 이후 Ruby로 다시 작성한 ruby-v1 브랜치를 거쳤음
  • 현재 master는 self-hosting 가능한 Ruby 부분집합으로 다시 작성된 버전임

라이선스

  • MIT License를 사용함
  • LICENSE 파일을 따름
Hacker News 의견들
  • Matz가 만든 거라면 Ruby semantics의 한계도 잘 알고 있을 테니 신뢰가 감
    제 석사 논문도 AOT JS compiler였는데, 돌아가긴 했지만 입력 데이터 제약이 커서 결국 접었음
    당시 JS 개발자들은 스스로 제약을 잘 지키는 쪽에 익숙하지 않았고, JSON.parse처럼 본질적으로 알 수 없는 입력이 걸림돌이었음
    지금은 TypeScript 덕분에 그때보다 훨씬 현실적일 수도 있음
    일반적인 lambda calculus만 봐도 타입 추론의 한계는 분명하고, Matt Might 쪽 논문이나 Shed-skin Python 작업에서도 비슷한 제약이 드러남
    eval, send, method_missing, define_method가 실제 Ruby 코드에서 얼마나 흔한지 궁금하고, 타입 없는 파싱, 예를 들어 JSON 입력은 보통 어떻게 처리하는지도 궁금함

    • 이 설계는 꽤 pragmatic해 보임
      Ruby 파싱은 번역 자체보다 더 어려울 정도라서 Prism을 쓰고, 결과는 C를 생성함
      기본적인 Ruby semantics 자체는 구현이 그렇게까지 어렵지 않음
      반대로 나는 순수 Ruby로 만든 오래된 self-hosting AOT compiler를 붙들고 있는데, 자체 파서를 쓰겠다고 고집해서 일부러 훨씬 힘든 길을 탔음
      초반 80%는 대충 만들어도 Ruby 코드 상당수가 돌아간다는 걸 일찍 배웠고, 진짜 어려운 "두 번째 80%"는 Matz가 이번 프로젝트와 mruby에서 뺀 것들, 예를 들면 인코딩이나 온갖 주변 기능들에 몰려 있음
      솔직히 Ruby에는 실제 코드에서 한 번도 못 본 기능도 꽤 있어서, 몇몇은 deprecated돼도 이상하지 않다고 봄
      send, method_missing, define_method는 아주 흔함
      제약은 mruby와 비슷하고, 그런 제약 아래에서도 쓸 곳은 있음
      send, method_missing, define_method 지원은 비교적 쉬운 편임
      반면 eval() 지원은 엄청 고통스러움
      다만 Ruby에서 eval()의 큰 비율은 정적으로 instance_eval의 block 버전으로 환원 가능해서, 그런 경우엔 AOT 컴파일이 꽤 쉬워짐
      예를 들어 eval()에 들어가는 문자열을 정적으로 알 수 있거나 분해할 수 있으면 해결 여지가 큼
      실제로 많은 eval() 사용은 불필요하거나 단순한 introspection 우회에 가까워서, 정적 검사로 처리 가능함
      내 컴파일러도 그게 병목이 되면 거기부터 손댈 생각임
    • 이런 기능이 꽤 많아야 Rails식 magic을 만들 수 있음
      타입 없는 JSON ingestion도 아마 그런 메커니즘을 쓸 가능성이 큼
      그걸 걷어내면 Crystal만큼 강한 타입은 아니지만 공식 Ruby만큼 메타프로그래밍에 기대지도 않는, 작고 읽기 쉬운 언어가 남음
      그래서 잠재력은 꽤 커 보이지만, 결국 시간 지나야 판단 가능함
    • Ruby를 Objective-C로 컴파일하는 방식이라면 Ruby 기능을 전부 지원하면서도 인터프리터 Ruby보다 더 빠를 수 있을 것 같음
    • 나는 eval을 자주 쓰는 쪽임
      안 쓸 수도 있겠지만, 내겐 그게 더 ergonomic함
    • 내 경험상 흥미로운 건 eval, exec, define_method, 그리고 Class.new, Struct.new로 새 클래스를 만드는 패턴임
      이들 사용 대부분은 앱 boot 시점이나 파일을 require하는 동안 몰려 있고, 어떤 면에선 이미 컴파일 단계와 비슷함
  • 이건 RubyKaigi 2026에서 Matz가 방금 발표한 것임
    실험적이긴 하지만 Claude 도움을 받아 약 한 달 만에 만들었고, 라이브 데모도 성공했음
    이름은 Matz의 새 고양이에서 따왔고, 그 고양이 이름은 Card Captor Sakura의 고양이 이름에서 왔으며, 거기서 또 Ruby라는 이름의 캐릭터와 짝을 이룸

    • 사람들이 AI가 프로그램을 처음부터 끝까지 다 만든다는 얘기를 많이 하지만, 더 현실적인 시나리오는 10x programmer100x programmer로 만들어 주는 쪽이라고 봄
      Matz 같은 사람에겐 100x를 500x로 밀어 올리는 셈일 수도 있음
    • 내 머릿속 최신 Spinel은 Steven Universe 쪽이라서 Spinel/Ruby (Moon) pun은 전혀 못 알아봤는데, 알고 나니 하루가 즐거워졌음
    • 나는 당연히 광물 spinel 얘기인 줄 알았음 :)
      https://en.wikipedia.org/wiki/Spinel
    • 고맙다
      영상은 아직 라이브가 아닌 것 같고, 여기 채널에 하나씩 올라오는 듯함
      https://www.youtube.com/@rubykaigi4884/videos
    • 그 고양이 이름 유래 얘기는 Ruby Central drama와 Spinel.coop 창립자들과의 관계를 생각하면 꽤 수상해 보임
      프로젝트 이름도 감정적으로 지은 것처럼 느껴짐
  • 분명 엄청 인상적이지만, AI agent 없이는 유지보수 불가능해 보임
    spinel_codegen.rb가 2만 1천 줄이고, 어떤 메서드는 중첩이 15단계까지 감
    원래 컴파일러 코드는 예쁘기 어렵지만, 이건 그 기준으로도 사람이 관리하기 매우 힘들어 보임

    • 컴파일러 코드는 시간만 있으면 충분히 예쁘게 만들 수 있음
      컴파일러는 서브시스템 경계가 뚜렷하고 단계별 handoff도 명확해서, 오히려 가장 modular하게 만들기 쉬운 축에 듦
      문제는 대개 일단 돌아가게 만든 뒤 리팩터링할 시간이 없다는 데 있고, 그러면 지저분함이 계속 불어남
    • spinel_codegen.rb는 거의 eldritch horror 수준임
      Claude를 쓰면 나도 늘 이런 스파게티 코드가 나오는데, 내가 뭘 잘못하고 있나 싶었음
      그런데 최고 수준 프로그래머라고 생각하는 사람이 만든 진짜 흥미로운 프로젝트에서도 코드 질이 군데군데 꽤 나쁜 걸 보니, 나만 그런 건 아니었음
      예를 들어 infer_comparison_type()은 최악의 사례까진 아니고 읽기 어렵지도 않지만, 훨씬 더 단순하고 명확한 구현이 있는데도 Claude가 거기로 못 감
      Set으로 비교 연산자를 모아두고 include?로 처리하면 더 짧고 빠르고 읽기 쉽고 유지보수도 쉬움
      그런데 Claude는 늘 if-return 연쇄로 흘러가고, 심지어 if-else조차 낯설어하는 느낌임
      내 Claude 코드베이스도 그런 패턴으로 가득한데, 이제 나만 그런 게 아니라는 걸 알겠음
      반면 다른 파일들은 훨씬 낫고, 특히 lib 디렉터리는 메인 Ruby repo의 ext 디렉터리와 대응되는 듯하며 품질이 괜찮음
      API도 MRI Ruby 영향을 분명히 받았고, 구현은 꽤 다르더라도 Matz가 원본 API 일부를 닮게 유도해서 출력이 더 정돈된 것 같음
      [1] https://github.com/matz/spinel/blob/98d1179670e4d6486bbd1547...
    • 지금 단계에선 사람이 손으로 유지보수 가능한지가 그렇게 중요하진 않다고 봄
      테스트와 벤치마크만 통과하면 일단 만족함
      다만 거대한 파일이 AI에게도 다루기 쉬운지는 의문임
      나는 파일을 300줄 안쪽으로 제한하려고 하고, 사람이 이해하기 쉬운 코드가 coding agents에게도 쉬울 거라고 생각함
  • 제약은 이렇다고 함
    No eval: eval, instance_eval, class_eval
    No metaprogramming: send, method_missing, define_method(동적)
    No threads: Thread, Mutex(Fiber는 지원)
    No encoding: UTF-8/ASCII 가정
    No general lambda calculus: [] 호출을 동반한 깊은 중첩 -> x { }
    UTF-8/ASCII 가정은 개인적으로 큰 제약이 아니지만, 나머지는 꽤 많은 프로그램에서 실제 제약이 될 듯함
    그리고 이걸 다시 넣으려면 상당한 작업이 필요해 보임

    • 이러면 Ruby의 magic 상당 부분이 사라짐
  • Ruby를 오래 써 왔고, 나열된 기능을 전부 활용해 본 입장에서 보면, 오히려 내가 진화 끝에 원하게 된 건 이런 버전의 단순한 Ruby
    더 단순하고 이해하기 쉬운데도 Ruby 특유의 미적 감각은 남아 있음
    이제는 LLM 덕분에 코드 생성 생산성이 워낙 높아서, 예전처럼 개발자 생산성을 위해 메타프로그래밍으로 boilerplate를 줄일 필요가 덜함
    개발자가 직접 코드를 쓰는 비중 자체가 줄고 있기 때문임

    • 그게 원하는 게 단지 Ruby aesthetic이라면 Crystal도 잘 맞을 수 있음
      문법은 비슷하고 정적 타입 시스템이 있어서, 더 효율적인 컴파일 코드로 이어짐
    • eval이 없는 건 차라리 낫다고 보지만, threads와 mutexes까지 없는 건 아쉬움
      define_method 부재는 용도를 생각하면 이해가 감
      하지만 sendmethod_missing은 기존 라이브러리에서 흔하고, 구현도 메모리 lookup table을 컴파일 시점에 구성하는 식으로 아주 어렵진 않을 것 같음
      그래서 의도적으로 뺀 건지, 아직 거기까지 못 간 건지 모르겠음
      내 바람은 후자지만, 최소한 당장은 호환성 때문에 실무 투입은 어려울 듯함
    • 메타프로그래밍의 장점은 원래 코드를 덜 쓰는 것이 아니었음
      읽어야 할 코드를 줄이는 것이었음
  • 이거 정말 멋지고, 나는 오랫동안 Ruby용 AOT compiler를 기다려 왔음
    다만 eval이나 메타프로그래밍 fallback이 없는 건 아쉽고, 그래도 작은 고성능 subset에 집중하려고 그렇게 한 것 같음
    이 AOT 컴파일러로 만든 gem이 MRI와 잘 상호작용하면 좋겠음
    표준 Ruby와 gem을 패키징하거나 번들링하는 쪽은 여전히 tebako, kompo, ocran이 필요하고, 예전엔 ruby-packer, traveling ruby, jruby warbler 같은 프로젝트들도 있었음
    선택지가 하나 더 생긴 건 좋지만, 더 나은 개발자 UX를 갖춘 결정판이 나오길 바라고 있음

    • 맞음, 나도 최근에 warbler를 포크해야 했음
      너무 오랫동안 업데이트가 없었기 때문임
  • no threads인지 궁금함
    Ruby scheduler와 밑단의 pthread 구현은 C 영역에서도 잘 동작할 것 같은데, 혹시 zero dependency를 노린 건가 싶음
    optional extension을 나중에 넣을 계획이거나 아직 뺀 상태가 아니라면, 이 선택은 좀 이상하게 느껴짐

    • 이걸 의도적으로 지원 안 하기로 결정했다는 근거는 아직 못 봤음
      아마 그냥 아직 거기까지 못 간 것 아닐까 싶음
      멀티스레딩은 원래 제대로 만들기 아주 어려움
  • 한 달 조금 넘는 기간에 만들었다니 놀라움
    AI에 대해 뭐라 하든, 실력 있는 개발자 손에 들어가면 엄청난 속도 향상을 만들어 냄

    • 업계 전체는 agent harness, SOUL.md, 권한 설정, skills, MCPs, hooks, env 다 깔고 시작하는데
      Matz는 그냥 gem env|infofind면 충분한 느낌임
  • 이게 Matz가 만든 만큼, 앞으로 Ruby core의 일부가 될 가능성이 얼마나 현실적인지 궁금함
    그리고 그렇게 되면 Crystal에 얼마나 위협적일지도 궁금함

    • Crystal은 명시적인 static type system이 있고, 언어 차원에서 AOT 컴파일에 최적화돼 있음
      이런 특성은 큰 프로그램을 컴파일하고 유지하는 데 사실상 필수에 가까움
      반면 이건 제한된 Ruby subset용이라서, 인기 있는 Ruby gem 대부분은 그대로는 돌아가지 않을 것임
      C 컴파일을 지향하는 언어 subset이라는 점에서 PreScheme에 더 가까워 보임
      지금 단계에선 둘이 같은 영역에서 직접 경쟁한다고 보지 않음
      완전한 Ruby는 거의 확실히 JIT가 필요함
      [1]: https://prescheme.org/
    • 다른 관점으로 보면, 결국은 LLM이 우리가 원하는 어떤 언어로든 formal specification을 쏟아내는 지점에 도달할 것 같음
      Rational Unified Process, Enterprise Architect 같은 도구들의 복수전이 벌어지는 셈임
      차이가 있다면 UML 다이어그램 대신 markdown 파일이 온다는 정도임
  • 이건 infrastructure tools 쪽에서 유용할 것 같음
    예를 들어 Ruby로 쓰였지만 정적으로 컴파일되는 bundler가 있어서, RVM 같은 Ruby 설치 도구 역할까지 같이 해 준다고 상상해 볼 수 있음
    기존 Ruby buildpack은 Ruby로 작성돼 있지만 부트스트랩을 bash로 해야 해서 짜증나고 edge case도 생김
    CNB는 그 문제를 피하려고 Rust로 쓰였고, 의존성 없는 단일 바이너리를 배포할 수 있다는 발상은 정말 강력함