Skip to content

The Wyn type system — what we check and what we don't

Every language makes tradeoffs in its type system. Rust catches everything at compile time but makes you fight the borrow checker. Python catches nothing until runtime but lets you move fast. Go is somewhere in between.

Wyn is closer to Go's philosophy: catch the common bugs at compile time, don't make the programmer jump through hoops for the rest.

What wyn check catches

The type checker runs before compilation and catches these errors:

  • Undefined variables and functions — typos, missing imports
  • Type mismatches — assigning a string to an int variable, passing wrong types to functions
  • Wrong argument counts — calling add(1) when it expects add(1, 2)
  • Const reassignment — trying to modify a const variable
  • Missing return values — function declares -> int but doesn't return
  • Unused variables — warnings for declared-but-unused vars
  • Unreachable code — code after a return statement
  • Assignment in conditionsif x = 5 when you meant if x == 5

That's 8 categories of errors caught before your code runs. For most programs, this catches the bugs that actually matter.

What we don't catch

Some things slip through to runtime:

  • Array index out of bounds — we check this at runtime with a helpful error message, not at compile time
  • Null/nil access — Wyn doesn't have null, but some C interop can produce it
  • Integer overflow — 64-bit integers, so this rarely matters in practice
  • Some stdlib return types — a few stdlib functions default to int in the checker when the return type isn't explicitly registered

We could add more static analysis. We chose not to, because every check we add is a check the programmer has to satisfy. We'd rather you ship working code today than fight the type system for a week.

The philosophy

From the Wyn flight rules:

If it compiles, it should work.

We take this seriously. The type checker is designed to catch the bugs that actually crash your program. It's not designed to prove your program is correct — that's your job.

The runtime adds a safety net: array bounds checking, stack overflow detection, and helpful error messages with line numbers. Between the compile-time checker and the runtime safety, most bugs are caught before they reach production.

What's next

We're working on:

  • Better generic type inference (so max(3, 7).to_string() works without a temp variable)
  • Trait bounds (fn sort<T: Comparable>(arr))
  • Exhaustive match checking

These will make the type system stricter over time, without breaking existing code. The goal is always the same: catch real bugs, don't create busywork.

MIT License