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 with wait_all, wait_any, and race. Pairs with the spawn/do/group concurrency from Advanced Features.
  • std.mem — the Allocator trait and its implementations (Arena, FixedBuffer, Pool, GlobalAlloc) for explicit memory management.
  • std.errorserrors.new(msg) for basic errors. (The richer error operations — kinds, wrap, unwrap — are built-in on error.* and need no import; see Error Handling.)

Module reference

ModuleWhat it’s for
std.ioConsole/stdin I/O
std.stringsString search, slicing, conversion, parsing
std.mathConstants and numeric functions
std.collectionsStack<T>, Queue<T>
std.jsonJSON parse/stringify + JsonValue tree
std.fsFiles and directories
std.pathLexical path manipulation
std.envEnvironment variables
std.processProcess info and subprocesses
std.timeTime, sleeping, durations
std.netTCP and UDP sockets
std.httpHTTP server and client
std.syncFuture coordination
std.memAllocators
std.errorsBasic 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.