What this page is for
A standard library is a box of ready-made tools that comes with the language. You could write a function that finds the biggest number in a list yourself, but someone already did, tested it, and put it in the box.
This page is the instruction sheet for the whole box. It does not describe any one tool. It describes the rules every tool follows, so that after you have used two or three modules, you can guess how the next one behaves.
This page says how the standard library is shaped: how it names things, how its functions report failure, how it handles resources, what is public, and what a 2.x program can rely on. The other pages in this folder document one module each.
Modules
| Module | For | Import | Page |
|---|---|---|---|
int, double, bool, char |
helpers on the primitive types: n.gcd(m), x.round(), c.is_digit() |
none (prelude) | Core: numbers, text and arrays |
strings |
string search, slicing, trimming, splitting, parsing, StringBuilder |
none (prelude) | Core: numbers, text and arrays |
array |
sum, max, min, contains, index_of over int[], double[], string[] |
none (prelude) | Core: numbers, text and arrays |
io |
printing, reading input, reading and writing files, exists |
none (prelude) | I/O and networking |
math |
abs, min, max, clamp, power, libm functions, divide, constants |
none (prelude) | Math and the operating system |
sys |
panic, exit, abort |
none (prelude) | I/O and networking |
list |
List<T>, a growable array |
import list::List; |
Collections |
stack |
Stack<T>, last in first out |
import stack::Stack; |
Collections |
queue |
Queue<T>, first in first out |
import queue::Queue; |
Collections |
map |
Map<K, V>, a hash map |
import map::Map; |
Collections |
set |
Set<T>, a hash set |
import set::Set; |
Collections |
hashing |
hash(x), the hash Map and Set use |
import hashing; |
Collections |
net |
TCP client and server sockets | import net; |
I/O and networking |
fs |
path strings and directories | import fs; |
Files and directories |
os |
environment variables and command-line arguments | import os; |
Math and the operating system |
time |
the clock, elapsed time, sleeping | import time; |
Time |
random |
pseudo-random numbers | import random; |
Random numbers |
mem |
manual-heap allocation and raw memory | import mem; |
Memory |
json |
parse and build JSON | import json; |
JSON |
http |
a small HTTP/1.1 server and request/response types | import http; |
HTTP |
test |
assert, assert_eq, fail |
import test; |
Testing |
A module outside the prelude must be imported in each file that writes its
qualifier: import list; lets a file write list::List<int>, while
import list::List; lets it write List<int> but not list::List. A file that
needs both writes both. See Functions and modules.
The prelude
Before you write a line, Turf has already loaded nine modules into every file. That set is the prelude, and it is why printline("hi") works with no import. The nine split into two groups, by how you reach them.
- Bare namesio, math, sys
Call them by their plain name:
printline("hi"),abs(-3),max(a, b),sqrt(2.0),exit(1),panic("message"). - Dot or qualifierint, double, bool, char, strings, array
Use a method (
c.is_digit(),s.trim()), an operator (a + bon strings), or a qualified name (strings::length(s)).
fn main(): int { printline(abs(-5)); printline(math::max(1.5, 0.5)); printline(math::PI); mut c: char = '7'; printline(c.is_digit()); printline(strings::length("turf")); printline("turf".length()); return 0;}$ turf run conventions_prelude.tr
5 1.5 3.14159 true 4 4
Four rules cover the rest:
- The six dotted modules have no bare names.
is_digit(c)is E041; writec.is_digit(). - Constants stay qualified.
math::PI, never a barePI. - Your own names win. A bare name is the last thing looked up, so a function of your own with the same name hides the prelude’s.
math::max(...)still reaches the original. - Everything else needs an
import. See the table above.
The full lookup order and the one trade-off it carries are in Functions and modules: the prelude.
1. Naming
Every public function, method and field is snake_case (index_of, try_get,
is_digit, to_upper). Types are PascalCase (List, StringBuilder,
TcpConnection) and constants UPPER_CASE (PI, CLOCKS_PER_SEC). The
library has one spelling for each name, with no aliases. A script checks this
on every CI run (scripts/check/stdlib_surface_check.sh).
2. Failure: three kinds, three shapes
A function reports failure in the way that matches why it can fail.
| Why it fails | Shape | Examples |
|---|---|---|
| The input or the environment: a file that is not there, text that is not a number, a refused connection. Not a bug in the caller. | returns !T, with an error variant that carries what failed (the path, the offset, the name) and a message saying so (err.message) |
io::read_to_string, io::write_string, fs::mkdir, os::set_env, strings::parse_int, json::parse, math::divide, net::TcpConnection::connect, Map.try_get |
A violated precondition: popping an empty stack, an index out of range, random_int(7, 3). A bug in the caller that no recovery makes correct. |
panics, and the message starts with the caller’s file:line:, so you learn which of your calls was wrong, not which line of the library noticed |
List.pop, Stack.pop, Queue.dequeue, Map.get, strings::substring, JsonValue.get, random::random_int |
| A question: is it there, is it a directory, does it start with this? Absence is a normal answer. | returns bool, or a sentinel (-1 from index_of, "" from os::env for an unset variable) |
io::exists, fs::is_dir, Set.contains, strings::index_of |
Some failures inside a function, such as an array index past the end or an integer overflow, are panics reported by the language; see Memory and safety: checks that panic.
Where a call has both a panicking and a recoverable form, they are a pair, and a predicate lets you ask first:
| Panics on a miss | Recoverable twin | Ask first |
|---|---|---|
Map.get(k) |
Map.try_get(k): !V |
Map.contains(k) |
JsonValue.get(k) |
none | JsonValue.has(k) |
http::Request.get_header(n) |
none | Request.has_header(n) |
http::Server.get_route(p) |
none | Server.has_route(p) |
A !void result is handled like any other !T: fs::mkdir(p) recover run { ... };
runs the block only on failure (Error handling).
Leaving the result unhandled discards the failure silently, so check anything
whose failure matters.
import fs;fn main(): int { mut dir: string = "/tmp/turf_stdlib_readme_dir"; fs::mkdir(dir) recover run { printline("could not create the directory"); return 1; }; fs::remove_dir(dir) recover run { printline("could not remove it"); }; printline(fs::is_dir(dir)); // false: a question, so a bool return 0;}$ turf run conventions_failure_shapes.tr
false
3. Resources and defer
Turf has no finalizers: the collector reclaims memory, not file descriptors. A
value that owns an operating-system resource has an explicit close(), and the
idiom is defer, which runs on every way out of the block, including each pass
of a loop:
import net;fn main(): int { mut conn: net::TcpConnection = net::TcpConnection::connect(127, 0, 0, 1, 8080) recover run { printline("could not connect"); return 1; }; defer conn.close(); conn.write("ping"); return 0;}TcpConnection.close() and TcpListener.close() are idempotent (a second call
does nothing), so an early explicit close alongside the defer is safe.
raw_fd() returns the descriptor (-1 once closed) for passing to FFI.
4. Visibility: what is public
A module’s declarations are private unless marked pub
(Functions and modules). The library follows
this rule:
- A function that does a job is public, whether or not the library itself
calls it.
io::input,test::assert_neqandstrings::last_index_ofare public because a program may want them. - A struct’s internals are public only if a real program does something with
them.
Request.methodis public (a handler reads it);List’s buffer,Map’s hash index andServer’s route table are not. - Raw C bindings are private. The library wraps
fopen,getenv,socketand the rest in checked Turf functions; the wrappers are the interface. The exceptions are the bindings that are public as they stand (mem::malloc,time::usleep,sys::exit, the libm functions inmath), which are part of the surface.
A program that needs a C function the library does not wrap declares its own
extern fn for it (FFI and unsafe).
5. Stability: what 2.x guarantees
The public items of the standard library are the 2.x compatibility surface: a
pub function, method, type, error, constant or field is not removed, renamed,
or given a different signature or failure shape before 3.0. Additions are
allowed in a minor release. This is checked, not just intended:
tests/46_stdlib_surface/compiles and runs a use of every public item, so a removal or rename breaks a test.scripts/check/stdlib_surface_check.shfails if a public item exists that no file there mentions (a new name added without being locked) or that is notsnake_case. It runs in CI.- Every module has its own behaviour tests.
Three things are not part of the surface: the behaviour of json::parse for
\uXXXX escapes (a known limitation, see JSON), the iteration
order of Map.keys() after a remove(), and mem::malloc_usable_size and
mem::usable_size, which depend on the C library and may be unavailable on a
platform.