Slide 1

Slide 1 text

No Types Needed, Just Callable Method Check Daichi Kamiyama @_dak2_ 2026-04-23 RubyKaigi 2026

Slide 2

Slide 2 text

About Me

Slide 3

Slide 3 text

˒ Daichi Kamiyama ˒ https://github.com/dak2 ˒ https://x.com/_dak2_ ˒ Software Engineer at Timee,Inc. @dak2

Slide 4

Slide 4 text

Timee,Inc.

Slide 5

Slide 5 text

No content

Slide 6

Slide 6 text

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.”

Slide 7

Slide 7 text

Outline

Slide 8

Slide 8 text

˒ The benefits of types ˒ How AI changed development experience ˒ Introducing my solution - `Method-Ray` - ˒ The difference from existing type checkers ˒ Future Outlook

Slide 9

Slide 9 text

Part 1 The benefits of types

Slide 10

Slide 10 text

Writing types

Slide 11

Slide 11 text

Writes RBS::Inline in my company

Slide 12

Slide 12 text

RBS::Inline & Steep

Slide 13

Slide 13 text

No content

Slide 14

Slide 14 text

No content

Slide 15

Slide 15 text

Writing types on a daily basis

Slide 16

Slide 16 text

Type ecosystem

Slide 17

Slide 17 text

˒ RBS ˒ RBS::Inline ˒ Sorbet ˒ Tapioca Ruby’s type ecosystem is thriving ˒ Steep ˒ TypeProf ˒ etc…

Slide 18

Slide 18 text

Why write types? Including annotations

Slide 19

Slide 19 text

What are the benefits of types?

Slide 20

Slide 20 text

Documentation

Slide 21

Slide 21 text

No content

Slide 22

Slide 22 text

No content

Slide 23

Slide 23 text

˒ Url is String ˒ Payload is Hash has title and body ˒ Return is Response

Slide 24

Slide 24 text

Types are living docs

Slide 25

Slide 25 text

Focus on I/F

Slide 26

Slide 26 text

No content

Slide 27

Slide 27 text

Think interface first

Slide 28

Slide 28 text

Contract

Slide 29

Slide 29 text

No content

Slide 30

Slide 30 text

Explicit boundaries between modules

Slide 31

Slide 31 text

Type Error Prevention

Slide 32

Slide 32 text

No content

Slide 33

Slide 33 text

Catch type errors before execution

Slide 34

Slide 34 text

Types are constraints and guardrails

Slide 35

Slide 35 text

Part 2 How AI changed development experience

Slide 36

Slide 36 text

Generative AI has changed development experience

Slide 37

Slide 37 text

Generative AI got dramatically better at writing programs

Slide 38

Slide 38 text

Of course, it's not perfect, since it’s non-deterministic

Slide 39

Slide 39 text

AI is getting better at writing Ruby code

Slide 40

Slide 40 text

Deploy-ready code has increased with minimal reviews

Slide 41

Slide 41 text

Hold on, I realized,,,

Slide 42

Slide 42 text

AI quality keeps climbing

Slide 43

Slide 43 text

Type ops cost is real

Slide 44

Slide 44 text

Last-mile typing is difficult

Slide 45

Slide 45 text

Are types needed in the AI era?

Slide 46

Slide 46 text

They can write deploy ready code even without types

Slide 47

Slide 47 text

No content

Slide 48

Slide 48 text

Ruby, Python, and JavaScript were the fastest, cheapest, and most stable

Slide 49

Slide 49 text

https://arxiv.org/pdf/2412.20545

Slide 50

Slide 50 text

LLM generation success rate inverts with task scale

Slide 51

Slide 51 text

Type safety improves, but code quality degrades

Slide 52

Slide 52 text

🤔

Slide 53

Slide 53 text

Types are constraints and guardrails for humans

Slide 54

Slide 54 text

But, some types are needed

Slide 55

Slide 55 text

Types as a contract

Slide 56

Slide 56 text

Other types are not needed

Slide 57

Slide 57 text

However, they do something non- deterministic

Slide 58

Slide 58 text

So, guardrails are needed for AI

Slide 59

Slide 59 text

The main benefit I gain from writing types is preventing `NoMethodError`

Slide 60

Slide 60 text

🧐

Slide 61

Slide 61 text

What if I could prevent `NoMethodError` — without writing any types?

Slide 62

Slide 62 text

Part 3 Introducing my solution `Method-Ray`

Slide 63

Slide 63 text

`Method-Ray` is a gem

Slide 64

Slide 64 text

No content

Slide 65

Slide 65 text

Just check callable methods

Slide 66

Slide 66 text

❌ No inferred type display ❌ No type error reports ❌ No nil-safety warnings ❌ No generic variance checks ❌ No LSP / editor integration Nothing else.

Slide 67

Slide 67 text

So, this just prevents `NoMethodError`

Slide 68

Slide 68 text

“Do one thing and do well” — Unix Philosophy

Slide 69

Slide 69 text

I/F is Ruby, But core logic is Rust

Slide 70

Slide 70 text

Interface: 1 command

Slide 71

Slide 71 text

Let’s see it in action

Slide 72

Slide 72 text

No content

Slide 73

Slide 73 text

Architecture

Slide 74

Slide 74 text

Ruby Code ruby-prism AST Installer Scan Graph Output

Slide 75

Slide 75 text

Parse Ruby code using ruby-prism crate

Slide 76

Slide 76 text

Ruby Code ruby-prism AST Installer Scan Graph Output

Slide 77

Slide 77 text

Using arena allocator using bumpalo crate Parsed Ruby code

Slide 78

Slide 78 text

Copy source bytes into the arena Parse Ruby code using ruby-prism crate API

Slide 79

Slide 79 text

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.

Slide 80

Slide 80 text

Walks AST nodes and builds graph Ruby Code ruby-prism AST Installer Scan Graph Output

Slide 81

Slide 81 text

ASTInstaller has global and local env Global env has RBS definition Maps AST nodes to graph operations

Slide 82

Slide 82 text

Handles by node type recursively

Slide 83

Slide 83 text

Call install_class

Slide 84

Slide 84 text

Enter Class scope, register constants, methods, etc..

Slide 85

Slide 85 text

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}"

Slide 86

Slide 86 text

Register user-defined methods

Slide 87

Slide 87 text

Register methods from RBS definition

Slide 88

Slide 88 text

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

Slide 89

Slide 89 text

Tracks how types flow between expressions via edges Ruby Code ruby-prism AST Installer Type Graph Output

Slide 90

Slide 90 text

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.

Slide 91

Slide 91 text

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.

Slide 92

Slide 92 text

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.

Slide 93

Slide 93 text

Detect methods in User class `floor` method is not found

Slide 94

Slide 94 text

Walks the method resolution order chain and returns the first match

Slide 95

Slide 95 text

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

Slide 96

Slide 96 text

Collects errors and formats diagnostics with source locations Ruby Code ruby-prism AST Installer Type Graph Output

Slide 97

Slide 97 text

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.

Slide 98

Slide 98 text

Parse => Graph => Scan => Output

Slide 99

Slide 99 text

Why Rust?

Slide 100

Slide 100 text

High performance

Slide 101

Slide 101 text

CLI performance is especially important in this era

Slide 102

Slide 102 text

No content

Slide 103

Slide 103 text

Oxc is a set of JavaScript tools created by voidZero

Slide 104

Slide 104 text

Oxlint is JavaScript linter of Oxc tools, 50~100x faster than ESLint

Slide 105

Slide 105 text

https://readoss.com/en/oxc-project/oxc/inside-oxlint-linter-architecture-and-rule-system

Slide 106

Slide 106 text

Optimization of Oxlint is so interesting

Slide 107

Slide 107 text

The existence of Prism's Rust bindings is another key factor

Slide 108

Slide 108 text

Seeking fast CLI

Slide 109

Slide 109 text

Part 4 The difference from existing type checkers

Slide 110

Slide 110 text

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

Slide 111

Slide 111 text

Part 5 Future Outlook

Slide 112

Slide 112 text

Currently, it's not production-ready at all yet

Slide 113

Slide 113 text

1. Multiple file analysis

Slide 114

Slide 114 text

2. Handle gem rbs

Slide 115

Slide 115 text

3. Handles pre-defined RBS files

Slide 116

Slide 116 text

Conclusion

Slide 117

Slide 117 text

Are types needed in the AI era? What do you think?

Slide 118

Slide 118 text

Thank You!