Wado

WEP: Type Stringification

Context

Converting an arbitrary value to a string splits into two questions a language answers separately: what a value looks like when a developer is debugging, and what it looks like in user-facing output.

Language Debug output User-facing output
Rust {:?}, #[derive(Debug)] Display, hand-written
Elixir Inspect protocol, total String.Chars protocol
Python __repr__, default unhelpful __str__
Ruby inspect to_s
Kotlin toString(), data class only the same toString()

Wado has no macros, so a derive-style opt-in is not available: debug output is either always present or absent for most types.

Decision

Debug output is total; user-facing output is opt-in.

Form Trait Availability
${x:?} Inspect Every type. Derived from the type's shape
${x} Display Only where an impl exists — otherwise a compile error

The two never substitute for each other. ${x} does not fall back to debug output, so T: Display certifies that the type has a string form its author chose; and an impl Display never changes what ${x:?} prints, so the escape hatch survives a wrong or misleading Display.

Debug output follows Wado literal syntax — Point { x: 10, y: 20 }, [1, "a", true], Option::Some(42) — so it reads as the expression that builds the value. The per-type format is specified in WEP: Inspect, the derivation rules in WEP: Trait Derivation Policy, and both traits' signatures in WEP: Format Traits.

Consequences

Debugging needs no preparation: ${x:?} works on a type the moment it is declared, and keeps working as fields and cases change. The cost is code size — each inspected type instantiates an impl — which DCE reclaims for the types a program never prints.

Requiring Display costs a hand-written impl on every struct and variant meant for user-facing output, and rejects ${x} on the ones that skip it. That is what makes the two forms distinguishable at a glance.

Known gaps

References