Skip to content

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.