Skip to content

Rigor Type System — Quick Guide

Rigor is an inference-first static analyzer for Ruby. Its type language is a strict superset of RBS: every RBS type round-trips losslessly through Rigor’s internal representation, and every Rigor-inferred type erases conservatively back to ordinary RBS.

This file is the one-page entry point. The full normative specification lives in docs/type-specification/. Design rationale and rejected/deferred options live in docs/adr/1-types.md.

  • No inline DSL. Application Ruby code stays free of Rigor-only annotation syntax. RBS, rbs-inline, and Steep-compatible annotations are accepted as type sources.
  • Lossless RBS in, conservative RBS out. Internal precision (literal sets, refinements, shapes, dynamic-origin provenance) MAY exceed what RBS can spell. On export, Rigor erases to ordinary RBS that is never narrower than what was proved.
  • Three-valued certainty. Type, reflection, and member queries return yes, no, or maybe. maybe does not narrow as if yes and does not produce the opposite-edge fact as if no.
  • Two relations, kept separate. Subtyping (A <: B, value-set inclusion) and gradual consistency (consistent(A, B), dynamic-boundary compatibility) are not unified. untyped is the dynamic type, distinct from top.
  • Robustness principle (Postel’s law). Rigor-authored types are strict on returns and lenient on parameters. A precise return propagates useful facts through the inference engine; a permissive parameter prevents coercion workarounds at call sites. Hand-written RBS authorship binds — the principle directs Rigor’s defaults, not user-supplied signatures. See robustness-principle.md for the normative rule and adr/5-robustness-principle.md for the design rationale.

A vanilla checker asks “what class is this?”; Rigor asks “what subset of values can this expression produce?” and records the answer in a carrier. The everyday ones, with the type Rigor infers in the trailing comment:

n = 1 + 2 # Constant<3> — a single proven value
len = ARGV.size # int<0, max> — a bounded range (a.k.a. non-negative-int)
s = id.downcase # lowercase-string — a refinement: String restricted by a predicate
row = [1, "a"] # Tuple[Constant<1>, Constant<"a">] — per-position array shape
cfg = {port: 8080} # HashShape{port: Constant<8080>} — per-key hash shape
tag = choose_color # Constant<:red> | Constant<:blue> — a finite union
x = gets # String | nil; Dynamic[Top] when nothing can be proved

Angle brackets hold a concrete value or bound (Constant<3>, int<0, max>); square brackets hold type parameters, as in RBS (Tuple[…], Dynamic[Top]). Every carrier erases to its base RBS class at the boundary (Constant<3>Integer), so adopting Rigor is strictly additive. The full walkthrough is handbook chapter 2 — Everyday types.

FeatureWhere to read more
Dynamic[T] algebra and gradual-typing provenancevalue-lattice.md, special-types.md
Edge-aware control-flow narrowing inside compound conditionscontrol-flow-analysis.md
Negative facts, difference types, complement display contracttype-operators.md
Structural duck typing through RBS interfaces and inferred object shapesstructural-interfaces-and-object-shapes.md
Capability roles (_RewindableStream, _ClosableStream, …) for IO-like compatibilitystructural-interfaces-and-object-shapes.md
Refinements (non-empty-string, positive-int, hash-shape extra-key policy, …)imported-built-in-types.md, rigor-extensions.md
RBS::Extended annotations (%a{rigor:v1:…} for predicates, assertions, conformance)rbs-extended.md
Lightweight HKT — defunctionalised App[F, A] type constructors (e.g. JSON.parse return discrimination)rigor-extensions.md, rbs-extended.md, adr/20-lightweight-hkt.md
Inference budgets and boundary contracts for recursion / operator ambiguityinference-budgets.md
Diagnostic identifier taxonomy and suppression markersdiagnostic-policy.md
Conservative RBS erasure and hash-shape erasure algorithmrbs-erasure.md

The full reading order, conventions (RFC 2119 keywords, RBS-first compatibility hierarchy), and one-line description of each topical document live in docs/type-specification/README.md.

For analyzer-internal contracts that complement the type specification (engine-surface, type-object public API), see docs/internal-spec/README.md.

© 2026 TypedDuck. Licensed under CC BY-SA 4.0.