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

No Types Needed, Just Callable Method Check

Avatar for dak2 dak2
April 23, 2026

No Types Needed, Just Callable Method Check

RubyKaigi2026 Day2 Sub Arena

Avatar for dak2

dak2

April 23, 2026

More Decks by dak2

Other Decks in Technology

Transcript

  1. This talk in one line “In the age of AI,

    maybe we only need to check that a method is callable, at least in Ruby.”
  2. ˒ The benefits of types ˒ How AI changed development

    experience ˒ Introducing my solution - `Method-Ray` - ˒ The difference from existing type checkers ˒ Future Outlook
  3. ˒ RBS ˒ RBS::Inline ˒ Sorbet ˒ Tapioca Ruby’s type

    ecosystem is thriving ˒ Steep ˒ TypeProf ˒ etc…
  4. ˒ Url is String ˒ Payload is Hash has title

    and body ˒ Return is Response
  5. ❌ No inferred type display ❌ No type error reports

    ❌ No nil-safety warnings ❌ No generic variance checks ❌ No LSP / editor integration Nothing else.
  6. Why Arena is fast ? Heap alloc (slow) A B

    C D E F scattered u per-node alloc/free u syscall overhead Arena alloc (fast) A B C D E F bump pointer contiguous u bump pointer u drop entire arena once No per-node free. Throw the whole arena away in one operation.
  7. ASTInstaller has global and local env Global env has RBS

    definition Maps AST nodes to graph operations
  8. user.rb 1 class User 2 def initialize (name) 3 @name

    = name 4 end 5 6 def greet 7 "Hello, #{@name} !" 8 end 9 end Initialize greet name / @name init "Hello, #{@name}"
  9. MethodRegistryɾO(1) Lookup HashMap<(ReceiverType, MethodName), MethodInfo> ("String", "length") → MethodInfo {

    return: Integer } ("String", "upcase") → MethodInfo { return: String } ("Array", "first") → MethodInfo { return: T? } ("User", "greet") → MethodInfo { return: String } ("User", "initialize") → MethodInfo { params: [name: String] } Lookup = single hash, regardless of project size
  10. Tracks how types flow between expressions via edges Ruby Code

    ruby-prism AST Installer Type Graph Output
  11. Type Graph in Action Source x = 1 y =

    x y .foo Graph flow flow call 1: Int x y y.foo MethodCall Each expression = Vertex. Edges carry type info forward.
  12. MethodCallBoxɾReactive Constraint Frame 1 x = "hello" x .length box

    checks: String#length ✓ OK Frame 2 x = 42 ← reassign x .length x's type changes! type: Integer (box subscribed to type) Frame 3 // box re-runs x .length on Integer box checks: Integer#length ✗ NoMethodError No manual re-run. The box subscribes to the receiver — when type changes, it re-fires automatically.
  13. ChangeSet · Atomic Edge Updates Without batching edge_a.add(T1) // trigger

    reaction edge_b.add(T2) // trigger reaction edge_c.add(T3) // trigger reaction ⚠ inconsistent intermediate states With ChangeSet changes.queue(edge_a, T1) changes.queue(edge_b, T2) changes.queue(edge_c, T3) changes. apply() // all at once ✓ single consistent snapshot Boxes re-execute on the final state, not every intermediate.
  14. Method Resolution Order via RBS 1. User Class ← Nothing

    2. String ← Nothing 3. Module ← Nothing 4. SuperClass ← Nothing 5. Object ← Nothing 6. Kernel ← Nothing ✗ TypeError method not found
  15. Source Location, End to End Source app.rb:12:30 AST Node SourceLocation

    { line:12, col:30 } MethodCallBox carries location Diagnostic app.rb:12:30 // Output app.rb:12:30 error: undefined method `floor' for String puts User.new("Alice").greet.floor We never drop position info. Every node, every box, every diagnostic carries it.
  16. Steep Sorbet TypeProf Method-Ray Approach Validates code against RBS signatures

    Flow-sensitive type checking Abstract interpretation Graph-based type inference Annotations Required (.rbs) Required (inline sig) Not required Not required