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.