「Rubyのしくみ Ruby Under a Microscope」を読んだ

RubyKaigi間近!ということで積読していたRubyのしくみ Ruby Under a Microscope を読んだので感想を書きます。

全部で12章ある本なんですが、自分の中での勝手な区切りで分けて感想を書きます。

1章, 2章 パースとコンパイル

  • 最近のRubyKaigiで熱いパーサーの話
  • 字句解析と構文解析という概念がある
    • 字句解析のステートマシンとか構文木の話を詳しく知りたい人はアンダースタンディング コンピュテーションを読もう!Rubyからコンピューターサイエンスが学べちまうんだ!(僕は積んでいます)
    • こういう日本語の解説が色々あるのがRubyの強みだよなぁ
    • 僕はDeterministic Finite Automatonって響きかっこいいなぁという気持ちしかありません
      • すごろくのマス目を進むイメージで理解している
  • コンパイル
    • ASTを更に命令列に変換する処理を説明してくれているのだが、あんまり理解できていない

3章, 4章 VMの動作

  • 内部スタックとコールスタック
    • 院で研究しているプログラムの操作的意味論のサンプルも環境スタックとコールスタックを2つ持つ実装だった。
    • 二重スタックが入り交じる解説を消化するのは難易度高い
  • キーワード引数の内部はハッシュとかforの中身がeachとか知るだけでも面白い話も載っている
  • ブロック内で変数をスタック遡って探す話、なぜclassとかdef通すとできないんだ?と思ったけど、第9章に答えが載っていた。

5章, 6章 オブジェクト、クラス、メソッド探索

  • Rubyのソースコードのあちこちで見かけるVALUEポインタについて書かれている
  • 6章のメソッド探索と定数探索の話は特に面白かった
    • クラスとモジュールの継承はわりと想像通りだったけど、prependの実装がおもしろかった
    • レシーバのクラスのコピーを作ってメソッド全部コピーしたクラスに棚上げするの頭良すぎる
    • Ruby利用者からするとprependって組み込みのクラスにパッチ当てたりしやすくなる神機能だけど、実装は結構トリッキーなんだなと思った。
  • レキシカルスコープ
    • Ruby3.2.2でもレキシカルスコープと継承チェーン内の探索の順序は変わっていなさそう
> ~/qwyng.dev on master ◦ irb
irb(main):001* class SuperClass
irb(main):002*   FIND_ME = 1
irb(main):003> end
=> 1
irb(main):004* module ModuleA
irb(main):005*   FIND_ME = 2
irb(main):006*   class SubClass < SuperClass
irb(main):007*     p FIND_ME
irb(main):008*   end
irb(main):009> end
2
=> 2

⋊> ~/qwyng.dev on master  ruby -v
ruby 3.2.2 (2023-03-30 revision e51014f9c0) [arm64-darwin22]
  • この章は実際の開発でも役に立つ知識が多そうだと感じた
  • fursich氏のRubyの定数が怖いなんて言わせないという神ドキュメントがあるのですが、Rubyのしくみの5,6章読んでからこのドキュメントを読むとめっちゃ面白いですよ!!

7章 ハッシュテーブル

  • Object#hashってそんな大事なことに使われてたんだ…
  • eql?を独自で実装するときにObject#hashも同じロジックでオーバライドしてねみたいなドキュメントがあるけど、hashをなぜオーバライドするのかがよくわかった
    • 例えば、a = SomeObj.new; b = SomeObj.new; a.eql?(b) => trueと実装したけど、a.hash == b.hash => falseだと、ハッシュテーブル内でabは別のバケットに入ってしまう可能性がある。
    • hashメソッドは十分に散らばった値になるようにしないとすぐ同じバケットに値が集まってしまう
      • hashメソッドの定義は慎重に行う必要がありそう

8章 Lispから借用したアイデア

  • 院で先生に「クロージャってなんですか」と聞かれて答えられなかったのを思い出す
    • 環境と関数のペアです…安西先生
    • lambdaを呼び出すと環境をまるごとヒープに保存するたまげたな
    • procとlambdaの違いって引数の厳密性だと思っていたけど、内部でちゃんとフラグを持ってるんですね

9章 メタプログラミング

  • 特異クラスとメタクラスの違いが書いてあって、メタプログラミングRubyより一歩踏み込んだ解説がなされていた
  • Rubyのclass << は引数のオブジェクトの特異クラスを作成するという構文っぽい
    • class << self はselfの特異クラスを作成しているという解釈ができる
  • class_evalとかinstance_evalはブロック構造体のselfの変更していたんですね
  • メタプログラミングRubyでスコープゲートと呼ばれていたものはレキシカルスコープのことだったのか?
    • ブロック内ではレキシカルスコープのポインタ先が違うのが原因っぽい
    • defやclassはレキシカルスコープをそのままcref構造体みているが、ブロックだとスタック上のスコープを直接参照している。
      • ここでやっと3章で出てきたスタックを遡って変数にアクセスする話が理解できた
      • つまりRubyのブロックはダイナミックスコープなんだよ!!!!Lispと一緒でレキシカルスコープもダイナミックスコープもある!!!!
      • この本、コラムっぽいページに大事な内容が書かれているので、読み飛ばさないように注意が必要

10章、11章

  • 飛ばしました….

12章 GC

  • GCのアルゴリズムが色々あることがわかったぜ!!!

全体通しての感想

最初の命令列とかスタックの動作はかなり理解が難しかった(今もそんなにわかってない)が、中盤のクラスとかメソッド探索の話は普段の業務でも役にたつことが多そうだと思った。
正直、このしょうもない感想記事に書いてあることの10000倍くらい濃い情報が本には書かれているので、興味がある人はぜひ読んでみてください。(もうRubyも3.3なので、この本の内容が古くなっている可能性もあるけど)