11 - Standard Library
Verge keeps the standard library small and modular. You import what you need — there’s no
implicit preamble — and the module names describe what they do. The whole library is
ordinary Verge source loaded from disk at compile time, so if a signature is ever unclear,
the definitive answer is one .v file away.
This chapter covers the modules you’ll reach for most. It isn’t exhaustive; treat it as an overview rather than a complete reference.
std.io
Everyday console I/O. write and writeln go to stdout (the latter adds a newline);
ewrite and ewriteln go to stderr. All are variadic and stitch their arguments together:
import std.io
main: f() {
io.write("hello")
io.writeln(" world")
io.ewriteln("this line goes to stderr")
}
hello world
There are input helpers too — io.readln(), io.read_char(), io.read_until(delim) — for
reading from stdin.
std.strings
The string toolkit: searching, slicing, transforming, and converting. These are free
functions (strings.fn(s, ...)), complementing the handful of built-in string methods
(.to_upper(), .to_lower(), .length()):
import std.io
import std.strings
main: f() {
io.writeln(strings.count("banana", "a")) # 3
io.writeln(strings.pad_left("7", 3, "0")) # 007
io.writeln(strings.trim_prefix("verge://docs", "verge://")) # docs
io.writeln(strings.from_int(42)) # 42
n := strings.parse_int("100") !! 0 # parsing returns (i32, error)
io.writeln(n * 2) # 200
}
3
007
docs
42
200
std.math
Numeric constants (PI, E, TAU) and functions. The integer helpers (abs, min, max,
clamp, sign, gcd, lcm, pow_int, isqrt, factorial) are generic; the float
functions (sqrt, pow, sin, cos, floor, ceil, round, random, …) are backed by
the platform math library:
import std.io
import std.math
main: f() {
io.writeln("sqrt(2) = {math.sqrt(2.0):.4}")
io.writeln("PI = {math.PI:.4}")
io.writeln("max(3,9) = {math.max(3, 9)}")
io.writeln("gcd = {math.gcd(12, 18)}")
io.writeln("clamp = {math.clamp(15, 0, 10)}")
}
sqrt(2) = 1.4142
PI = 3.1416
max(3,9) = 9
gcd = 6
clamp = 10
std.collections
Generic Stack<T> (LIFO) and Queue<T> (FIFO), implemented purely in Verge over a growable
slice:
import std.io
import std.collections
main: f() {
s := collections.Stack<i32>{}
s.push(1)
s.push(2)
s.push(3)
top, _ := s.pop() # pop/peek return (value, error)
io.writeln("popped {top}, {s.length()} left")
q := collections.Queue<string>{}
q.enqueue("first")
q.enqueue("second")
front, _ := q.dequeue()
io.writeln("dequeued {front}")
}
popped 3, 2 left
dequeued first
std.json
Parse and stringify JSON. json.parse returns a JsonValue tree you navigate with get,
at, has, and read out with as_string, as_int, as_number, as_bool:
import std.io
import std.json
main: f() {
doc := json.parse(`{"name": "verge", "year": 2026}`) !! json.null_val()
io.writeln("name: {doc.get(\"name\").as_string()}")
io.writeln("year: {doc.get(\"year\").as_int()}")
io.writeln("has name: {doc.has(\"name\")}")
}
name: verge
year: 2026
has name: true
(Note the backtick raw string around the JSON literal — it keeps the { and " characters
from being read as interpolation and string terminators.)
std.fs
File-system access: open/create, read_all/write_all/append_all, plus exists,
is_dir, delete, rename, copy, mkdir, and read_dir. The bulk helpers are the
easiest way in:
import std.io
import std.fs
import std.strings
main: f() {
path := "/tmp/verge_demo.txt"
fs.write_all(path, "hello from verge".bytes()) !! return
data := fs.read_all(path) !! return
io.writeln("read back: {strings.from_bytes(data)}")
io.writeln("exists: {fs.exists(path)}")
fs.delete(path) !! return
}
read back: hello from verge
exists: true
System modules: std.path, std.env, std.process
std.path does purely lexical path manipulation; std.env reads and writes environment
variables; std.process covers the process itself and running subprocesses:
import std.io
import std.path
import std.process
main: f() {
io.writeln("ext: {path.ext(\"src/main.v\")}") # .v
io.writeln("base: {path.base(\"src/main.v\")}") # main.v
io.writeln("dir: {path.dir(\"src/main.v\")}") # src
io.writeln("platform: {process.platform()}")
io.writeln("pid > 0: {process.getpid() > 0}")
}
ext: .v
base: main.v
dir: src
platform: linux
pid > 0: true
std.env’s get(name) returns (value, error), and std.process also offers
run(command) for a shell command and exec(argv) for a captured subprocess.
std.time
Current time, sleeping, and duration formatting. time.now() is a nanosecond timestamp, so
subtracting two readings gives an elapsed duration you can format:
import std.io
import std.time
main: f() {
start := time.now()
time.sleep_ms(10)
elapsed := time.now() - start
io.writeln("slept for at least 10ms: {elapsed >= 10_000_000}")
}
slept for at least 10ms: true
Networking: std.net and std.http
std.net provides TCP listeners/connections and UDP sockets. std.http builds on it with a
server (declared with @server/@get/@post attributes), a client (http.get,
http.post), and response helpers (http.ok, http.json_resp, http.redirect). A minimal
server looks like this:
import std.http
@server()
App: object {}
@get(App, "/")
index: f(req: HttpRequest) => HttpResponse {
return http.ok("Hello from Verge!")
}
@get(App, "/json")
data: f(req: HttpRequest) => HttpResponse {
return http.json_resp(`{"ok": true}`)
}
main: f() => i32 {
app := App{}
return app.run()
}
On the client side, http.get(url) returns a response with .Status and .Body.
Lower-level modules
A few modules exist for when you need them:
std.sync— coordinate futures withwait_all,wait_any, andrace. Pairs with thespawn/do/groupconcurrency from Advanced Features.std.mem— theAllocatortrait and its implementations (Arena,FixedBuffer,Pool,GlobalAlloc) for explicit memory management.std.errors—errors.new(msg)for basic errors. (The richer error operations — kinds,wrap,unwrap— are built-in onerror.*and need no import; see Error Handling.)
Module reference
| Module | What it’s for |
|---|---|
std.io | Console/stdin I/O |
std.strings | String search, slicing, conversion, parsing |
std.math | Constants and numeric functions |
std.collections | Stack<T>, Queue<T> |
std.json | JSON parse/stringify + JsonValue tree |
std.fs | Files and directories |
std.path | Lexical path manipulation |
std.env | Environment variables |
std.process | Process info and subprocesses |
std.time | Time, sleeping, durations |
std.net | TCP and UDP sockets |
std.http | HTTP server and client |
std.sync | Future coordination |
std.mem | Allocators |
std.errors | Basic error construction |
Finding your way around
The standard library is still young, so the reliable strategy is: start with the obvious
module name, read its exported declarations in stdlib/<name>/<name>.v, try a small example,
and build up from there — a sound approach for most software.