Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Standard library

Generated from the language spec, which is normative.

Standard library modules are imported by bare name: import "math". The module name then acts as a namespace: math.sqrt(2.0), math.pi. The v1 modules are math, error, file, ctx, and http.

Where a stdlib signature below ends in error? or (T, error?), failures are ordinary error values subject to chapter 10; stdlib functions do not fault on I/O failure.

error

The constructors error.new and error.wrap require no import; they are part of the core language. import "error" remains legal and adds nothing.

  • error.new(msg str) error — a new error with the message; empty pytype, traceback, no cause, and origin set to the call site (section 5.7).
  • error.wrap(cause error, msg str) error — a new error with the message and the given cause; origin is the wrap site.
fn fetch() (error?) {
    return error.new("connection refused")
}

fn main() {
    err := fetch()
    if err != none {
        wrapped := error.wrap(err, "startup failed")
        print(wrapped.msg)          // startup failed
        cause := wrapped.cause
        if cause != none {
            print(cause.msg)        // connection refused
        }
    }
}

▸ run it in the playground

Error fields are specified in section 5.7.

math

  • math.abs(int) int or (float) float — absolute value, polymorphic over the two numeric types; abs(-9223372036854775808) faults with integer overflow.
  • math.min(int, int) int / (float, float) float, math.max(...) likewise — both arguments the same numeric type.
  • math.sqrt(float) float — square root.
  • math.cos(float) float, math.sin(float) float, math.tan(float) float — trigonometry, radians.
  • math.pow(base float, pow float) float — exponentiation.
  • math.exp(pow float) float — e raised to the argument.
  • math.ln(num float) float — natural logarithm.
  • math.log(base float, num float) float — logarithm of num in base.
  • math.floor(float) int — round down. math.ceil(float) int — round up. math.round(float) int — round half away from zero (not banker’s rounding).
  • math.pi, math.efloat constants.
import "math"

fn main() {
    print(math.max(2, 40) + math.abs(-2))     // 42
    print(math.pow(2.0, 10.0))                // 1024
    printf("%.4f\n", math.log(2.0, 1024.0))   // 10.0000
    print(math.round(2.5))                    // 3
    printf("%.5f\n", math.pi)                 // 3.14159
}

▸ run it in the playground

file

Paths are str. Contents are UTF-8 str; there is no bytes type in v1.

  • file.read(path str) (str, error?) — whole-file read; the value slot is "" on error.
  • file.write(path str, s str) error? — create or truncate, then write.
  • file.append(path str, s str) error? — create if missing, append.
  • file.exists(path str) bool — existence test; never errors.
  • file.list(dir str) ([]str, error?) — entry names, sorted lexicographically.
  • file.remove(path str) error? — remove a file or an empty directory.
  • file.mkdir(path str) error? — create the directory and any missing parents.
import "file"

fn main() (error?) {
    path := "/tmp/nevla-book-example.txt"
    check file.write(path, "one\n")
    check file.append(path, "two\n")
    body := check file.read(path)
    print(body.lines())              // [one, two]
    print(file.exists(path))         // true
    check file.remove(path)
    return none
}

▸ run it in the playground

ctx

Importing "ctx" also brings the opaque struct type Ctx into scope. A Ctx is a cancellation handle: a deadline plus an interrupt flag. Ctx values are handles with reference semantics (section 11.1) and cannot be constructed with a struct literal (section 7.2.3).

  • ctx.background() Ctx — never done.
  • ctx.timeout(parent Ctx, secs float) Ctx — deadline secs from now, clamped so a child deadline never exceeds its parent’s; negative secs is treated as 0; non-finite or unrepresentably large secs faults.
  • ctx.interrupt(parent Ctx) Ctx — additionally becomes done when the process receives SIGINT.

Methods on Ctx:

  • done() bool — whether the deadline has passed or the interrupt fired.
  • err() error?none while live; "deadline exceeded" or "interrupted" when done.
import "ctx"

fn main() {
    c := ctx.timeout(ctx.background(), 0.0)   // already expired
    print(c.done())                           // true
    e := c.err()
    if e != none {
        print(e.msg)                          // deadline exceeded
    }
    print(ctx.background().done())            // false
}

▸ run it in the playground

test

Importing "test" provides the helpers nevla test is built around (section 17.7); each returns error? so it composes with check, and each failure carries an origin (section 5.7).

  • test.eq(got, want) error?none when the two values are structurally equal (the comparison of section 11.2’s contains); otherwise an error naming both sides. Comparing values deeper than the implementation limit faults.
  • test.neq(got, unwanted) error? — the negation.
  • test.err(e error?) error?none when given an error; an error when given none. Asserts that something failed.
  • test.skip(reason str) error? — an error the test runner reports as skipped rather than failed.
import "test"

fn main() {
    print(test.eq([1, 2], [1, 2]) == none)   // true: structural
    bad := test.eq(2, 3)
    if bad != none {
        print(bad.msg)                        // expected 3, got 2
    }
}

▸ run it in the playground

http

Importing "http" also declares two struct types:

struct Request  { method str, url str, body str, headers map[str]str }
struct Response { status int, body str, headers map[str]str }
  • http.get(c Ctx, url str) (Response, error?) — GET.
  • http.post(c Ctx, url str, body str) (Response, error?) — POST with the given body.
  • http.request(c Ctx, req Request) (Response, error?) — any method, with headers.
  • http.stream(c Ctx, url str, body str, f fn(str)) (Response, error?) — POST, invoking f per response line as it arrives.
import "ctx"
import "http"

fn main() (error?) {
    c := ctx.timeout(ctx.background(), 5.0)
    resp, err := http.get(c, "http://localhost:9/unreachable")
    if err != none {
        print("transport error, as expected here")
    } else {
        print(resp.status)
    }
    return none
}

▸ run it in the playground

Behavior:

  • If the ctx is already done, the call returns an error before any network I/O.
  • A live ctx deadline bounds the whole request; without a deadline, an implementation-defined default timeout applies (30 seconds in the reference implementation).
  • A completed HTTP exchange is a success regardless of status code: a 404 is a Response with status 404 and a none error. Only transport-level failures (connection refused, timeout, invalid request) are error values, with the zero Response in the value slot.
  • Redirects are followed automatically.
  • For http.request, an empty body on a GET request sends no body.
  • http.stream POSTs body and invokes f once per response line as lines arrive, before the response completes (server-sent events are consumed this way). The returned Response.body holds the accumulated lines, newline terminated, so the program can reparse the full payload afterward; closures historically could not accumulate it themselves (pre-ADR-0010 closures captured by value); kept for compatibility. Its default deadline, absent a ctx deadline, is 300 seconds rather than 30.
  • Response header names are as received; values that are not valid strings read as "".

gpu

GPU sharing. The module speaks the gputex lock protocol (an advisory flock plus a holder registry under $GPUTEX_DIR, default ~/.gputex; the contract is documented in the gputex repository), so a nevla program coordinates with every other job on the host — wrapped in the gputex CLI or not — without an external wrapper.

Every function takes the card id first ("default" on single-card hosts; multi-card hosts name their cards, e.g. "cuda0" — the host’s convention is whatever gputex status lists); label names the job for status displays. A card id that is empty or contains a path separator is an error value (“bad card id”): ids become file names in the shared state directory.

  • gpu.lock(card str, label str) error? — take the card exclusively, blocking in the kernel until it is free. Errors if this program already holds that card.
  • gpu.trylock(card str, label str) (bool, error?) — non-blocking probe: true and hold the card if it was free, false if it is busy (including when held by this program). Busy is data, not an error; the error slot is for real failures (an unwritable state directory).
  • gpu.shared(card str, label str) error? — take the card as a shared, lowest-priority holder: many coexist, all yield to an exclusive acquirer, which may terminate them (the gputex --low semantics).
  • gpu.unlock(card str) error? — release. Errors if that card is not held.

Behavior:

  • A program may hold several cards at once (training on one while embedding on another), one hold per card; a second acquire of a held card is an error.
  • A hold lasts until gpu.unlock(card) or process exit — any exit. The kernel releases the flock when the process dies, so a fault, kill, or crash never strands a card.
  • Acquiring also injects the managed environment ($GPUTEX_ENV_FILE if set, else /etc/gputex/env; KEY=VALUE lines) into the process environment, existing values winning: taking the card and getting the metrics contract (MLFLOW_TRACKING_URI) are one step, as with the CLI.
  • Two environment variables configure the module, mirroring gputex: GPUTEX_DIR relocates the state directory (tests, sandboxes) and GPUTEX_ENV_FILE relocates the managed environment file.
  • On non-unix builds (the playground) every gpu function faults (“gpu.lock is not available in this build”).
import "gpu"

fn main() (error?) {
    check gpu.lock("default", "tinyllama eval")
    // the card is ours until unlock or exit
    check gpu.unlock("default")

    ok, err := gpu.trylock("default", "opportunistic sweep")
    if err != none {
        return err
    }
    if !ok {
        print("card busy; skipping")
        return none
    }
    check gpu.unlock("default")
    return none
}

▸ run it in the playground