Pranoy Dutta’s short essay is three language features with a minimal code sample each: Crystal’s flow typing, Rust’s borrow checker, and contract programming in D. The thread connecting them is placement — the check lives where the invariant does, declared once and enforced by the compiler or runtime, instead of being restated in every caller and re-verified in review.
Flow typing. In Crystal a variable can change type over its lifetime, and the compiler tracks the type at each program point:
- Before a branch
my_varisInt32; inside it,String; afterwards,Int32 | String— the union is what the compiler reports when it “cannot actually guarantee one or the other.” - Calling a
Stringmethod on that union is a compile error until you addis_a?(String), which narrows the type. The check is demanded only where it’s genuinely unknowable. - Static checking that feels dynamic, with no meaningful runtime cost. TypeScript’s control-flow narrowing is the same idea.
Borrow checking. Rust’s rules are short: a borrow can’t outlive its owner, and you get exactly one mutable reference (&mut T) or any number of immutable ones — never both.
- That’s a readers-writer lock as a compile-time property. Data races only require synchronizing reads against writes, so serializing writes to a location is precisely the invariant that removes them.
- Nothing at runtime. The cost is compile-time complexity, which Dutta argues is inherent to the subject matter rather than imposed by the language.
Contract programming. D splits two things most languages blur:
assert— a violated program invariant, meaning a bug in the program.enforce— an external condition (bad input, environment) that throws.- Functions take
in { }preconditions andout (result) { }postconditions; classes take aninvariant() { }block checked around members. - In his
BankAccountexample,balance >= 0is declared once at class level instead of re-checked at the top and bottom of every method.daysInFebruaryasserts its result is 28 or 29 — anything else is a logical error.
The portable idea is the assert / enforce distinction: naming “this is impossible” separately from “the world misbehaved” changes how a stack trace gets triaged. The second is that invariants declared next to the field they constrain — and checked by the platform — are cheaper than the equivalent discipline maintained in review. Dutta is honest about the trade: borrow checking costs ergonomics, and flow typing’s unions show up in your error messages.