Skip to main content

Standard library

The standard library lives in std/ as ordinary Xi modules that wrap C runtime primitives. Import a module and call it through its namespace:

import "std/math.xi"
import "std/text.xi"
import "std/log.xi"

async entry (logger: Logger) main(args: String[]) -> Integer {
logger.info("sqrt(2) = " + math.sqrt(2.0))
logger.info(text.toUpper("hello"))
return 0
}

Import everything at once with import "std/all.xi".

Resolving imports

import "std/<mod>.xi" is resolved first relative to the importing file, then relative to $XC_STD. The installed toolchain points XC_STD at its bundled standard library automatically, so import "std/..." just works:

$ xc myapp.xi

Which module do I want?

This page is the API reference - every module and its functions. Several modules have a guide of their own that explains the design; start there if you are learning, and come back here to look up a signature.

I want to…ModuleGuide
work with lists, sets, maps, sequencescollectionsCollections
read or write JSON / YAML / XMLjson, yaml, xmlSerialization
query a data sourcequery, sqlQuery
put a repository over a data sourcedataRepository
serve HTTPwebWeb
publish and subscribe to eventseventsEvents
report health and metricsmonitoringMonitoring
run work concurrentlythreadThreading
hash, sign, encodecrypto-
read files, paths, env, processesfs, path, process-
make HTTP calls / open socketshttp, net-
numbers, text, bytes, time, conversionmath, text, bytes, time, convert-
print and read from the consoleio-
call C(extern "C")FFI

Modules

math - std/math.xi

FunctionSignature
pi, e() -> Number
abs, sqrt, exp, ln, log10(Number) -> Number
sin, cos, tan(Number) -> Number
floor, ceil, round(Number) -> Number
pow(Number, Number) -> Number
min, max(Number, Number) -> Number
clamp(Number, Number, Number) -> Number

text - std/text.xi

FunctionSignature
length(String) -> Integer
charAt(String, Integer) -> Integer (code point, -1 out of range)
substring(String, Integer, Integer) -> String
trim, toUpper, toLower(String) -> String
startsWith, endsWith, containspredicate (String, String)
indexOf(String, String) -> Integer (-1 if absent)
repeat(String, Integer) -> String
replace(String, String, String) -> String (all occurrences)
isEmptypredicate (String)
split, join(String, String) -> String[] / (String[], String) -> String
isAlpha, isDigit, isAlnum, isSpacepredicate (Integer) - classify a char code (e.g. from charAt)
indexOfChar(String, Integer) -> Integer - first index of a char code (-1 if absent)
fromCharCode(Integer) -> String - a one-character string from a char code

bytes - std/bytes.xi

Bytes is a primitive type: a raw byte buffer, distinct from String. Like String it is an immutable value (copies share the buffer; producers heap-allocate a fresh one). Used for binary I/O.

FunctionSignature
length(Bytes) -> Integer
at(Bytes, Integer) -> Integer (byte 0..255, -1 out of range)
slice(Bytes, Integer, Integer) -> Bytes ([from, to))
concat(Bytes, Bytes) -> Bytes
fromString(String) -> Bytes
toString(Bytes) -> String
empty() -> Bytes
isEmptypredicate (Bytes)

convert - std/convert.xi

FunctionSignature
toString(Number) -> String
intToString(Integer) -> String
boolToString(Bool) -> String
parseNumber(String) -> Number!
parseInteger(String) -> Integer!

parseNumber/parseInteger return a result:

let r = convert.parseInteger("42")
if isOk(r) { system.stdout.writeln("got " + r.value) }

json - std/json.xi

Xi's serialization library. Json is an opaque value tree; build it with the constructors, compose with set/push, render with stringify/pretty, and read text back with parse. See Serialization for the full guide.

FunctionSignature
nul() -> Json
of(Bool) -> Json
num / int(Number) -> Json / (Integer) -> Json
str(String) -> Json
array, object() -> Json
push(Json, Json) -> Json (append to array; returns it)
set(Json, String, Json) -> Json (set object key; returns it)
stringify, pretty(Json) -> String
parse(String) -> Json (check with isValid)
isValidpredicate (Json)
kind, length(Json) -> Integer
isNullisObjectpredicate (Json)
at(Json, Integer) -> Json (array element)
get, has, keyAtobject access
asString, asNumber, asBoolleaf coercion
getString, getNumber(Json, String) -> … (field shortcut)

yaml - std/yaml.xi

YAML over the same Json tree (block style). See Serialization.

FunctionSignature
stringify(Json) -> String
parse(String) -> Json

xml - std/xml.xi

XML over the same Json tree (object → child elements, array → repeated element, scalar → text). See Serialization.

FunctionSignature
stringify(Json) -> String (wraps in <root>)
stringifyAs(Json, String) -> String (custom root tag)
parse(String) -> Json

crypto - std/crypto.xi

Self-contained hashing, HMAC, encodings, and a CSPRNG - no external libraries. Digests are Bytes; render with hex/base64. (Test-vector verified.)

FunctionSignature
sha256 / sha1 / md5(Bytes) -> Bytes (digest)
sha256Hex / sha1Hex / md5Hex(String) -> String (hex digest of text)
hmacSha256(Bytes, Bytes) -> Bytes (key, msg)
hmacSha256Hex(String, String) -> String
hex / fromHex(Bytes) -> String / (String) -> Bytes
base64 / fromBase64(Bytes) -> String / (String) -> Bytes
randomBytes / randomHex(Integer) -> Bytes / (Integer) -> String (from /dev/urandom)

web - std/web.xi

A tiny REST framework over HTTP/1.1: implement WebRequestHandler, route by overloading handle with where guards, and run web.serve. Payloads auto-(de)serialize via a WebTransport (JSON by default). See Web.

NameKind / Signature
WebRequestHandlerinterface - action handle(req: HttpRequest, res: HttpResponse)
WebTransportinterface - serialize(Json) -> String / deserialize(String) -> Json (JSON default)
req.path / req.method / req.bodyHttpRequest -> String
req.query("q") / req.header("X")(HttpRequest, String) -> String
req.parse(T)deserialize the body into a T
res.send(dto)serialize dto and reply 200
res.sendStatus(code, msg) / res.sendText(code, body)plain-text reply
web.serve(Integer) - run a blocking HTTP/1.1 server
web.serveTLS(Integer, String, String) - HTTPS (cert, key); build with XC_TLS=1
web.serveHttp2(Integer, String, String) - HTTP/2 over TLS (ALPN h2); build with XC_HTTP2=1
web.shutdown() - stop a running server (safe from another thread)

query - std/query.xi

Query: reified query plans and the provider contract. A chain rooted at query.from<T>("source") compiles to a QueryPlan value; collect(provider) runs it. See Query.

NameKind / Signature
query.from<T>("src")root a query at a named source
list.asQuery() / arr.asQuery()root a query at an in-memory collection; run with .toList()
.using(p)bind a provider to the query so terminals run against it
.collect(p) / .collect() / .toList() / .first()run: explicit / bound / bound / first row (T?)
QueryProviderinterface - name() -> String (identity, for where selection) + run(plan) -> Json (read) + insert / remove (write)
RowStoreinterface - load(name: String, rows: Json)
MemorySourceclass - the in-memory reference provider (implements both)
QueryPlan / QueryStage / QueryExprthe plan's types - walk with match; serialize with as Json
someList.asQuery().toList()root a query at a List<T>/T[] and run it locally

sql - std/sql.xi

Render a QueryPlan to a SQL statement with bound parameters. Dialects are an interface - three are bundled; add your own without touching std.

NameKind / Signature
sqlRender(QueryPlan, SqlDialect) -> SqlStatement!
SqlStatement{ text: String, params: Json }
SqlDialectinterface - name / placeholder / quoteIdent / callSql / regexpExpr / limitSql
SqliteDialect / PostgresDialect / MysqlDialectbundled dialects (.name() -> "sqlite" / "postgres" / "mysql")

data - std/data.xi

Standard repository interfaces over any QueryProvider. Implement getProvider() and source(); findAll / findById / save / delete / deleteById and entity ↔ model conversion are overridable defaults. Reads return a composable Query bound to the repo's provider. See Repository.

NameKind / Signature
Repository<TKey, TEntity, TModel>interface - implement getProvider() / source(); default findAll / findById / convertTo / convertFrom
CrudRepository<TKey, TEntity, TModel>interface - extends Repository with default save / delete / deleteById

monitoring - std/monitoring.xi

A mechanism for monitoring, decoupled from what it watches: implement Monitoring and the registry loops over it. Off by default - enable it from main. Each subsystem is a separate import beside its own module. See Monitoring.

NameKind / Signature
Monitoringinterface - name() / startMonitor() / healthy() -> Bool / metrics() -> Json
MonitoringRegistryinterface - enable() / isEnabled() / report() -> Json / health() -> Json / healthy() -> Bool
monitor.rss() / monitor.peakRss()() -> Integer - resident / peak bytes
monitor.uptimeMs()() -> Integer - since process start
monitor.info(n)(Integer) -> String - module id (0) / name (1) / version (2)
monitor.status(name, up) / monitor.gauge(name, n)report a status / number directly
monitor.snapshot()() -> Json - uptime + reported gauges
MemoryMonitoring / CpuMonitoringstd/monitoring/memory.xi / .../cpu.xi
WebMonitoring + MonitorControllerstd/monitoring/web.xi - numbers + /monitor/* endpoints
QueryMonitoring / ThreadMonitoringstd/monitoring/query.xi / .../thread.xi

thread - std/thread.xi

Share-nothing threads + channels. A parallel block runs on a new OS thread and yields a Thread handle; threads communicate only through thread-safe channels, which carry strings or structured values. See Threading.

NameKind / Signature
thread.channel()() -> Channel (thread-safe FIFO)
ch.send(x)String as-is; a structured event/type is JSON-serialized; numbers/bools stringify
ch.recv() / ch.recv(T)raw String / deserialize a structured T (blocks)
ch.close()(Channel) - wakes a blocked recv
parallel [(caps…)] { … }spawn a thread, evaluates to a Thread (captures must be channels)
thread.stopped()() -> Bool - inside a block, has stop been requested?
t.stop() / t.wait() / t.running()request stop / join / Bool liveness

events - std/events.xi

Built-in typed publish/subscribe. A producer publishes any DTO under a topic through an injected PublisherService; a listener subscribes to a topic and receives the typed DTO - no JSON. The default MemoryBus/MemoryConsumer queue events in memory and pass the typed value through without serialization; bind your own transport to go external (serialize on publish, deserialize on receive). See Events.

NameKind / Signature
event T { … }a typed event DTO (publishable; gets a derived codec)
events.publish(topic, dto)publish any DTO under a topic (via PublisherService)
listener f(e: T) on "topic"typed subscriber for a topic
PublisherServiceinterface { producer publish(e: Event) } - outbound transport
ConsumerServiceinterface { consumer run() } - the delivery pump
MemoryBus / MemoryConsumerdefaults: in-memory queue, no serialization
Events.run()run the pump synchronously (resolve + run the ConsumerService)
Events.runAsync() / Events.stop()deliver on a worker thread (returns a Thread) / close the queue to stop it
Events.dispatch(e)deliver an envelope to its typed listeners
Events.encode(e) / Events.decode(topic, type, json)codec helpers for transports
Events.topic(e) / Events.type(e)envelope accessors

io - std/io.xi

FunctionSignature
println, print, eprintlnconsumer (String)
readLine() -> String
eofpredicate ()

config - std/config.xi

Typed configuration: bind I -> readConfig("app.yaml") makes the compiler synthesize an implementor of interface I that loads the file once and deserializes each method's value from the matching key. See Configuration.

log - std/log.xi

A leveled Logger interface with a ConsoleLogger default (DI picks it as the sole implementor). Inject Logger anywhere deps are wired - classes, functions, and entry - and bind your own implementor to redirect output.

MethodLevel / Destination
print(msg)unprefixed line → stdout
debug(msg)[debug] → stdout
info(msg)[info] → stdout
warn(msg)[warn] → stderr
error(msg)[error] → stderr
fatal(msg)[fatal] → stderr
audit(msg)[audit] → stdout (security/compliance trail)

ConsoleLogger is the default implementor; bind your own Logger (file, structured JSON, a test buffer, …) to redirect any of these.

fs - std/fs.xi

FunctionSignature
exists, isDir, isFilepredicate (String)
readFile(String) -> String! (Err if missing)
readBytes(String) -> Bytes! (Err if missing)
writeFile(String, String) -> Bool
writeBytes(String, Bytes) -> Bool
appendLine(String, String) -> Bool
size(String) -> Integer! (bytes; Err if missing)
modifiedTime(String) -> Integer! (epoch seconds)
remove, mkdir, mkdirAll(String) -> Bool
rename, copy(String, String) -> Bool
cwd() -> String
listDir(String) -> String[] (names; empty if not a dir)

path - std/path.xi

Pure-Xi path string helpers (no I/O).

FunctionSignature
join(String, String) -> String
dirname(String) -> String ("." if none)
basename(String) -> String
ext(String) -> String (incl. dot, "" if none)
stripExt(String) -> String

net - std/net.xi

Blocking TCP sockets, client and server. Conn and Listener wrap a socket file descriptor. Data is sent/received as Bytes (with *Text convenience helpers). Listen on port 0 for an OS-assigned port, then read it with port.

FunctionSignature
dial(String, Integer) -> Conn! (host, port)
listen(Integer) -> Listener!
accept(Listener) -> Conn! (blocks)
port(Listener) -> Integer
send / recv(Conn, Bytes) -> Integer / (Conn, Integer) -> Bytes
sendText / recvText(Conn, String) -> Integer / (Conn, Integer) -> String
closeconsumer (Conn)
closeListenerconsumer (Listener)

http - std/http.xi

A minimal HTTP/1.1 client over net. http:// always works; https:// needs the toolchain built with XC_TLS=1. A Response is { status: Integer, headers: String, body: String }, where headers is the raw CRLF-separated header block; look one up with header.

FunctionSignature
get(String) -> Response!
post(String, String, String) -> Response! (url, body, contentType)
request(String, String, String, String) -> Response! (method, url, body, contentType)
header(Response, String) -> String (case-insensitive; "" if absent)
parseUrl(String) -> Url! ({host, port, path, tls}; supports http:// and https://)
parseResponse(String) -> Response!
import "std/http.xi"
let r = http.get("http://example.com/")
if isOk(r) {
system.stdout.writeln("status " + r.value.status)
system.stdout.writeln(r.value.body)
}

proc - std/process.xi

FunctionSignature
env(String) -> String (empty if unset)
envOr(String, String) -> String
run(String) -> Integer (shell command exit)
exitconsumer (Integer)

time - std/time.xi

FunctionSignature
nowNanos, nowMillis() -> Integer (monotonic)
sleepMsconsumer (Integer)

How it's built

Each module declares the C primitives it needs via extern "C" (e.g. xstd_sqrt, xstd_trim) and exposes a clean, namespaced API. The primitives live in runtime/runtime.c. Because modules use namespace, two modules can expose the same short name without colliding - see Multi-file projects.

!!! note "Collections" Generic containers (List<T>, Map<K,V>) await generics (monomorphization) and are not in the library yet. Use T[] arrays with for … in for now.