Part 0 はじめに:推論を土台にした型チェッカー
本編の入口です。コードを書く前に、chibirigorが何であり何でないかを掴みます。実装の前提(Prism、
check/annotate)と、全編を貫く2つの約束ごとを先に共有します。
chibirigorは、型を自分で推論する、最小限のRuby型チェッカーです。本物のRigor(Ruby向けの漸進的型チェッカー)のアーキテクチャの「最小版」を、ステップバイステップで自分の手で作っていきます。chibivueがVueを小さく作り直して学ぶように、私たちはRigorを小さく作り直して学びます。本書もchibivueがbook/implsに各回の到達点を残すのと同じ作りで、章末からimpls/dist/partNの段階スナップショット(その章まで作った、動く最小版)に飛べます。
本編で中心に作るのは、2つのコマンドだけ:
check:Rubyのコードを読み、型の矛盾(診断)を報告するannotate:推論した型を表示する
このあと付録では、checkに--explain/--unreachableのopt-inフラグを足す小さな発展も扱いますが、本編の軸はずっとこの2つです。
0.1推論を土台にした型チェッカー
Section titled “0.1推論を土台にした型チェッカー”最初に、chibirigorが何をする道具なのかを掴んでおきます。
世の中の型チェッカー(type checker)の多くは、プログラムに書かれた型注釈を前提に、それらが矛盾しないかを判定します。注釈が無いと仕事になりません。
- 型注釈=「この変数は
Integer」のように型をコードに書き添えること。Rubyでは普段書きません。だから初めて自分で書くのはずっと先のPart 8(RBS)で、それまでは登場しません
ところがRubyのコードにはふつう型注釈がありません。そこでchibirigorは、注釈に頼る前に、まず式から型を自分で導出します。1を見れば整数、"a".upcaseを見れば文字列、というふうに。そうして得た型を使って、矛盾を見つけたり(check)、型を見せたり(annotate)します。
一言で言えば、chibirigorは「推論を土台にした、注釈のいらない型チェッカー」です。推論はチェックと別物の前段ではなく、チェックを成り立たせている土台です。
ここで言う「推論」とは、式から型を上向きに組み立てる(合成)ことです。「1は整数」「"a".upcaseは文字列」というふうに、書いてあるものから型を積み上げます。逆に、メソッドの引数の型を呼び出し元の使われ方から逆算することは、前編ではしません。引数は分からなければuntypedに倒します。不明はとりあえずuntypedに逃がす、と覚えておけば十分です。untypedをはじめ特別な型のカタログは付録a1にあり、通読の必要はなく、Part 9まで読んだ後に引く参照用です。
「まず推論し、その結果でチェックする」。推論が土台で、checkとannotateはその出力を使います。この順序を、最初に頭に入れてください。
0.2入力を拒まないという約束
Section titled “0.2入力を拒まないという約束”もう一つの約束ごとがあります。chibirigorは、Rubyが構文エラーにしないコードなら、すべて受理します。「型が付かないから解析しない」とは言いません。
- パーサにはRuby標準のPrismを使います。Prismは、多少壊れた構文すら部分的に解析するので、受け付ける範囲はむしろRuby実行系より広いくらいです
- ただし、「chibirigorが通した=実行して動く」ことは保証しません。私たちが返すのは、あくまで推論した型と診断だけです
これは『しくみ』と決定的に違う姿勢です。同書のチェッカーは型エラーで例外を投げて止まります。chibirigorは、入力を常に受け取り、出力は診断と推論型です。止まらず、分からない所は黙って先に進みます。
0.3心臓となる2つの関数
Section titled “0.3心臓となる2つの関数”これから作る型チェッカーの心臓は、たった2つの関数です。全編、この2つを育てていきます。
type_of:式から型を求める(合成する)。1 + 2→Integer、"a"→"a"。分からなければuntypedを返すだけで、失敗しませんaccepts:ある型が期待される所に、別の型を渡して合うか確かめる
全体の流れはこうです。type_ofが主役で、check/annotateはその出力を使うだけです。
┌─ 期待型がある所で accepts → 合わなければ ─→ check(診断)ソース ─Prism→ AST ─type_of→ 型 ┤ └─ そのまま見せる ───────────────────────→ annotate(推論型)▼ 図0-1 chibirigorのデータの流れ
type_ofが型を組み立て、checkは「期待型と合うか」を確かめて診断を出し、annotateは組み立てた型をそのまま見せます。推論が土台で、checkもannotateもその出力を使うのです。
そして、全編を貫く中核の原理を、いま言葉にしておきます。専門用語は要りません。
Rigorのスローガンは「動くコードを脅かさない」です。
chibirigorも、健全さ(バグを一つ残らず捕まえること)より、誤検知を出さないこと(動くコードを赤くしないこと)をずっと大切にします。なぜそちらを選ぶのかは、各章でRubyの現実にぶつかりながら腑に落ちていきます。
0.4各章を貫く三つの視点
Section titled “0.4各章を貫く三つの視点”各章は、小さな三つの視点(パースペクティブ)から書かれています:
- ① 型理論:その章で出会う概念を1つ(『しくみ』のどこに対応するか)。
- ② Ruby/RBSだと:それがRubyだとどう見えるか、あるいはどう見えないか。
- ③ Rigor実装の問題:素直な実装がRubyの現実でなぜ破綻し、どう折り合ったか。
「Rigorを理解する」とは、③の困りごとが②(Rubyの現実)から必然的に生まれ、それが①の概念でやさしく腑に落ちることです。難しい話(双方向型付けの形式化、変性、再帰型、本物の型推論…)は、すべて後編『The Seasoned chibirigor』に送ります。前編は、まず動く最小版を、気持ちよく作り切ることに集中します。
これから9章で積むもの(全体像)
Section titled “これから9章で積むもの(全体像)”各章は前の章の上に積み上がります。1章で増えるのは「難しいこと1つ」だけ:
| 章 | 足すもの | キーワード |
|---|---|---|
| 1 | 型をデータで表し、式から型を求める | Const、type_of、check、annotate |
| 2 | メソッド送信を型付けする | ディスパッチ表、未知はDynamic |
| 3 | 変数と文を扱う | 不変Scope、文を縫う |
| 4 | 型が枝分かれする(Union) | Union |
| 5 | 場合分けで型を絞る | ナローイング、2つの掟 |
| 6 | ハッシュと配列に型をつける | HashShape、Tuple |
| 7 | 「合うか」を三値で判定する | accepts、:yes/:no/:maybe |
| 8 | RBSから型を引き、戻り型を推論して見せる | Rbs.load、defのsig合成 |
| 9 | gradualの哲学で締める | untypedの伝播、baseline |
Part 9まで来ると、checkとannotateが一通り動く型チェッカーが手元に残ります。
Ruby 3.4以降(Prism同梱)があれば動きます。テストフレームワークも使いません。
$ ruby exe/chibirigor check path/to/file.rb # 型診断$ ruby exe/chibirigor annotate path/to/file.rb # 推論型の表示parseして構文木(AST)を覗くところから始めましょう。Prismがプログラムをどんな木にするか。それが分かれば、type_ofが木をたどって型を組み立てる準備は整います。
次章(Part 1):最初のtype_ofを書きます。リテラルと算術だけの、ほんの数十行の型チェッカーが、もう「型を付ける」仕事を始めます。
© 2026 TypedDuck. Licensed under CC BY-SA 4.0.