Functions
Declaring functions, returns, methods, generics, and function values.
Functions are declared with finna (as in “finna do a thing”) and hand results back with bet.
Declaring a function
finna add(a: int, b: int) -> int { // finna = fn, bet = return
bet a + b
}
The return arrow and type are omitted when a function returns nothing (void).
Returns and multi-value returns
bet returns zero or more values: bet, bet x, or bet a, b. A function that returns several
values declares a tuple return type, destructured at the call site.
finna divmod(a: int, b: int) -> (int, int) {
bet a / b, a % b
}
lowkey q, r = divmod(17, 5) // destructure
Errors ride along as the last value of a multi-value return ((T, yikes)); see
Features.
Methods (Go-style receivers)
A function with a receiver attaches behavior to a drip (struct):
drip Counter { flex n: int }
finna (c: Counter) bump(by: int) -> int {
bet c.n + by
}
finna main() {
lowkey c = Counter{ n: 10 }
spill.it(c.bump(5)) // 15
}
Generics
Type parameters go in [...] on functions and structs, resolved by ahead-of-time monomorphization:
each instantiation compiles to its own concrete code, with no runtime type info.
finna pickFirst[T](a: T, b: T) -> T { bet a }
spill.it(pickFirst[int](7, 9)) // 7
spill.it(pickFirst[str]("a", "b")) // a
Function values
Functions are first-class citizens here: pass one by name, stash it in a variable, hang it off a
struct field. The type is spelled with finna. Function-pointer fields are the whole dispatch story
in v1, so no interfaces or traits yet, but you can get surprisingly far with a struct full of
finnas.
finna dub(x: int) -> int { bet x * 2 }
finna apply(f: finna(int) -> int, x: int) -> int {
bet f(x)
}
lowkey g: finna(int) -> int = dub
spill.it(apply(g, 21)) // 42