Draft / Experimental: Zith documentation describes a language under active development. Check Implementation Status before relying on a feature.
Implementation status:
fn,flow fn,raw fn, andextern fnare working. Overloading (§5.4) is working.const fnis a parse error —constis a binding keyword;const fn f()is parsed as aconstbinding namedfn. Concurrency is no longer a core function kind; any runtime async/task model is expressed through ordinary library types and calls. See impl-status.md.
fn add(a: i32, b: i32): i32 { a + b } // explicit type, implicit return
fn add(a: i32, b: i32) { a + b } // inferred type
// Bounds-checked indexing: returns the element if in range, otherwise
// propagates null via the implicit optional from '?'.
fn first<T>(slice: []T): ?T {
slice[0]?
}
The compiler cannot infer a
unionordynreturn type without an explicit type hint.
| Kind | Description |
|---|---|
fn |
Standard runtime function. |
const fn |
Resolved entirely at compile time. |
flow fn |
Enables marker/dock control flow (§9.4). |
raw fn |
Always unchecked, bypassing safety in both debug and release. The compiler warns in release builds if raw could be removed. |
Function kinds are orthogonal and cannot be combined on a single declaration.
Macro calls use the @ prefix — @println, @log, @serialize — while ordinary function calls use a bare name, such as console.write, process, or save. See §15 for the full rule.
// The compiler treats runtime task types like any other library type.
// There is no `async fn`, `yield`, `spawn`, or `await` syntax in the core language.
fn fetch(url: string): Task<Response!> {
return runtime.schedule(url);
}
Concurrency is modeled by stdlib or runtime APIs, not by dedicated syntax or a special function kind. A library may expose Task<T>, Generator<T>, channels, executors, or thread handles, but the compiler only sees ordinary declarations, calls, traits/capabilities, and the NRA facts needed to validate resource usage around them.
Several functions in one scope may share a name as long as their parameter lists differ, either in count or in parameter types. The call site selects the declaration whose parameters accept the given arguments.
fn add(a: i32, b: i32): i32 { a + b }
fn add(a: f64, b: f64): f64 { a + b }
fn add(a: i32, b: i32, c: i32): i32 { a + b + c }
fn main(): i32 {
let i: i32 = add(1, 2); // add(i32,i32)
let f: f64 = add(1.0, 2.0); // add(f64,f64)
return add(i, 3, 4); // add(i32,i32,i32)
}
Methods overload the same way; the implicit self parameter participates in the signature.
implement Point {
fn shifted(self): Point { ... }
fn shifted(self, by: i32): Point { ... }
}
Rules:
E2002), evenwhen their return types differ. The return type is never part of overload selection.
fn f(p: lend P) and fn f(p: view P) are thesame signature, and therefore E2002.
E2002).extern fn cannot be overloaded: it carries a fixed C linkage name.E2007; a call accepted by more than onecandidate is E2008. There is no ranking of conversion quality, so any tie is an error rather than a silent choice.
wins and shadows outer declarations entirely.
Overloading is implemented by qualifying linkage names as <module>.<Owner>.<name>(<params>), for example std.io.console.println(*char). extern fn declarations and main keep their plain source name so that C interop and the linker's entry point are unaffected.
Zith Language Specification — Draft v0.9