Types & Values

Values are what programs compute with; types classify values and decide when operations are legal. Choosing a typing mode is one of the first semantic decisions a DSL makes — and one of the hardest to change later.

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

Typing modes spectrum: dynamic at runtime, gradual in between, static at compile time

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.

Resources