Upgrade to Pro — share decks privately, control downloads, hide ads and more …

動的型解析器 Ethotrace

動的型解析器 Ethotrace

Avatar for すぎうり

すぎうり

June 23, 2026

More Decks by すぎうり

Other Decks in Programming

Transcript

  1. 自己紹介 すぎうり • Twitter:@uproad3 • Ruby歴20年 • VRChat歴7年 • 仕事:Rails |

    AWS | LT芸人 • 趣味:アーキテクト | リファクタリング | 電子工作 • 言語:Ruby | C# | C | JS | ほかいろいろ • 技術:Terraform | Unity | Ubuntu | MySQL | RaspberryPi • 悲しきフルスタックエンジニア • 最近はClaudeをシバきまわしている • 特に型に強い思い入れがあるわけではない
  2. 第1章 型注釈 強制されてませんか ? TypeScript / Java / C# /

    Rust … 型を書かないとコンパイルが通らない Rubyには、それがない。 ——自由だ。
  3. 第1章 Rubyには型注釈がない=自由 型注釈を書かなくていい。書いてすぐ動く。 ダックタイピング ——「each できれば何でもいい」 # 型注釈なしでも動く def process(items)

    items.each { |x| puts x } end process([1, 2, 3]) # OK process("hello") # OK (each を持てば何でも) この自由さがRubyの良さ——でも、代償がある
  4. 第1章 でも、その自由の代償に —— def process(x) String Array Enumable IO 結局

    何を渡せばいいのか? 何が返ってくるのか? 型注釈もドキュメントもない。 コードを上から下まで追うしかない。 String Array
  5. 第1章 見えないものは、 3つある def process(x) ① 引数 何を渡せばいい? ② 投げる例外

    何がraiseしうる? ③ 外部依存 裏で何に触ってる? インターフェースが見えないから ——使い方がわからない ④ 戻り値 何が返ってくる?
  6. 第1章 "書けば"解決する。でも —— 型注釈を書けば解決する # sig { params(x: T.untyped) #

    .returns(String) } def process(x) ... end RBS / Sorbet でも—— ・書くコストが高い ・既存コードへの後付けは重い ・メタプロと相性が悪い 書かずに、型情報を手に入れる方法はないか ? ——テストがすでに証拠を持っている
  7. 2章 型は"書く"のではなく "観測する" 「書く」 RBS / Sorbet 人がコードを読んで宣言する 書くコストがかかる 後付けは重い 書くコストなしには型情報が得られない

    「観測する」 ethotrace テストを走らせて記録する 書かなくていい テストがあれば証拠はある テストを走らせるだけで型情報が手に入る
  8. 第2章 型には、3つの側面がある Effect-TS の Effect<Success, Error, Requirements> という捉え方を借りた (詳しいのはそのへんにいるんで後で聞いてください) Success

    成功して何を返すか Error どんな失敗をしうるか Requirements 何を必要とするか 型は「引数」と「戻り値」だけじゃない ——テストで観測すればこの 3つが採れる
  9. 第3章 prepend で、メソッド呼び出しを捕まえる module ArgumentTracer def process(x) # 引数に呼ばれたメソッドを記録 called

    = detect_protocol(x) # x に何が呼ばれるか観測 $observations << { method: :process, args_protocol: called } super # 元のメソッドを呼ぶ end end MyClass.prepend(ArgumentTracer) 詳しくはリポジトリで: github.com/uproad/ethotrace
  10. 第3章 観測結果——引数プロトコル 整形後の観測結果(実際の出力を読みやすく表示) method: MyClass#process args: x → protocol: [:each,

    :map, :size] # xに呼ばれたメソッドの集合 # = "each、map、sizeを持っていれば何でも渡せる " 「型名」ではなく「呼ばれたメソッド集合」 ——これがダックタイピングの型の実体
  11. 第3章 このメソッドは例外を投げうるか prepend の rescue で、外に飛んだ例外を捕まえる module ExceptionTracer def process(x)

    super rescue => e # 外に飛んだ例外クラスを記録 $observations << { raised: e.class } raise # 再度投げる end end 「型注釈には載らない情報」 ——呼び出し元が何を rescueすべきか、観測が教えてくれる
  12. 第3章 観測結果——例外チャネル method: MyClass#process raised: - ArgumentError - Tax::RateError #

    このメソッドはこれらを投げ "うる" 型注釈には普通載らない情報。 でも、呼び出し元はこれを知りたい ——何を rescue すべきかが分かる
  13. 第3章 もうひとつ ——外部依存も見える method: MyClass#process requirements: - time.read # Time.now

    を呼んだ - env.read(KEY) # ENV['KEY'] を参照した # requirements が空 = 純粋なメソッド ENV・Time・IO に触れたか——「純粋かどうか」が分かる