付録 — mypy / Pyrightから来た場合
静的型付けのベースラインがPythonのmypyやPyrightであれば、この付録でRigorとの語彙をマッピングする。ふたつのエコシステムは見かけ以上に多くを共有している — 漸進的型付け、「ランタイムを壊さない」哲学、独立した型スタブファイル(.pyi/.rbs) — だが、アノテーションをどこに置くか、推論をどこまで積極的にするかで異なる選択をしている。
この付録の内容 5秒ピッチ · 型語彙マッピング · リファインメントキャリア · ナローイング · スタブ ↔ RBS · 深刻度とstrictモード · Pyright vs Rigor · 「アノテーション不要」 · ジェネリクス · Protocol ↔ RBSインターフェース · mypy/PyrightにあってRigorにないもの · Rigorにあってmypy/Pyrightにないもの · マイグレーションvignette
| 問い | mypy / Pyright | Rigor |
|---|---|---|
| アノテーションはどこに書くか? | ソース内(def f(x: int) -> str:) | .rbの隣の.rbsファイル |
| スタブ形式 | .pyiファイル | .rbsファイル |
| アノテートされていないコードのデフォルト | どこでもAny(mypy)/ 推論(Pyright) | 精密に推論、不明ならDynamic[Top] |
| strictモード | --strict(mypy)/ strict: true(Pyright) | severity_profile: strict |
| 抑制 | # type: ignore[error-code] | # rigor:disable <rule> |
| 型の同一性 | 名前的 + 構造的(Protocol) | 名前的 + 構造的ファセット |
| ナローイング(narrowing) | フローセンシティブ(flow-sensitive)な型、型ガード | フローセンシティブな型、述語メソッド + RBS::Extended |
Python/Rubyの類似点は構文より深い: 両言語とも動的に生まれ、どちらも後から漸進的型付けを取り入れ、どちらも型チェックを助言的なものとして扱い、どちらも型ヒントの公式構文(Pythonのtyping、RubyのRBS)を提供する。Rigorの設計優先事項の多くはmypyが正しくやったことを反映している。
型語彙マッピング
Section titled “型語彙マッピング”| Pythonの型付け | Rigorの表現 | 備考 |
|---|---|---|
int | Integer | |
float | Float | |
bool | bool(`Constant | Constant |
str | String | |
bytes | String(バイナリエンコーディングを持つ) | Rubyには独立したbytes型がない。 |
None | Constant<nil> | nilはRubyの唯一の「値なし」。 |
Any | Dynamic[Top] | 「ここは黙っていて」キャリア(carrier)。 |
object | Object(またはTop) | objectはPythonの普遍的なスーパータイプ(Noneを含むすべて)。Rigorの最も近い対応はTop。 |
Never / NoReturn | Bot | 空の型。 |
Optional[T] / `T | None` | T?(すなわち`T |
Union[A, B] / `A | B` | `A |
Literal[42] | Constant<42> | 直接対応。 |
Literal["foo", "bar"] | `Constant<“foo”> | Constant<“bar”>` |
Final[T] | (対応なし) | Rigorはまだイミュータビリティを追跡しない。 |
tuple[int, str] | Tuple[Integer, String] | 同じ位置ごとのモデル。 |
list[T] | Array[T] | |
dict[K, V] | Hash[K, V] | |
set[T] | Set[T] | |
TypedDict | HashShape{...} | required/optionalキーを持つクローズドシェイプ(shape)。 |
NotRequired[T](TypedDict) | HashShape内のオプショナルキー | Rigorのキーごとのrequired/optionalフラグでカバー。 |
Callable[[int], str] | ^(Integer) -> String(RBSのproc/block構文) | |
TypeVar('T') | RBSの[T]型パラメータ | |
Generic[T] | RBSのclass Foo[T] | |
Protocol(PEP 544) | RBSのinterface _Foo | 構造的型付け(structural typing)。 |
runtime_checkable Protocol | (対応なし) | Rigorは構造的プロトコルに対してisinstanceを実行しない。 |
Self(PEP 673) | RBSのself型 | |
ClassVar[T] | シングルトン側のattr_* / self.@var | |
Annotated[T, "tag"] | RBS::Extendedの%a{...}アノテーション | 両者とも型にメタデータを付与する。 |
リファインメントキャリアvs Pythonのアノテーション慣習
Section titled “リファインメントキャリアvs Pythonのアノテーション慣習”Pythonの型システムはリファインメント(refinement、篩型とも)形状の機能を一度に一つずつ追加してきた(Literal、LiteralString、TypeIs、Annotated)。Rigorはより広いカタログを標準で提供する。
| Rigorのリファインメント | Pythonで最も近いもの |
|---|---|
non-empty-string | (ビルトインなし。PEP 675のLiteralStringは精神的に近いが意味論が異なる) |
literal-string | LiteralString(PEP 675) — ソースコードのリテラルから構築されたことが証明可能。直接対応。 |
positive-int | (ビルトインなし。サードパーティのバリデーターでAnnotated[int, Gt(0)]という慣習) |
int<min, max> | (ビルトインなし。同じAnnotated[int, Range(...)]の慣習) |
numeric-string | (ビルトインなし) |
non-empty-array[T] | (ビルトインなし。一部のライブラリはtuple[T, *tuple[T, ...]]を使う) |
Constant<42> | Literal[42] |
LiteralStringが最も深い等価 — PythonのLiteralStringもRigorのliteral-stringも「この文字列はソースコードから来ており、ランタイム入力ではない」という事実を保持し、フォーマット/補間を通じて合成する。
ナローイング — 親しみやすい部分
Section titled “ナローイング — 親しみやすい部分”両チェッカーともフローセンシティブ。ナローイングのプリミティブには直接対応するものがある:
| Python | Rigor |
|---|---|
if x: | if x — 真側のエッジからFalse/Noneを除去 |
if x is None: | if x.nil? |
if x is not None: | unless x.nil? |
isinstance(x, int) | x.is_a?(Integer) |
if isinstance(x, (int, str)): | `if x.is_a?(Integer) |
assert isinstance(x, T) | プラグイン経由の# rigor:assert-typeスタイル、またはrigor-sorbet経由のT.cast |
match x: case ...(PEP 634) | case x; in ...(Rubyのパターンマッチング) |
ユーザー定義TypeGuard[T](PEP 647) | %a{rigor:v1:predicate-if-true: x is T}ディレクティブ |
ユーザー定義TypeIs[T](PEP 742) | 同じディレクティブ — Rigorのナローイングはデフォルトで対称(truthy側とfalsey側の両方) |
assert x is not None; x.upper() | 同じイディオム: unless x.nil?; x.upcase; end |
cast(int, x) | rigor-sorbet経由のT.cast(x, Integer)、またはRBS側のparam:ディレクティブ |
注目: PythonのTypeGuardは一方向(truthy側だけをナローイング)だが、TypeIs(PEP 742、採択済み)は双方向。Rigorのpredicate-if-trueとpredicate-if-falseディレクティブは独立していて合成できる — デフォルトでpredicate-if-true: x is Tを宣言するとfalsey側もx is ~Tにナローイングされ、TypeIsに等価。
スタブ ↔ RBS
Section titled “スタブ ↔ RBS”Pythonの.pyiファイルとRigorの.rbsファイルは同じ役割を果たす: インラインで型を提供しないライブラリの型を宣言する。
| Python | Rigor |
|---|---|
.pyiスタブ | .rbsファイル |
typeshed(コミュニティメンテナーのスタブ) | rbs_collection + Rigorの同梱stdlibカタログ |
mypy_path設定 | .rigor.ymlのsignature_paths: |
py.typedマーカー | (対応なし — Rigorはpaths:以下の任意のファイルを確認する) |
from __future__ import annotations | (対応なし — RBSはファイル分離の性質上常に遅延) |
型を明らかにする: reveal_type(x) | dump_type(x)(info診断)/ assert_type("...", x) |
reveal_typeとdump_typeは名前が異なる同じツール — 両者ともcall-siteで推論された型を診断として発行し、どちらも慣用的なテストハーネスではランタイムでno-opで、どちらも「チェッカーはここで何を見ているか?」を調べる正規のプローブ。
深刻度、抑制、「strictモード」
Section titled “深刻度、抑制、「strictモード」”| Python(mypy) | Rigor |
|---|---|
--strict | severity_profile: strict |
--strict-optional | Rigorでは常にオン(独立したフラグなし) |
--no-implicit-optional | Rigorでは常にオン |
--check-untyped-defs | Rigorでは常にオン |
--disallow-untyped-defs | (対応なし — Rigorはアノテーションを要求しない) |
--disallow-any-explicit | (対応なし) |
# type: ignore | # rigor:disable all |
# type: ignore[error-code] | # rigor:disable <rule> |
# mypy: ignore-errors(ファイルスコープ) | # rigor:disable-file all |
mypy.ini / pyproject.toml | .rigor.yml / .rigor.dist.yml |
概念的なギャップ: mypyの--disallow-untyped-defsはアノテーションがどこにでも存在すべきというベースライン前提を反映している。Rigorはアノテーションを要求しない — 推論が常に最初の答えであり、RBSはエスケープハッチ。これにより採用がスムーズになる: 「このモジュール全体をアノテートするまでmypyは役に立たない」という段階がない。
Pyright vs Rigor
Section titled “Pyright vs Rigor”Pyright(MicrosoftのTypeScript型チェッカー、Pylanceのエンジン)はmypyよりRigorに精神的に近い — 両者ともアノテーションの完全性より推論の深さと実用的なナローイングを優先する。
| Pyright | Rigor |
|---|---|
# pyright: ignore[reportError] | # rigor:disable <rule> |
pyright --stats | (直接対応なし — rigor check --explainは漸進的フォールバックの決定を示す) |
| ボディからの推論された戻り型 | 同様 — defボディが辿られ推論された戻りが伝播する |
| 投機的推論(Pyrightは高速) | Rigorの型オブジェクトはイミュータブルな共有構造。キャッシュ駆動の再計算はインクリメンタル |
| ファイルレベルでのstrict / basic / offの設定 | severity_profile:はプロジェクト全体。ファイルごとは# rigor:disable-fileで |
Pyrightの「積極的に推論し、ナローイング」というオーサリングループを使ったことがあれば、Rigorは親しみやすい。最大の調整はRigorのアノテーションが.rbソースではなく.rbsファイルに存在することだ。
「アノテーション不要」— ここでも本当
Section titled “「アノテーション不要」— ここでも本当”典型的なmypyのオンボーディング例:
def classify(n: int) -> Literal["zero", "positive", "negative"]: if n == 0: return "zero" if n > 0: return "positive" return "negative"
result = classify(7)# mypy: result: Literal['zero', 'positive', 'negative']Rigorで対応するコード — アノテーションなし:
def classify(n) return :zero if n.zero? return :positive if n.positive? :negativeend
result = classify(7)assert_type("Constant<:zero> | Constant<:positive> | Constant<:negative>", result)同じ精度。片方はパラメータとreturnのアノテーションを書くが、もう片方は書かない。
sigが必要なとき — パブリックライブラリ境界のため、パラメータバリデーションのため、def.return-type-mismatchを発火させたいとき — それは.rbソースではなくsig/<file>.rbsに書く。
ジェネリクス
Section titled “ジェネリクス”両エコシステムともジェネリクスを持つ。Rigorのものはより保守的なRBSのもの。
| Python | Rigor(RBS経由) |
|---|---|
T = TypeVar('T') | メソッドまたはクラス名の後の[T] |
def first(xs: list[T]) -> T | def first: [T] (Array[T]) -> T |
Generic[T]クラス | class Foo[T] |
T = TypeVar('T', bound=Comparable) | [T < Comparable](RBSのバウンド付き型パラメータ) |
ParamSpec | (現時点で対応なし) |
TypeVarTuple | (現時点で対応なし) |
Concatenate[X, P] | (現時点で対応なし) |
Rigorのジェネリクスカバレッジはより保守的なRBSのものと一致するが、一般的なケース(コレクション、ジェネリックコンテナへのメソッド、クラスレベル型パラメータ)はカバーしている。
Protocol ↔ RBSインターフェース
Section titled “Protocol ↔ RBSインターフェース”PythonのPEP 544はProtocolによる構造的型付けを導入した。RubyのRBSは最初のリリース以来構造的なinterface _Fooを持っている。
class SupportsClose(Protocol): def close(self) -> None: ...interface _SupportsClose def close: () -> voidendcloseを定義するクラス(正しいシグネチャで)は両方を満たす。どちらのシステムもクラスに継承を宣言することを要求しない — 構造的なマッチは暗黙的。
Rigorはsig/からRBSインターフェースを読む。RBS宣言されたパラメータが_SupportsCloseの場合、Rigorはmypy/PyrightがProtocolに対してチェックするのと同じように、call-siteの引数を構造的にチェックする。
Pythonから持ち越した注意点をひとつ: Rigorにおいて「protocol」はこれを意味しない。構造的型付け(structural typing)の概念はRBSのinterfaceであり、「protocol」は別の、プラグインが宣言する機能(パススコープの振る舞い契約)のために予約されている。プロトコルと構造的型付けの付録がこの区別を詳しく解説する。
mypy / PyrightにあってRigorにないもの
Section titled “mypy / PyrightにあってRigorにないもの”- TypeVarへの分散アノテーション。
TypeVar('T', covariant=True)。Rigorは標準ライブラリに基づくRBSの分散に依拠する。ユーザー側での分散オーサリングはない。 Final/ イミュータビリティ追跡。Rigorは「この名前は再代入されない」をまだモデル化しない。@overloadスタック。RBSはメソッドオーバーロードをサポートするが、Rigorのアナライザーのディスパッチロジックはmypyのパターンベースのオーバーロード解決より保守的。- デコレーター対応の型変換。Pythonの型エコシステムは関数の型を変換するデコレーターのサポートが発達している。Rubyの対応物は少なく、Rigorは
Module#prepend/define_method変換をまだモデル化しない。 async/await型。RubyにはFiberとAsyncがあるが、非同期型のRBSサーフェス(surface)はPythonのCoroutine[T, U, V]より断片的。
Rigorにあってmypy / Pyrightにないもの
Section titled “Rigorにあってmypy / Pyrightにないもの”- メソッド呼び出しを通じた定数folding。mypyもPyrightもリテラルをfoldするが、どちらも任意のビルトインメソッドを通じたfoldはしない。Rigorは
Numeric、String、Symbol、Array、Hash上のカタログ化された純粋メソッドのセットを通じてfoldする。 - 自動ナローイングを持つファーストクラスのリファインメントキャリア。
non-empty-string、positive-int、numeric-string、int<min, max>— 述語で制限された値が対応するRubyの述語メソッドでナローイングされる。 - false-positiveなしのスタンス。mypyは
--no-warn-unused-ignoresや--ignore-missing-importsを設定しない限り動的コードについて警告する。Rigorは設定なしでDynamic[Top]には沈黙する。 - 引数シェイプによる戻り型変化のプラグインサイド。Pyrightの「型エイリアスナローイング」とmypyのオーバーロードスタックがいくつかのケースをカバーする。Rigorのプラグイン契約(contract)はディスパッチポイントで完全なRubyコードを提供する。
rigor-lisp-evalの例が標準デモ —Lisp.eval([:+, 1, 2])はIntegerを返し、Lisp.eval([:<, 1, 2])はboolを返す。
マイグレーションvignette
Section titled “マイグレーションvignette”mypy-tightenedなPythonモジュールをRubyに移植している。元のコード:
def classify_input(s: str) -> Literal["empty", "numeric", "text"]: if not s: return "empty" if s.isdigit(): return "numeric" return "text"
def shout(s: str) -> str: assert s, "expected non-empty" return s.upper()Rigorの移植:
def classify_input(s) return :empty if s.empty? return :numeric if s.match?(/\A\d+\z/) :textend
def shout(s) raise ArgumentError if s.empty? s.upcaseend%a{rigor:v1:return: Constant<:empty> | Constant<:numeric> | Constant<:text>}def classify_input: (String s) -> Symbol
%a{rigor:v1:param: s is non-empty-string}def shout: (String s) -> non-empty-string得られるもの: s.empty?は認識されたリファインメントナローワー(assert sは不要)。match?(/\A\d+\z/)はまだnumeric-stringにナローイングしない(v0.1.1のロードマップにある — docs/ROADMAP.md参照)が、最終的な動作はPyrightでのs.isdigit()ナローイングを反映する。
次のステップ
Section titled “次のステップ”この付録セクションの残りを順番に読む必要はおそらくない。3つの有用なポインタ:
- 第2章 — 日常的に出会う型 — リファインメント語彙が新しければキャリアの種類を確認する。
- 第3章 — ナローイング — フローセンシティブなルール — mypyのナローイングの直接対応物。
- 第7章 — RBSと
RBS::Extended— ディレクティブ文法 —predicate-if-trueはRigorのTypeGuard/TypeIs。
他のツールと比較したい場合は、兄弟付録ページがTypeScript、PHPStan、Steep、TypeProfをカバーしている。
© 2026 TypedDuck. Licensed under CC BY-SA 4.0.