Effects and capabilities
What a function may do, and how a package is held to it.
Learn
Everything you need to read Grasm, in order.
let binds; var binds something you may reassign.
Types are inferred and may be written.
let n = 1 + 2
let x: f64 = 2 // an integer literal becomes a float where one is wanted
var total = 0
total = total + n
| Kind | Written | Example |
|---|---|---|
| Primitives | i64 f64 bool str bytes () | let ok: bool = true |
| List | [T] | [1, 2, 3] |
| Map | Map[K, V] | { "a": 1 } |
| Tuple | (A, B) | (1, "a") |
| Range | range | 0..10, 0..=10 |
| Struct | type P = { x: i64 } | P{ x: 1 } |
| Sum | type C = | Red | Green | Red |
| Option | Option[T] | Some(1), None |
| Fallible | T ! E | i64 ! MathError |
| Function | fn(A) -> B | |n| n + 1 |
| Reference | ref T | ref(0) |
There is no null. Something that may be absent is an Option, and
the compiler makes you open it.
let label = if n > 0 { "positive" } else { "not" }
match value {
Some(v) -> print("{v}")
None -> print("nothing")
}
for i in 0..3 { }
for v in [1, 2, 3] { }
while more { }
if and match are expressions. A match
must cover its subject, and the compiler says which case is missing.
fn twice(n: i64) -> i64 {
n * 2
}
let f = |n| n + 1 // a closure captures by value, at creation
let g: fn(i64) -> i64 = twice // a function is a value
[1, 2, 3].map(twice)
3 |> twice // the same call, written the other way
type Stack[T] = { items: [T] }
fn same[T](v: T) -> T {
v
}
Generics are monomorphised: one compiled copy per member of the family. The one thing this cannot do is polymorphic recursion, and it is refused by name rather than miscompiled.
| Mode | Means |
|---|---|
fn f(x: T) | A borrow. The caller keeps it. |
fn f(own x: T) | Ownership moves in. Scalars copy, so the caller keeps those. |
fn f(inout x: T) | An exclusive mutable borrow; writes land in the caller's value. |
What a function may do, and how a package is held to it.
Four ways to own a value, chosen per value.