DEV Community

flisk
flisk

Posted on

Goose Lang 101: One Array, One Slice, One Function

Part 3 of Goose lang and Flisk.sh 101. Stay in ~/src/goose-flisk-101. Run every file with goose file.goose before you build it.

Goose has no heap allocator and no garbage collector. Dynamic values live on data stacks the compiler assigns. Growth bumps a pointer. Scope exit moves that pointer back. You do not write the stack. You do choose the array spelling, because that spelling is the contract.

The array you want in week one

T[>..] is grow-only. References into it stay valid across push. That is the difference from Vec<T>.

total.goose:

fn total(xs: i64[:]) -> i64 {
    var t = 0;
    for x in xs { t += x; }
    return t;
}

fn main() {
    var grow: i64[>..] = [];
    for i in 5 { grow.push(i * i); }
    print(grow);
    print(total(grow));
    print(total(grow[1..4]));
}
Enter fullscreen mode Exit fullscreen mode
goose total.goose
Enter fullscreen mode Exit fullscreen mode

i64[:] is a slice: a reference plus a count. total can take the whole array or grow[1..4] without copying elements. ^k counts from the end, as in grow[^2..].

let cannot be reassigned. var can. Types usually infer. fn is a free function. xs.total() would mean total(xs) if you prefer the call shape. There is no impl block.

Strings are byte arrays

There is no string type.

Spelling Use
u8[>..] builder
u8[] finished text, frozen after construction
u8[..16] small text inside a struct
u8[:] a view, including string literals
fn main() {
    var s: u8[>..] = [];
    s.append("hello");
    s.push(',');
    format(s, " world! ", 6 * 7);
    print(s, " [", s.len, " bytes]");
}
Enter fullscreen mode Exit fullscreen mode

format appends. str(...) builds a new string straight into its destination. Slicing does not copy: trim and find in the standard library hand back views.

Two results, not a tuple

There is no tuple type. A trailing bool is the usual "did this work" signal.

fn divide(a: i64, b: i64) -> i64, bool {
    if b == 0 { return 0, false; }
    return a / b, true;
}

fn main() {
    let q, ok = divide(7, 2);
    print(q, " ", ok);
}
Enter fullscreen mode Exit fullscreen mode

Three rules that bite on day one

  • Arithmetic uses the operands' width. u8 + u8 is an 8-bit add. A narrow type widens implicitly. Narrowing needs as or as!. u32 + i32 does not compile until you pick a side.
  • % is Euclidean. The result is never negative. -7 % 3 is 2.
  • A growable value is not copied by assignment. var b = a on an i64[>..] binds a reference. var b = copy(a) is the copy, and you should see it.

if and match are expressions:

let kind = if hp > 50 { "healthy" } else { "hurt" };
Enter fullscreen mode Exit fullscreen mode

What to ignore until the official tutorial

Relative references (Node&<u32>?), reusable pools, return err from load, threads, and extern fn are real and they are not 101. The compiler is also young: no namespace privacy, no labeled break, whole-program compilation only. Read section 18 of the tutorial before you design a service around it.

Leave a binary for part 5

goose build -o ./total total.goose
./total
Enter fullscreen mode Exit fullscreen mode

./total prints and exits. It is not an HTTP server. Part 4 builds one in a language Flisk already scaffolds. Part 5 calls this binary from the host and explains why Docker will not see it.

Top comments (0)