Declarations
This page lists the declaration forms accepted at module scope. Local bindings and expression forms are covered in Expressions.
Module declaration
Section titled “Module declaration”module app::users;A module path uses :: between segments. The module declaration belongs at the beginning of the source file.
Imports
Section titled “Imports”Selected names:
import std::core::{List, Option, print};Aliased name:
import app::storage::{User as StoredUser};Glob import:
import app::prelude::*;Grouped imports allow a trailing comma.
Visibility
Section titled “Visibility”pub makes a declaration visible outside its declaring module:
pub fn parse(input: String) -> Value { // ...}Without pub, a top-level declaration is module-private. Current Khora uses pub; export is not the public-visibility keyword.
Constants
Section titled “Constants”const NAME = "khora";pub const DEFAULT_PORT: Int = 8080;General form:
pub? const Pattern (: Type)? = Expr ;A module-level binding is a const, not a let. const mut is invalid; Khora has no mutable global binding.
Reach for const for a genuine constant — a limit, a protocol value, a fixed
default — and for let for a value computed while a function runs.
Type declarations
Section titled “Type declarations”Alias or structural definition:
pub type UserId = Int;
pub type User = { id: UserId, name: String,};Variant type:
pub type Result<A, E> = | Ok(value: A) | Err(error: E);Opaque declaration:
pub type Handle;General form:
derive(...)? pub? type Name<TypeParams>? (= TypeDefinition)? ;derive(...)
Section titled “derive(...)”A derive clause appears immediately before the type it applies to:
derive(Eq, Ord, Show, Hash)pub type Point = { x: Int, y: Int,};The syntax accepts a comma-separated list of trait names and an optional trailing comma. The compiler-supported derivable traits are Eq, Ord, Show, Hash, Decode and Encode.
The trait must be in scope, so derive(Show) needs Show imported from
std::core. Derive where the implementation follows from the fields, and write
an impl where the behaviour is a domain decision rather than a structural
consequence of the data.
derive(Decode) reads the /// above the type and above each record field
into the schema’s descriptions, so a JSON Schema rendered from the type says
what the comment says. The comment is the description; there is nothing to
write a second time.
A field’s type decides whether the derive is available at all, and a missing impl is sometimes the point:
List<A>hasShowandEqwhenAdoes, so a record holding a list derives both, andDecode/Encodefromstd::schemaon the same terms.Redacted<A>hasShow— it prints<redacted>— andDecode, and deliberately noEncode. A record holding a secret stays printable and readable, and refuses to serialise, so the build stops rather than the payload leaking. It has noEqeither: comparing two secrets byte by byte is how a timing side channel gets written by somebody who was not writing one.
Function declarations
Section titled “Function declarations”Definition:
pub fn add(left: Int, right: Int) -> Int { left + right}Generic and effectful definition:
pub fn load<A>(id: Id) -> A with { store: Store } raises StoreError{ store.load(id)!}Signature without a body:
fn intrinsic(value: Int) -> Int;General form:
pub? fn Name<TypeParams>? (Params) (-> Type)? EffectClause* (Block | ;)There is no = between a function signature and its block body, and a function definition has no semicolon after the block.
The clauses are read in order. pub fn load<A>(id: Id) -> A with { store: Store } raises StoreError says: given an Id it produces an A, it requires a
capability named store implementing Store, and it may fail with
StoreError. The two rows are independent dimensions of the type — see
Capabilities and Failures.
A public signature states both. A private helper can usually let the compiler infer them, and inference is why a row variable appears in a signature nobody wrote one into.
External C functions
Section titled “External C functions”Declare a C symbol with contextual extern before fn:
extern fn strlen(ptr: Ptr) -> U64;A public external function can form part of a generated C-compatible library surface:
pub extern fn khora_add(left: Int, right: Int) -> Int;See FFI for ABI rules and supported boundary types.
Effect declarations
Section titled “Effect declarations”pub effect Store { load: Id -> User raises StoreError, save: User -> () raises StoreError,}General form:
pub? effect Name<TypeParams>? { operation: FunctionType, ...}Effect members are named function types separated by commas.
Context declarations
Section titled “Context declarations”pub context Production { scope: Scope::root(), db: postgres_db()!, store: sql_store(),}General form:
pub? context Name { label: Expr, ...}Bindings are evaluated in order; a later binding may use capabilities introduced above it.
Trait declarations
Section titled “Trait declarations”pub trait Named { fn name(self) -> String;}With supertraits and an associated type:
pub trait Iterator: Show { type Item; fn next(self) -> Step<Self, Self::Item>;}Trait functions may be signatures or have default bodies.
Implementations
Section titled “Implementations”Trait implementation:
impl Named for User { fn name(self) -> String { self.name }}Inherent implementation:
impl User { pub fn display_name(self) -> String { self.name }}Generic implementations may put type parameters after impl:
impl<A: Show> Show for Box<A> { fn show(self) -> String { self.value.show() }}Associated type definitions
Section titled “Associated type definitions”Inside a trait:
type Item;type Item: Show;Inside an implementation:
type Item = User;test "addition works" { assert(add(20, 22) == 42);}General form:
test "name" BlockBenchmarks
Section titled “Benchmarks”bench "parse payload" { parse_payload(fixture)!;}General form:
bench "name" Blocktest, bench, context, extern, and derive are contextual keywords in their declaration positions rather than globally reserved identifiers.
Khora 0.1.0built frombd964738afb6on 2026-10-17 20:41ZVersioningSecurityContributingReleasesSource