Documentation / Design scope

Design scope

Moth keeps the source language small. This page explains what belongs in the language, what defers and what the language intentionally excludes.

Choose the explanation level: Design principles

Design principles

Moth is a small language by design. Every feature has to earn its place.

How Moth decides

The language prefers one clear mechanism over many specialised syntax forms. When a problem can be solved with an existing feature, the language doesn't add a second way to do it.


    -- Templates handle text composition
    greeting = [: Hello, [name]]

    -- Choices handle runtime heterogeneity
    Status :: Ready, Waiting | reason String |;

    -- Error! handles expected failures
    result = parse(text)!
    

What this means for you

  • fewer syntax forms to learn
  • code from different authors looks similar
  • the compiler can give clear error messages
  • optimisations happen behind the scenes

Moth keeps the source language small. Compiler complexity is acceptable when it makes the programmer-facing model simpler, safer and more predictable.

Central bias

  • keep the source language small, explicit and statically inspectable
  • prefer constrained built-in mechanisms over general metaprogramming
  • prefer static resolution over runtime dynamism
  • keep backend optimisations hidden behind stable source semantics
  • avoid features that need pervasive solver-heavy reasoning

Preferred constrained mechanisms

| Need | Preferred mechanism | | ---- | ------------------- | | Compile-time generation | Templates, constants and builder directives | | Static reuse | Generic functions and explicit trait bounds | | Runtime heterogeneity | Choices | | Expected failure | Error!, postfix propagation and catch | | Invariant failure | assert | | Explicit result-like domain values | Ordinary choices | | Platform integration | Builder packages, external packages and source-backed packages | | Memory safety | GC baseline plus mandatory borrow and lifetime-region validation | | Mutation | Explicit exclusive access rather than implicit mutation | | Ownership | Compiler-inferred optional transfer rather than source-visible move or lifetime syntax | | Declared bulk lifetimes | Accepted deferred group / into hard lifetime regions | | Performance | Hidden compiler and backend optimisation |

Read next

Choose the explanation level: Deferred and outside scope

Deferred and outside scope

Moth has two kinds of features that aren't available today.

Deferred: coming later

Some features are planned but not built yet. The compiler knows about them and gives a helpful error when you try to use them. Examples include declared memory groups and recursive choice types.

Outside scope: not planned

Other features are intentionally excluded. They don't fit Moth's design goals. The compiler rejects them, but the error message won't say "coming soon" because they aren't coming.

Why exclude features?

Moth prefers:

  • one clear mechanism over many specialised forms
  • static resolution over runtime dynamism
  • explicit behaviour over implicit magic
  • small surface area over broad feature coverage

When a feature would make the language broader, more implicit or harder to reason about locally, Moth says no.

Moth separates future work into two categories.

Deferred

Deferred features fit Moth's language design but aren't implemented yet or aren't part of the current source surface. The compiler rejects deferred syntax with structured diagnostics pointing to the feature's status.

Examples of deferred features:

  • declared memory groups (group / into)
  • choice payload field access without pattern matching
  • nested choice payload patterns
  • recursive choice types
  • generic external package functions
  • package manager, versions and lockfiles
  • broader standard trait taxonomy
  • field/path reactive subscriptions

Outside scope

Outside-scope features conflict with the intended language model. They need an explicit design change before implementation. The compiler rejects these forms with ordinary diagnostics.

Outside-scope features aren't "not yet implemented." They're intentionally excluded because they would make the language broader, more implicit, more solver-heavy or harder to reason about.

See Excluded language families for the exact list.

How to tell the difference

If a feature could be added without changing Moth's core design, it's deferred. If adding it would require rethinking the language model, it's outside scope.

Diagnostic distinction

A deferred-feature diagnostic tells the author that accepted syntax hasn't landed yet. An outside-design-scope diagnostic tells the author that the form conflicts with the language model and won't be accepted without an explicit design change.

Both use structured CompilerDiagnostic. The distinction matters because the remedy differs: a deferred feature needs implementation progress, while an outside-scope form needs a different approach or a language philosophy change.

The progress matrix at Progress matrix reports whether a deferred surface is implemented today. This page explains whether the surface belongs in the language at all.

Read next

Choose the explanation level: Excluded language families

Excluded language families

Moth says no to many features that other languages include. Here is why.

No general macros

Macros create a second language inside your code. Moth uses templates, constants and builder directives for compile-time work instead.

No closures or function values

General closures add complexity to the type system and make code harder to follow. Moth uses reactivity for the common UI pattern that closures usually solve.

No trait objects

Trait objects need runtime dispatch and hidden wrappers. Moth uses choices for runtime variety and generic bounds for static reuse.

No operator overloading

Operators in Moth always do the same thing. You can't redefine + or == for your own types.

No reflection

You can't inspect types at runtime or write code that examines other code's structure. This keeps programs predictable and easy to read.

The pattern

Each excluded feature has a Moth replacement that is more constrained and more explicit. The language trades generality for clarity.

The following surfaces are intentionally outside Moth's language design scope. A feature listed here shouldn't be implemented unless the language philosophy is explicitly changed first.

Macros and metaprogramming

General-purpose macros, procedural macros, derive macros and AST macros create a second compile-time language and make code harder to inspect. Templates, constants and builder directives are the constrained compile-time surface.

General closures and function values

General closures, anonymous function values, generic function values and higher-order polymorphism add broad function-value semantics and solver pressure. Reactivity is the constrained UI-oriented mechanism intended to cover many closure-heavy template cases.

Dynamic trait values

Dynamic trait values and trait objects require erased wrappers, runtime dispatch, dynamic-safety rules, backend-specific runtime support and a second meaning for trait names. Use choices for runtime heterogeneity and generic trait bounds for static reuse.

Trait-level programming

Trait inheritance, trait aliases, trait composition, default methods, associated types and associated constants turn traits into a type-level programming system rather than simple method contracts.

Generic traits and generic trait methods

Generic traits and generic trait methods make trait solving significantly more complex and create Rust-like abstraction patterns.

Conformance extensions

Blanket, conditional, negative or specialised conformance requires coherence and specialisation rules outside Moth's simplicity target. Structural conformance must not silently imply conformance. Type must TRAIT stays explicit.

Constraint extensions

Type-set constraints, union constraints, underlying-type constraints and user-defined numeric or operator constraints make generic bounds a constraint sublanguage. Generic bounds name traits only.

Operator overloading

Operators stay compiler-owned and predictable. No source-visible operator overloading.

Source-authored receiver extensions

Source-authored receiver methods for builtins, imported types, dependency package types, external opaque types or types declared in another file are rejected. Receiver methods belong only to the same file as their nominal receiver type.

User-defined map semantics

User-defined HASHABLE, custom map hashers or comparers and user-defined keys for builtin maps are rejected. Builtin map syntax stays scalar-keyed. More sophisticated maps belong in packages as ordinary structs.

First-class result values

First-class public Result values and result pattern matching stay outside the language. Error!, postfix ! and catch form the error path. Authors can define ordinary choices when they want explicit result-like domain values.

Exceptions and invariant assertions

Expected failures use Error!. Invariants use assert. No public exception system. The two lanes stay distinct: Error! handles recoverable failure, assert handles impossible invariant violation.

Reflection and type introspection

Reflection, runtime type IDs, compile-time type inspection and type-returning functions encourage generic metaprogramming and weaken static readability.

Higher-kinded types

Higher-kinded types, type functions, partial type application and parameterised type aliases introduce a type-level abstraction language.

Const generics

User const generics beyond fixed collection capacity are rejected. Capacity syntax remains a small built-in collection feature, not a general type parameter system.

Source-visible memory annotation systems

Source-visible lifetime, reference-category and ownership annotation systems stay outside the language. Moth omits explicit reference types and lifetime syntax, not references themselves. The compiler tracks ownership and lifetimes internally.

Source-visible RC and dynamic shared ownership

Source-visible RC, retain/release, weak ownership, finalisers and unrestricted dynamic shared ownership are outside language design scope. Internal backend RC remains allowed for already-legal topology. Diagnostics, common-owner groups, copy and builder lifecycle roots replace source-level RC.

Backend-specific semantics leaking into source

Backend-specific observable semantics must not leak into the language. Source semantics stay backend-neutral. Backends receive validated topology and may not reconsider source legality.

Read next