A tour of the language
Types, pattern matching, closures, generics — the whole surface in one read.
Start
A first program, run three ways, in about two minutes.
Put this in hello.gr.
fn main() {
print("hello from grasm")
}
main needs no effect row: it is the entry point, and the
capability to print is granted there. Every other function that prints must say
so.
grasm run hello.gr
That is the interpreter, and it is normative: where the engines could ever disagree, it is the one that is right by definition.
grasm build hello.gr --native -o hello
./hello
Ahead-of-time through Cranelift. Byte-for-byte the same stdout, the same exit
code. Drop --native and you get a bundle instead — the interpreter
with your program attached, one file, no toolchain needed to run it.
Failure is a value, not an exception. A function that can fail says so in its type, and a caller that ignores it does not compile.
type MathError = | DivByZero
fn safe_div(a: i64, b: i64) -> i64 ! MathError {
if b == 0 {
fail(DivByZero)
}
a / b
}
fn main() {
match safe_div(10, 0) {
Ok(v) -> print("{v}")
Err(e) -> print("caught: {e}")
}
// or take a default and move on
print("{safe_div(10, 0) ?? 0 - 1}")
}
test "division works" {
let v = try safe_div(10, 2)
assert_eq(v, 5)
}
Tests live beside the code and run with grasm test.
Types, pattern matching, closures, generics — the whole surface in one read.
The part that is unlike other languages, and the reason to use this one.