Keywords¶
Accepted
Accepted for the V1 reserved word set and non-keyword policy.
Catalyst should keep the keyword set small. New keywords must earn their place by making source meaning clearer than ordinary functions, declarations, attributes, or prelude-defined language surface.
Criteria¶
Prefer a keyword when it:
- changes parsing or binding structure
- changes runtime representation or dispatch mode
- marks a core declaration or statement form
- prevents a misleading ordinary expression reading
Avoid a keyword when an ordinary const, fn, comptime function, attribute, or library API can express the idea clearly.
V1 Reserved Words¶
V1 reserves these source words:
fn
const
var
pub
priv
import
comptime
dyn
opaque
as
is
struct
enum
error
contract
impl
move
defer
if
else
for
in
while
return
break
continue
try
catch
orelse
and
or
not
true
false
void
null
undefined
Reserved words cannot be used as ordinary identifiers. Some reserved words introduce declarations or control-flow forms, some are type modifiers, some are operator words, and some are primitive value forms.
Self is a special comptime type constant inside type, contract, and implementation bodies. It is capitalized because it is type-like; it is not an ordinary lowercase keyword and is not globally available.
import¶
import introduces a declaration-scope import:
import module("json")
const std = module("std")
import std.math
It is reserved because imports change name and implementation visibility in the current declaration scope. Import semantics are documented in Imports.
comptime¶
comptime is reserved because it changes parsing or evaluation mode in several positions:
- before a parameter, it marks an explicit compile-time parameter;
- before an expression or block, it forces compile-time evaluation;
- before
fn, it declares a compile-time-only function.
Examples:
fn sum(comptime S: Sequence(f32), xs: *const S) f32
const n = comptime compute_len()
comptime fn validate_len(n: usize) void {
}
Anonymous static contract and structural parameters use impl in pointer pointee position, such as fn sum(xs: *const impl Sequence(f32)) f32, rather than a comptime type-mode form.
The canonical evaluation and comptime fn rules are documented in Comptime.
dyn¶
dyn marks dynamic contract object types:
fn use(xs: *const dyn SomeDynSafeContract(f32)) void
var xs: *const dyn SomeDynSafeContract(f32) = &value
var owned: Box(dyn SomeDynSafeContract(f32)) = try box_dyn(SomeDynSafeContract(f32), ConcreteValue, alloc, value)
dyn changes runtime representation and dispatch mode. It is a keyword/type modifier, not an attribute. dyn Contract is a concrete unsized erased type. Borrowed dynamic contract objects use pointers such as *dyn Contract and *const dyn Contract; owned dynamic contract objects use resource types such as Box(dyn Contract). The applied contract must be dyn-safe; static-only operations must be explicitly guarded out of the dyn surface.
impl¶
impl declares semantic conformance in an implementation header and marks anonymous static contract parameters in pointer pointee position:
impl Buffer as Sequence(f32) {
}
fn use(xs: *const impl Sequence(f32)) f32
In a function declaration parameter, *impl Contract or *const impl Contract means the concrete implementer type is statically known and dispatch is monomorphized. The pointer value remains a runtime value. The form is function-declaration-parameter-only in V1; use dyn Contract for erased runtime polymorphism.
Canonical style keeps pointer tokens attached and writes *impl C or *const impl C. Spaced forms such as * impl C and * const impl C remain parseable source, but the formatter rewrites them and style linting may reject them.
opaque¶
opaque is the erased pointee type for dynamic dispatch and low-level erased data pointers:
*opaque
*const opaque
It is not a value type and cannot be directly dereferenced. Typed pointers coerce to opaque pointers in expected-type contexts, and opaque pointers recover typed pointers with the explicit cast, as_type, and assume_aligned operations documented in Reference and Mutability. opaque is separate from void; void means no value.
as¶
as marks conformance declarations and catch error bindings:
impl Buffer as Sequence(f32) {
}
var bytes = read(path) catch as err {
diag.err(err)
empty_bytes
}
In an impl header it declares semantic conformance. Ordinary conformance checks use the T.implements(Contract) method on comptime Type values.
In a catch expression it introduces the immutable binding for the caught error value. The catch-specific grammar is documented in Error Handling.
orelse¶
orelse is the optional defaulting unwrap operator:
const value = maybe_value orelse fallback
It is reserved because it short-circuits and unwraps one optional layer as a core source form rather than an ordinary function call. Optional defaulting semantics are documented in Optional Types.
contract¶
contract introduces a semantic contract type expression:
const RealtimeSafe = contract {
}
fn Sequence(comptime T: Type) => contract(Indexable(T)) {
}
It is parallel to struct { ... }: both are type expressions returning Type values.
struct, enum, and error¶
struct introduces a concrete aggregate type expression:
const Header = struct {
channels: u32
}
Generic structs are ordinary comptime functions returning struct type expressions.
enum introduces a closed tag-only enum type expression:
const Ordering = enum {
less,
equal,
greater,
}
error { ... } introduces a closed error-set type expression. Error is the prelude top error-set Type value:
const ReadError = error {
FileNotFound,
PermissionDenied,
}
fn read(path: Path) []u8!Error
Enum and error case source semantics are documented in Enums and Error Types.
move¶
move explicitly transfers a value out of a local binding or by-value parameter:
var next = move current
consume(move file)
return move result
It is a keyword-owned source form, not a function. It changes binding state: after move value, the source binding is uninitialized until reassigned, or permanently unusable if it is a const binding.
Plain assignment, argument passing, aggregate construction, and return value are copy-like in the semantic model. For types whose declaration metadata forbids copying, those forms are errors unless the source is a fresh rvalue or the programmer writes move.
move applies only to the restricted movable-place forms documented in Move Expressions. It is not a generic prefix operator over arbitrary expressions.
Control Flow and Cleanup¶
if, else, for, in, while, return, break, continue, try, catch, and defer introduce core control-flow, error-handling, or scope-exit forms. Their source semantics are documented in Control Flow, Error Handling, and Defer. Error-only cleanup syntax such as errdefer is deferred to CEP-0065: Error-Only Defer.
Boolean Operator Words¶
and, or, and not are boolean operator words. They are not ordinary functions or overloadable identifiers:
not ready
ready and valid
ready or fallback_allowed
The symbolic forms &&, ||, and !condition are not V1 boolean operators. ! is reserved for error-return type syntax such as T!E and T!.
Primitive Value Words¶
true, false, void, null, and undefined are primitive value forms. Their typing rules are documented in Literals and Built-In Types.
is¶
is is reserved for future runtime type or pattern tests and narrowing:
if value is SomeType {
}
Runtime is tests are deferred from V1. Reserving the word now prevents later narrowing syntax from colliding with ordinary identifiers.
Non-Keywords by Default¶
Semantic contract construction, structural constraints, and reflection operations should start as prelude-defined declarations over primitive Type/reflection APIs rather than keywords:
satisfies(.{ ... })
mutable_fields(.{ ... })
module("std")
include("./local.ct")
Buffer.implements(Sequence(f32))
Contract definitions use contract(...) { ... } type expressions. Conformance declarations use impl Type as Contract { ... }. Prelude declarations such as satisfies(...), mutable_fields(...), module(...), and include(...) remain ordinary lowercase functions at the source level. Some prelude functions may be implemented over compiler primitives when they need compiler-owned capabilities, such as resolving root module names. Type values provide method-style reflection such as T.implements(Contract).
Attribute names are also not keywords. The @ token starts attribute syntax, but provider names such as export, callconv, resource, and deprecated resolve as ordinary attribute providers.