Chelis has two syntaxes for one language. Surf is what people read and
write: functions, named dimensions, pipes, algebraic data types, and
typed tensor programs. Deep is the canonical representation that tools
can inspect after Surf has done its job.
01 Functions are the everyday unit
Surf is the human-facing syntax. A function declares the tensor shape it returns, so callers and compiler passes agree on the shape contract.
module HelloTensordef main() -> tensor[n, f32] = { a = to_tensor([1.0, 2.0, 3.0]) b = to_tensor([4.0, 5.0, 6.0]) out = add(a, b) out}
02 Named dimensions travel through transforms
Dimensions are not comments. process works on a feature vector, and batch_process lifts it over the batch axis while preserving the feature axis in the type.
Surf supports algebraic data types and pattern matching. The compiler sees both variants of Activation, and match arms make the control flow visible in the source.
SurfSource slice: basics/pipeandmatch.ch
type Activation = | Relu | Sigmoiddef activate(act: Activation, x: &tensor[n, f32]) -> tensor[n, f32] = { match act with { | Relu => relu(x) | Sigmoid => sigmoid(x) }}
05 The type system rejects shape drift
Tensor widths, axes, and scalar element types are part of signatures and intermediate expressions. A wrong matmul width or bias shape is a type error instead of a backend crash.
After Surf is parsed, Chelis can lower to Deep: a stable s-expression tree for compiler tooling, shell integration, tests, and round-tripping back to Surf.