コンテンツにスキップ

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まで読んだ後に引く参照用です。

「まず推論し、その結果でチェックする」。推論が土台で、checkannotateはその出力を使います。この順序を、最初に頭に入れてください。


もう一つの約束ごとがあります。chibirigorは、Rubyが構文エラーにしないコードなら、すべて受理します。「型が付かないから解析しない」とは言いません。

  • パーサにはRuby標準のPrismを使います。Prismは、多少壊れた構文すら部分的に解析するので、受け付ける範囲はむしろRuby実行系より広いくらいです
  • ただし、「chibirigorが通した=実行して動く」ことは保証しません。私たちが返すのは、あくまで推論した型と診断だけです

これは『しくみ』と決定的に違う姿勢です。同書のチェッカーは型エラーで例外を投げて止まります。chibirigorは、入力を常に受け取り、出力は診断と推論型です。止まらず、分からない所は黙って先に進みます。


これから作る型チェッカーの心臓は、たった2つの関数です。全編、この2つを育てていきます。

  • type_of:式から型を求める(合成する)。1 + 2Integer"a""a"。分からなければuntypedを返すだけで、失敗しません
  • accepts:ある型が期待される所に、別の型を渡して合うか確かめる

全体の流れはこうです。type_ofが主役で、check/annotateはその出力を使うだけです。

┌─ 期待型がある所で accepts → 合わなければ ─→ check(診断)
ソース ─Prism→ AST ─type_of→ 型 ┤
└─ そのまま見せる ───────────────────────→ annotate(推論型)

図0-1 chibirigorのデータの流れ

▼ 図0-1 chibirigorのデータの流れ

type_ofが型を組み立て、checkは「期待型と合うか」を確かめて診断を出し、annotateは組み立てた型をそのまま見せます。推論が土台で、checkannotateもその出力を使うのです。

そして、全編を貫く中核の原理を、いま言葉にしておきます。専門用語は要りません。

Rigorのスローガンは「動くコードを脅かさない」です。

chibirigorも、健全さ(バグを一つ残らず捕まえること)より、誤検知を出さないこと(動くコードを赤くしないこと)をずっと大切にします。なぜそちらを選ぶのかは、各章でRubyの現実にぶつかりながら腑に落ちていきます。


各章は、小さな三つの視点(パースペクティブ)から書かれています:

  • ① 型理論:その章で出会う概念を1つ(『しくみ』のどこに対応するか)。
  • ② Ruby/RBSだと:それがRubyだとどう見えるか、あるいはどう見えないか
  • ③ Rigor実装の問題:素直な実装がRubyの現実でなぜ破綻し、どう折り合ったか。

「Rigorを理解する」とは、③の困りごとが②(Rubyの現実)から必然的に生まれ、それが①の概念でやさしく腑に落ちることです。難しい話(双方向型付けの形式化、変性、再帰型、本物の型推論…)は、すべて後編『The Seasoned chibirigor』に送ります。前編は、まず動く最小版を、気持ちよく作り切ることに集中します。

これから9章で積むもの(全体像)

Section titled “これから9章で積むもの(全体像)”

各章は前の章の上に積み上がります。1章で増えるのは「難しいこと1つ」だけ:

足すものキーワード
1型をデータで表し、式から型を求めるConsttype_ofcheckannotate
2メソッド送信を型付けするディスパッチ表、未知はDynamic
3変数と文を扱う不変Scope、文を縫う
4型が枝分かれする(Union)Union
5場合分けで型を絞るナローイング、2つの掟
6ハッシュと配列に型をつけるHashShapeTuple
7「合うか」を三値で判定するaccepts:yes/:no/:maybe
8RBSから型を引き、戻り型を推論して見せるRbs.loaddefのsig合成
9gradualの哲学で締めるuntypedの伝播、baseline

Part 9まで来ると、checkannotateが一通り動く型チェッカーが手元に残ります。


Ruby 3.4以降(Prism同梱)があれば動きます。テストフレームワークも使いません。

Terminal window
$ 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.