Types as Interfaces
Scope of the Discussion
- Centered on how to model behavior and structure: types vs interfaces, records vs maps, and how far type systems should go in expressing invariants.
- Strong focus on practical tradeoffs between theoretical elegance and real‑world ergonomics and evolution of codebases.
Professional Practice and “Basic” Type Modelling
- Some commenters argue this is a “first‑year” problem and most professionals just ship code without overthinking elegance.
- Others push back, saying even experienced programmers regularly get data modelling wrong and revisiting fundamentals is valuable.
- Consensus: there is rarely an “obviously optimal” design; tradeoffs only become clear as software evolves.
Row Typing, Extensible Records, and Map‑like Types
- Several posts discuss “row typing” / “extensible records”: statically known maps from field names to types that can be merged and extended.
- Languages mentioned as supporting this (with varying ergonomics) include PureScript, Elm, Haskell, OCaml, Scala 3, C++, Ceylon, TypeScript, and others.
- Advantages: composability (e.g.,
mergeMaps Foo Bar), avoiding repeated field listings, and more generic code over “has field X” constraints. - Concerns: ceremony, performance vs nominal types, clunky syntax, and unclear real‑world payoff relative to simple structs/records.
Types vs Interfaces / Behavior vs Shape
- One view: interfaces describe behavior (methods), types describe structure/shape; mixing them increases complexity.
- Counter‑view: structure is part of observable behavior; you must know shapes and types of values to use APIs correctly.
- Some note language differences: in TypeScript and C#, interfaces can be purely structural; elsewhere, they’re more behavior‑oriented.
- Object‑oriented analogs (interfaces, traits, mixins) are compared to Haskell typeclasses and record constraints; composition without duplication remains tricky.
Soundness, Pragmatism, and TypeScript
- TypeScript’s type system is praised as highly expressive but explicitly unsound.
- Some argue unsoundness is a feature: it allows typing idiomatic JavaScript and keeps the language practical.
- Others emphasize that unsoundness limits how much you can rely on types as specifications, but still greatly reduces bugs via “sanity checks.”
- Discussion contrasts “clean” foundations (Idris, Scala 3, etc.) with messy but effective systems like TypeScript.
Dependent, Refinement, and Richer Types
- Several participants want types to encode all known invariants (ranges, refined subsets, domain rules), citing dependent types (Idris, Coq, etc.) and refinement types (Liquid Types, Ada predicates, SPARK).
- Benefits: stronger guarantees, constraints propagated through the system, fewer runtime checks.
- Pushback: high cognitive and tooling cost, rigidity, difficulty composing evolving systems, and theoretical limits (halting problem).
- Suggested compromise patterns include “parse, don’t validate” and branded/newtypes that validate once and then use stronger types.
Schemas, Maps, and Evolution
- Debate over “just use maps” / schemaless approaches:
- One side: schemaless systems still have implicit schemas; pushing validation to every consumer is error‑prone.
- Other side: partial/implicit schemas ease evolution, coexistence of versions, and rapid change.
- Examples raised: Protobuf’s move away from
required, default values, and how database schemas parallel type‑system constraints.