Constants and Override
const declares a value fixed at compile time (UPPER_SNAKE_CASE by
convention) — the compiler folds it throughout the program and rejects any
attempt to reassign it:
const accepts an optional type annotation and can be declared inside functions.
03-const.zolo
// Feature: Compile-time constants with `const`
// Syntax: `const NAME = value` (typically UPPER_SNAKE_CASE)
// When to use: fixed values known at compile time — limits,
// versions, immutable configuration. Unlike `let`, `const` must
// be initialized with a constant expression.
// Numeric constant.
const PI = 3.14159
const MAX_RETRIES = 3
// String constant.
const APP_NAME = "Zolo"
const VERSION = "0.1.0"
// Optional type annotation.
const BUFFER_SIZE: int = 1024
print(PI) // 3.14159
print(MAX_RETRIES) // 3
print(APP_NAME) // Zolo
print(VERSION) // 0.1.0
print(BUFFER_SIZE) // 1024
// `const` can also appear inside functions (local scope).
fn area(r: float) -> float {
const TWO_PI = 6.28318
return TWO_PI * r * r * 0.5
}
print(area(5.0)) // 78.5...
// Reassigning a `const` is an error (uncomment to test):
// PI = 3.14 // error: cannot assign to const
override sits between const (build-time) and let (runtime): the value is
fixed at module load time, but the host can replace it via the
ZOLO_OVERRIDE_<NAME> environment variable or the --set NAME=VALUE flag —
without recompiling:
override PORT = 8080 defaults to 8080 but accepts --set PORT=9000 at runtime.
17-override.zolo
// Feature: `override` — load-time substitutable constants
// Syntax: `override NAME [: T] [= default]`
// When to use: configuration that you want to fix at build time most of
// the time, but allow the host (CLI flag, env var, embedder API) to
// substitute without recompiling. Sits between `const` (fixed at build)
// and `let` (computed at runtime).
//
// Two resolution channels are supported today:
// 1. env var `ZOLO_OVERRIDE_<KEY>` at load time
// 2. CLI flag `--set KEY=VALUE` (or `--set=KEY=VALUE`) passed to
// `zolo run`
// Both fall back to the comptime-constant default when no host value is
// supplied. The `@id("custom-name")` decorator lets you choose a key
// shape that doesn't have to match the Zolo identifier (kebab-case,
// dotted-path, etc.).
//
// See `specs/override-declarations.md` for the full specification.
// Top-level only — overrides are part of the module's public interface.
override PORT: int = 8080
override MAX_CONNECTIONS: int = 1024
override DEBUG: bool = false
override APP_NAME: str = "zolo-server"
// Custom key via `@id`. Useful when the host's config uses kebab-case
// or dotted paths that the Zolo identifier can't carry.
override RATE_LIMIT: int = 100
fn main() {
print("Starting {APP_NAME} on port {PORT}")
print("Max connections: {MAX_CONNECTIONS}")
print("Debug mode: {DEBUG}")
print("Rate limit: {RATE_LIMIT} req/s")
}
// Run without overrides:
// $ zolo run 17-override.zolo
// Starting zolo-server on port 8080 ...
//
// Run with --set flag (preferred form):
// $ zolo run 17-override.zolo --set PORT=9000 --set DEBUG=true \
// --set rate-limit-rps=200
// Starting zolo-server on port 9000 ...
//
// Run with environment overrides (equivalent):
// $ ZOLO_OVERRIDE_PORT=9000 ZOLO_OVERRIDE_DEBUG=true \
// ZOLO_OVERRIDE_RATE_LIMIT_RPS=200 zolo run 17-override.zolo
//
// Hyphens and dots in `@id` keys are normalized to underscores in the
// env-var name (`rate-limit-rps` → `ZOLO_OVERRIDE_RATE_LIMIT_RPS`).
//
// Restrictions enforced by the compiler:
//
// 1. Default must be comptime-constant. Runtime calls don't compile.
//
// override BAD = read_user_input()
// // error[E_OverrideNotConst]: `override` default must be a
// // comptime-constant expression
//
// 2. At least one of `: T` or `= default` is required.
//
// override SOMETHING
// // parse error: `override` requires either a type annotation
// // or a default value
//
// 3. `override` without a default forces the host to provide a value:
//
// override REQUIRED: str
// // running without ZOLO_OVERRIDE_REQUIRED set:
// // error: E_OverrideMissing: override `REQUIRED` has no default
// // and was not set via ZOLO_OVERRIDE_REQUIRED
//
// Difference vs. `const`:
// const — fixed at BUILD time; aggressive constant-folding allowed.
// override — fixed at LOAD time; host can substitute before main runs.
// let mut / var — runtime; can change during execution.
Challenge
Add override TIMEOUT: int = 30 to the example above and run it with
--set TIMEOUT=5. Observe how the value changes without touching the source code.
See also