Build configuration values let a project or source module receive typed primitive values before its modules are compiled.
Implemented contract. Direct and source #Config, typed --input values and explicit @project are supported. Entry-local config: blocks remain deferred; see Progress for current support.
#Config values
A direct project field can use #Config of String:
project #= |
name = "my_project",
version #Config of String = "0.1.0",
entry_root = "src",
|
Source modules use the same spelling:
analytics #Config of Bool = false
if analytics:
send_analytics()
;
#Config values feed ordinary if; there is no #Config if.
Command examples
moth build . --input enabled=true
moth build . --input retries=4
moth build . --input api_url=https://example.com
Bare true and false infer as Bool, whole numbers as Int, decimal values as Float, and other text falls back to String. Quote "true" when a String is intended. Omitted optional values can resolve to none.
Build configuration cannot inspect operating-system, architecture, backend or target identity; builders handle platform mapping.
Implemented build configuration values. The compiler supports direct and source #Config contracts, typed --input values for build, check and dev, ordinary static-if integration and immutable explicit @project. Semantic fingerprints are constructed transiently during frontend compilation; retaining and comparing them for dependency-aware targeted reuse remains deferred to the existing incremental and persistent artefact work. Entry-local config: blocks remain deferred. See Progress for current status.
Build configuration values are typed primitive values resolved before module AST execution semantics are finalised. They can represent schema-approved project metadata, deployment settings, feature choices and similar stable semantic configuration. They do not represent project identity, source-discovery settings, operating-system, architecture, backend or target identity.
Direct project #Config fields
Direct project #Config is governed by the selected project schema's per-field policy. Only a direct field marked Configurable by that policy, with a supported primitive or optional primitive shape, may use #Config of T. A primitive shape alone does not make a field configurable.
project #= |
name = "moth_docs",
version #Config of String = "0.1.0",
author #Config of String? = none,
license #Config of String? = none,
entry_root = "src",
|
name is the required project identity field and entry_root controls source discovery; both are fixed-only. Compiler-control fields such as template_const_loop_iteration_limit are also fixed-only. The built-in schema marks ordinary metadata fields such as version, author and license as configurable, and open metadata fields not registered by that schema are configurable too. The exact policy comes from the selected schema; a primitive shape alone never makes a registered fixed-only field configurable.
Direct-field-only placement applies. Nested project fields, builder and tooling sections, entry-local config: blocks and runtime declarations cannot use #Config. Accepted types are String, Int, Float, Bool, Char and optional forms of those types.
The grouped project record remains open for ordinary metadata: schema-unknown folded fields are accepted and preserved for @project. Openness and qualifier eligibility are separate: the selected schema policy decides whether a direct metadata field may carry #Config, while fixed-only identity and source-discovery fields remain non-configurable.
Source #Config contracts
Any selected source module can declare a module-wide top-level compile-time contract:
api_url #Config of String = "https://api.example.com"
max_retries #Config of Int = 3
analytics #Config of Bool = false
Source defaults must be self-contained primitive literals or none for an optional contract. They cannot use names, templates, expressions, calls, casts, field projections, collections, records or another configuration value. Same-name contracts must agree on primitive type, optionality, required/default state and normalised default value.
Command build-input typing
The command accepts repeated --input name=value arguments. It splits at the first = and preserves all later = characters. Names must be lower_snake_case. Values are typed immediately in this order:
- exact lowercase
true or false -> Bool - a complete valid signed whole-number literal ->
Int - a complete valid decimal-point or exponent literal ->
Float - a complete valid single-quoted character literal ->
Char - a complete valid double-quoted string literal ->
String - every other value ->
String
Examples:
moth build . --input analytics=true
moth build . --input retries=4
moth build . --input ratio=0.75
moth build . --input api_url=https://example.com
moth build . --input 'label="true"'
moth build . --input "separator=':'"
Anything else, including empty text after name=, falls back to String. A value beginning with a quote must be a complete valid quoted literal; malformed quoted text is a command-input diagnostic rather than a String fallback. Bare none is String text. To force an ambiguous String such as true or 42, use explicit double-quoted Moth String syntax. A Char requires explicit single-quoted syntax and never infers from an unquoted character.
Omitting an input lets optional absence resolve to none through the contract or default. A concrete T input may satisfy a matching T? contract as a present value. There is no implicit numeric/String coercion: in particular, Int does not satisfy Float. Programmatic build APIs use the same typed carrier and conversion policy.
Ordinary static if
Ordinary if is the only conditional source form. There is no #Config if.
analytics #Config of Bool = false
if analytics:
send_analytics()
;
Both branches remain valid Moth source and complete frontend validation. A known Bool selects the executable branch before HIR, preserving the selected branch's lexical scope; inactive executable work does not reach HIR or later analysis. This does not condition imports, declarations, exports or package topology.
Platform boundary
Moth source cannot inspect or branch on operating system, architecture, backend, target or runtime-platform identity. Builders own physical target decisions and map stable source semantics and capabilities to JavaScript, Wasm or other platform-specific artefacts. Source may use a stable capability API exposed by a selected builder or package without learning which physical backend implements it.
Resolution and isolation
For a schema-configurable direct project field, #Config values resolve from explicit command or programmatic input, a compatible builder primitive global, the declaration default or a missing-input diagnostic. Project-wide source contracts resolve from a compatible fixed direct project field, a resolved direct project #Config, explicit source-only input, a compatible builder global, the shared source default or a missing-input diagnostic. A fixed project field is authoritative and blocks an override.
Resolved values carry boundary-local provenance and semantic fingerprints based only on their stable name, contract type and effective value-or-absence during frontend compilation. These facts are not retained after the build. Every project or package compilation boundary owns a separate configuration namespace; consumer inputs do not implicitly satisfy dependency contracts. Configuration can specialise executable behaviour, but cannot change source discovery, dependency clauses, declarations, exports, receiver methods, traits, conformances or module/package topology. Retention, dependency-aware targeted invalidation and reuse are deferred to the existing incremental and persistent artefact work.
Related concepts