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; emptypytype,traceback, no cause, andoriginset to the call site (section 5.7).error.wrap(cause error, msg str) error— a new error with the message and the given cause;originis 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
}
}
}
Error fields are specified in section 5.7.
math
math.abs(int) intor(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 ofnuminbase.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.e—floatconstants.
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
}
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
}
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— deadlinesecsfrom now, clamped so a child deadline never exceeds its parent’s; negativesecsis treated as 0; non-finite or unrepresentably largesecsfaults.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?—nonewhile 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
}
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?—nonewhen the two values are structurally equal (the comparison of section 11.2’scontains); 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?—nonewhen given an error; an error when givennone. 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
}
}
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, invokingfper 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
}
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
Responsewithstatus404 and anoneerror. Only transport-level failures (connection refused, timeout, invalid request) are error values, with the zeroResponsein the value slot. - Redirects are followed automatically.
- For
http.request, an empty body on a GET request sends no body. http.streamPOSTsbodyand invokesfonce per response line as lines arrive, before the response completes (server-sent events are consumed this way). The returnedResponse.bodyholds 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:trueand hold the card if it was free,falseif 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--lowsemantics).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_FILEif set, else/etc/gputex/env;KEY=VALUElines) 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_DIRrelocates the state directory (tests, sandboxes) andGPUTEX_ENV_FILErelocates the managed environment file. - On non-unix builds (the playground) every
gpufunction 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
}