GitHub

Standard Library

The stdlib is the std package, rooted at nxlib/stdlib/. Each module imports under a std:-prefixed path; here is one example.

import { Console }, * as stdio from "std:stdio"

A bare path (no colon) is still a relative file import. Each std:-prefixed name resolves to a file in nxlib/stdlib/. The WIT interface name comes from the same name, with _ rewritten to -. For instance, the str module maps to the string-ops WIT interface under the same WIT prefix.

I/O Caps

I/O is gated by caps. Each cap has a system_handler that declares require { PermX }. The handler hands the perm up to the caller on inject. A mock handler with no require clause needs no runtime perm.

Console (std:stdio)

Needs PermConsole.

cap Console do

  fn print(val: string) -> unit

  fn println(val: string) -> unit

  fn eprint(val: string) -> unit

  fn eprintln(val: string) -> unit

  fn read_line() -> string

  fn getchar() -> string

  fn read_bytes(n: i64) -> %ByteBuffer

end
let main = fn () -> unit require { PermConsole } do

  inject stdio.system_handler do

    Console.println(val: "Hello")

  end

  return ()

end

File System (std:fs)

Needs PermFs.

Exception types: FileNotFound(path), WriteError(path), RemoveError(path). Exception group: FsThrow = FileNotFound | WriteError | RemoveError.

Value types:

type Handle      = Handle(id: i64)                                   // linear file handle

type ReadResult  = ReadResult(content: string, handle: %Handle)

type WriteResult = WriteResult(ok: bool, handle: %Handle)

type PathResult  = PathResult(path: string, handle: %Handle)

Direct-call helpers (the two operations exported as plain functions; the rest live on the Fs cap):

fn list_dir(path: string) -> [ string ] throws { FileNotFound }

fn is_file(path: string) -> bool

Cap methods (every other operation; reached via inject):

cap Fs do

  // Query

  fn exists(path: string) -> bool

  fn is_file(path: string) -> bool

  fn list_dir(path: string) -> [ string ] throws { FileNotFound }

  fn read_to_string(path: string) -> string throws { FileNotFound }



  // Mutating

  fn write_string(path: string, content: string) -> unit throws { WriteError }

  fn append_string(path: string, content: string) -> unit throws { WriteError }

  fn remove_file(path: string) -> unit throws { RemoveError }

  fn create_dir_all(path: string) -> unit throws { WriteError }

  fn read_dir(path: string) -> %[ Handle ] throws { FileNotFound }



  // File-descriptor operations (consume-and-return pattern)

  fn open_read(path: string) -> %Handle throws { FileNotFound }

  fn open_write(path: string) -> %Handle throws { WriteError }

  fn open_append(path: string) -> %Handle throws { WriteError }

  fn read(handle: %Handle) -> ReadResult

  fn write(handle: %Handle, content: string) -> WriteResult

  fn handle_path(handle: %Handle) -> PathResult

  fn close(handle: %Handle) -> unit

end

The fd operations use a consume-and-return shape. The linear handle is consumed; a fresh one comes back in the result record. So a handler can stay stateless.

let %h = Fs.open_read(path: "data.txt")

let ReadResult(content: c, handle: %h2) = Fs.read(handle: %h)

Fs.close(handle: %h2)

Network (std:network)

Needs PermNet.

Exception types: RequestError(url), BindError(addr), ResponseError(msg). Exception group: NetError = RequestError | BindError | ResponseError.

Value types:

type Header = Header(name: string, value: string)

type Response = Response(status: i64, headers: string, body: string)

opaque type Server = Server(id: i64)               // linear server handle

opaque type Request = Request(...)                 // linear request handle

opaque type RespondStream = RespondStream(...)     // linear streaming response handle

Helper:

fn header(name: string, value: string) -> Header

Cap methods: every HTTP client and server op goes through the Net cap. No direct-call API is exported, so code must inject a handler.

cap Net do

  // HTTP client

  fn get(url: string) -> string throws { RequestError }

  fn request(method: string, url: string, headers: [ Header ], body: string)

    -> Response throws { RequestError }

  fn request_with_timeout(method: string, url: string, headers: [ Header ], body: string,

                          timeout_ms: i64) -> Response throws { RequestError }



  // HTTP server

  fn listen(addr: string) -> %Server throws { BindError }

  fn accept(server: &Server) -> %Request

  fn cancel_accept(server: &Server) -> bool

  fn respond(req: %Request, status: i64, body: string) -> unit throws { ResponseError }

  fn respond_with_headers(req: %Request, status: i64, headers: [ Header ], body: string)

    -> unit throws { ResponseError }



  // Streaming response

  fn respond_streaming_start(req: %Request, status: i64, headers: [ Header ])

    -> %RespondStream throws { ResponseError }

  fn respond_streaming_write(stream: &RespondStream, chunk: string) -> bool

  fn respond_streaming_finish(stream: %RespondStream) -> unit throws { ResponseError }



  fn stop(server: %Server) -> unit

end

Random (std:rand)

Needs PermRandom.

cap Random do

  fn next_i64() -> i64

  fn range(min: i64, max: i64) -> i64

  fn next_bool() -> bool

end

Clock (std:clock)

Needs PermClock.

cap Clock do

  fn sleep(ms: i64) -> unit

  fn now() -> i64

end

Process (std:proc)

Needs PermProc.

Types:

type ExecResult = ExecResult(exit_code: i64, stdout: string, stderr: string)

Direct-call API:

fn argv() -> [ string ] require { PermProc }

Cap methods:

cap Proc do

  fn exit(status: i64) -> unit

  fn argv() -> [ string ]

  fn exec(cmd: string, args: [ string ]) -> ExecResult

end

Environment (std:env)

Needs PermEnv.

cap Env do

  fn get(key: string) -> Option<string>

  fn set(key: string, value: string) -> unit

end

Env.get returns None when the variable is unset. The shape skips exceptions for the absent case.

Data Structures

Option (std:option)

type Option<T> = Some(val: T) | None



fn is_some<T>(opt: Option<T>) -> bool

fn is_none<T>(opt: Option<T>) -> bool

fn unwrap_or<T>(opt: Option<T>, default: T) -> T

fn map<T, U>(opt: Option<T>, f: (val: T) -> U) -> Option<U>

fn and_then<T, U>(opt: Option<T>, f: (val: T) -> Option<U>) -> Option<U>

fn or_else<T>(opt: Option<T>, other: Option<T>) -> Option<T>

fn unwrap<T>(opt: Option<T>) -> T throws { Exn }

fn expect<T>(opt: Option<T>, msg: string) -> T throws { Exn }

List (std:list)

An immutable cons list, with two ctors named Nil and Cons. The Cons ctor takes a head v and a tail rest. The form [ T ] is an alias for List<T>, with literal sugar.

type Partition<T> = Partition(matched: [ T ], rest: [ T ])



fn empty<T>() -> [ T ]

fn cons<T>(x: T, xs: [ T ]) -> [ T ]

fn is_empty<T>(xs: [ T ]) -> bool

fn length<T>(xs: [ T ]) -> i64

fn head<T>(xs: [ T ]) -> T

fn tail<T>(xs: [ T ]) -> [ T ]

fn last<T>(xs: [ T ]) -> T

fn reverse<T>(xs: [ T ]) -> [ T ]

fn concat<T>(xs: [ T ], ys: [ T ]) -> [ T ]

fn take<T>(xs: [ T ], n: i64) -> [ T ]

fn drop_n<T>(xs: [ T ], n: i64) -> [ T ]

fn nth<T>(xs: [ T ], n: i64) -> T

fn contains(xs: [ i64 ], val: i64) -> bool

fn fold_left<T, U>(xs: [ T ], init: U, f: (acc: U, val: T) -> U) -> U

fn map<T, U>(xs: [ T ], f: (val: T) -> U) -> [ U ]

fn map_rev<T, U>(xs: [ T ], f: (val: T) -> U) -> [ U ]

Tuple (std:tuple)

type Pair<A, B> = Pair(left: A, right: B)



fn fst<A, B>(p: Pair<A, B>) -> A

fn snd<A, B>(p: Pair<A, B>) -> B

Arrays use the built-in linear type [| T |]. Building and indexing are language primitives, written as [| e1, e2 |] and arr[i]. The stdlib ships no separate array module.

HashMap (std:hashmap)

Open-addressed hash map (linear probing) from i64 keys to i64 values. The impl rides on the runtime collection module. The map is an opaque linear handle and must be freed.

opaque type HashMap = HashMap(id: i64)  // linear -- must be freed

type Lookup = Found(value: i64) | Missing



fn empty() -> %HashMap

fn put(map: %HashMap, key: i64, value: i64) -> %HashMap

fn get(map: &HashMap, key: i64) -> Lookup

fn get_or(map: &HashMap, key: i64, default: i64) -> i64

fn contains_key(map: &HashMap, key: i64) -> bool

fn remove(map: %HashMap, key: i64) -> %HashMap

fn size(map: &HashMap) -> i64

fn keys(map: &HashMap) -> [ i64 ]

fn values(map: &HashMap) -> [ i64 ]

fn free(map: %HashMap) -> unit

StringMap (std:stringmap)

Open-addressed hash map (linear probing) from string keys to i64 values. The impl rides on the runtime collection module. The map is an opaque linear handle and must be freed.

opaque type StringMap = StringMap(id: i64)  // linear -- must be freed

type Lookup = Found(value: i64) | Missing



fn empty() -> %StringMap

fn put(map: %StringMap, key: string, value: i64) -> %StringMap

fn get(map: &StringMap, key: string) -> Lookup

fn get_or(map: &StringMap, key: string, default: i64) -> i64

fn contains_key(map: &StringMap, key: string) -> bool

fn remove(map: %StringMap, key: string) -> %StringMap

fn size(map: &StringMap) -> i64

fn keys(map: &StringMap) -> [ string ]

fn values(map: &StringMap) -> [ i64 ]

fn free(map: %StringMap) -> unit

ByteBuffer (std:bytebuffer)

Mutable byte buffer for building binary data. The impl uses a bump-arena header in linear memory. Handles are opaque and linear. The API offers LEB128 encoding, little-endian int writes, and raw byte, string, and buffer appends.

opaque type ByteBuffer = ByteBuffer(id: i64)  // linear -- must be freed



fn empty() -> %ByteBuffer

fn push_byte(buf: %ByteBuffer, byte: i64) -> %ByteBuffer

fn push_i32_le(buf: %ByteBuffer, val: i64) -> %ByteBuffer

fn push_i64_le(buf: %ByteBuffer, val: i64) -> %ByteBuffer

fn push_uleb128(buf: %ByteBuffer, val: i64) -> %ByteBuffer

fn push_sleb128(buf: %ByteBuffer, val: i64) -> %ByteBuffer

fn push_string(buf: %ByteBuffer, s: string) -> %ByteBuffer

fn push_buf(dst: %ByteBuffer, src: &ByteBuffer) -> %ByteBuffer

fn length(buf: &ByteBuffer) -> i64

fn get_byte(buf: &ByteBuffer, idx: i64) -> i64

fn to_string(buf: &ByteBuffer) -> string

fn write_file(buf: &ByteBuffer, path: string) -> bool require { PermFs }

fn free(buf: %ByteBuffer) -> unit

Every mutating op consumes the buffer and returns a new handle. The shape is consume-and-return.

Utilities

String (std:str)

// Inspection



// Transformation



// Conversion



fn length(s: string) -> i64

fn contains(s: string, sub: string) -> bool

fn index_of(s: string, sub: string) -> i64

fn starts_with(s: string, prefix: string) -> bool

fn ends_with(s: string, suffix: string) -> bool

fn char_at(s: string, idx: i64) -> char

fn char_code(s: string, idx: i64) -> i64              // Unicode codepoint, -1 if OOB

fn substring(s: string, start: i64, len: i64) -> string

fn trim(s: string) -> string

fn to_upper(s: string) -> string

fn to_lower(s: string) -> string

fn replace(s: string, from_str: string, to_str: string) -> string

fn concat(a: string, b: string) -> string

fn repeat(s: string, n: i64) -> string

fn pad_left(s: string, width: i64, fill: string) -> string

fn pad_right(s: string, width: i64, fill: string) -> string

fn join(xs: [ string ], sep: string) -> string

fn split(s: string, sep: string) -> [ string ]

fn from_i64(val: i64) -> string

fn from_float(val: float) -> string

fn from_bool(val: bool) -> string

fn from_char(c: char) -> string

fn from_char_code(code: i64) -> string                // Unicode codepoint → string

fn parse_i64(s: string) -> Option<i64>

fn parse_f64(s: string) -> Option<f64>

fn to_f64(s: string) -> f64 throws { Exn }

Math (std:math)

fn abs(val: i64) -> i64

fn max(a: i64, b: i64) -> i64

fn min(a: i64, b: i64) -> i64

fn mod_i64(a: i64, b: i64) -> i64

fn abs_float(val: float) -> float

fn sqrt(val: float) -> float

fn floor(val: float) -> float

fn ceil(val: float) -> float

fn pow(base: float, exp: float) -> float

fn i64_to_float(val: i64) -> float

fn float_to_i64(val: float) -> i64

fn negate(val: bool) -> bool

Result (std:result)

type Result<T, E> = Ok(val: T) | Err(err: E)



fn is_ok<T, E>(res: Result<T, E>) -> bool

fn is_err<T, E>(res: Result<T, E>) -> bool

fn unwrap_or<T, E>(res: Result<T, E>, default: T) -> T

fn map<T, U, E>(res: Result<T, E>, f: (val: T) -> U) -> Result<U, E>

fn map_err<T, E, F>(res: Result<T, E>, f: (val: E) -> F) -> Result<T, F>

fn and_then<T, U, E>(res: Result<T, E>, f: (val: T) -> Result<U, E>) -> Result<U, E>

fn from_exn<T>(exn: Exn) -> Result<T, Exn>

fn to_exn<T>(res: Result<T, Exn>) -> T throws { Exn }

Exception Utilities (std:exn)

fn to_string(exn: Exn) -> string

fn backtrace(exn: Exn) -> [string]

backtrace returns call-stack frames (with source file and line info) captured at the throw point.

Char (std:char)

ASCII character class tests:

fn ord(c: char) -> i64

fn is_upper(c: char) -> bool

fn is_lower(c: char) -> bool

fn is_alpha(c: char) -> bool

fn is_digit(c: char) -> bool

fn is_alnum(c: char) -> bool

fn is_hex_digit(c: char) -> bool

fn is_whitespace(c: char) -> bool

fn is_ident_start(c: char) -> bool

fn is_ident_char(c: char) -> bool

fn is_newline(c: char) -> bool

fn digit_value(c: char) -> i64

fn hex_digit_value(c: char) -> i64

Lazy (std:lazy)

Combinators for @ thunk forcing. The code is pure Nexus, layered over the lazy_spawn and lazy_join dispatch primitives in the runtime lazy module.

fn race(a: i64, b: i64) -> i64

fn cancel(thunk: i64) -> unit

fn detach(thunk: i64) -> unit

fn force_all(tasks: [i64]) -> [i64]
Function Description
race(a, b) Force two thunks in parallel, return the first to complete; loser discarded
cancel(thunk) Consume a thunk without evaluating (satisfies linearity)
detach(thunk) Fire-and-forget: start evaluation, don’t wait for result
force_all(tasks) Spawn all thunks in parallel, join results in order

Note: the underlying functions use i64 inside, since every value is i64 at the WASM level. The typechecker pins @T use-once at the call site.

Core (std:core)

Legacy re-exports for back-compat. Use tuple.nx, list.nx, and math.nx for new code.

type Pair<A, B> = Pair(left: A, right: B)

type Partition<T> = Partition(matched: [ T ], rest: [ T ])



fn fst<A, B>(p: Pair<A, B>) -> A

fn snd<A, B>(p: Pair<A, B>) -> B

fn negate(val: bool) -> bool

fn id<T>(val: T) -> T