Types & Values
Values & Data
Every DSL names its universe of values: numbers, strings, structured data, and sometimes functions themselves.
Kinds of Values
- Primitives: integers, reals, booleans, and text — decide how many there are; fewer is simpler.
- Composite: records/lists/tables — the shape of data the DSL manipulates (rows for SQL, circuits for Verilog).
- Callable: functions as values — powerful, but often worth omitting in DSLv1.
Representation Is an Implementation Detail
The spec describes values abstractly (an integer is a mathematical integer); the Phase 2 implementation picks the bit-level encoding. Keep the two layers separate and the semantics stay clean.
Typing Modes
Figure 1 — Earlier checks are safer; later checks are more flexible. Choose one and document it.
Dynamic Typing
Kinds are checked when operations run (Python, JS). Fast to write, slow to catch mistakes — and the easiest to prototype a DSL in.
Static Typing
Kinds are checked before any run (Java, C, Rust, and the stricter dialects). More ceremony now, fewer crashes later, and better tooling feedback.
Gradual & Inferred
TypeScript and mypy mix both; Haskell/Rust/Julia infer types so you write fewer annotations. For a DSL, inference plus optional checks is often the sweet spot: domain files stay short, errors stay early.
What Type Checking Buys
Catching Errors Early
Type errors are semantic errors with a flag: “capacity needs a number, got yes” found before any run is worth dozens of runtime messages. For configuration-like DSLs this is usually the single best safety feature.
Better Tooling, Better Models
Types power completion, hover documentation, refactoring, and more accurate AI assistance. A DSL with a stated type model gets all four — and the model is also its contract with humans.
Soundness on Purpose
“Type sound” means accepted programs cannot crash with a type error. Promise soundness explicitly, or state where escapes live (casts, dynamic slots); silent ambiguity damages trust.
Memory Semantics
Value semantics and ownership preview the deepest evolution a DSL can undergo; most DSLs never need more than the first two.
Value vs. Reference
Value semantics copy on assignment (predictable, costly on big data); reference semantics share storage (cheap, surprising to beginners). Documents: pick one per composite kind and state it.
Ownership & Borrowing
Systems languages (Rust) replace GC with static ownership rules — the “disruption that pays” example from Design Considerations. A DSL targeting embedded or systems use should study it; a configuration DSL should not.
Example: The Same Function, Two Modes
Dynamic (unannotated)
# dynamic.py — mistakes surface at runtime
def book(room, seats):
return room + seats # what if seats is "six"?
book("A10", "six") # TypeError when it runs, not before
Annotated (checked)
# checked.py — the same function with a stated type model
def book(room: int, seats: int) -> int:
return room + seats # a checker rejects "six" before any run
book(10, 6) # the fix is visible in one line
Nothing about the algorithm changed; only where the mistake is found. That location is the typing-mode decision in miniature.
Next Steps
Continue Phase 1
Values and checks meet reality in Errors & Diagnostics; then Phase 2 (Compiler Design onward) builds the machinery that checks and runs what this chapter specified.