Builtins
Memory: builtins follow the same ownership rules as everything else. A builtin that builds a value hands it to the caller.
The built-in functions, by category. These are part of the language — always there, no import needed; the importable corelib layers more on top. Anything written as an operator or keyword (m[k], k in m, delete, for x in xs) lives on its own topic page.
Output and input
| Builtin | Type | Notes |
|---|---|---|
print(s) | string -> void | No implicit newline; add "\n". |
println(s) | string -> void | print(s) plus a trailing newline. String-only, like print — use println(str(x)) for non-strings. |
input() | -> string | One line from stdin (newline stripped). |
read_all() | -> string | All of stdin as one string. |
args() | -> [string] | Command-line arguments; args()[0] is the program name. |
getenv(name) | string -> string | The variable's value, or "" if unset. |
die(msg) | string -> void | Print msg to stderr and exit with status 1. |
exit(code) | int -> void | Terminate with status code, printing nothing; never returns. exit(0) is an ordinary answered exit, not a failure. |
input() cannot distinguish EOF from a blank line: both return "". When that matters, use io.open_lines / io.read_line, whose None is EOF.
print/println are stdio-backed. When stdout is a pipe or file, the buffer may not reach the reader until the process exits; call io.flush_stdout() when the reader must see a line while the program is still running.
Conversions
| Builtin | Type | Notes |
|---|---|---|
str(x) | int, sized ints, f32, bool, float, string, char -> string | A finite float prints as a decimal with at most 17 significant digits that reads back as the same float (the formatter tries 15, 16, then 17 and keeps the first that round-trips); fixed notation uses a ., exponent forms (e±NN) may omit it. A bool prints true/false. NaN and the infinities are the exception — see below. |
to_float(n) | numeric scalar -> float | Widen or reinterpret through the scalar base. |
to_int(x) | float, sized int, f32, char, or int-newtype -> int | Truncate a float toward zero; sized ints/char convert by value; int-newtype unwraps. |
to_bytes(s) / to_str(b) | string <-> bytes | Same byte buffer; bytes may carry interior NULs. |
to_bytes(xs) | [int] -> bytes | Each element & 0xFF into a fresh buffer — builds a binary bytes (interior NULs and all) that a string can't hold. |
to_bytes(v) | packed struct -> bytes | Exactly size_of$(T) bytes, little-endian, fields in declaration order. |
size_of$(T) | -> int | The byte size of a packed struct, exact. Also takes an align(N) struct, whose size is implementation-defined but a multiple of N. |
from_bytes$(T)(b) | bytes -> T | Read a packed struct back. len(b) must equal size_of$(T) exactly; any other length aborts, naming the type and both lengths. |
chr(n) | int -> string | The one-byte string for byte value n (0–255). |
to_char(n) | int -> char | The byte value n as a char. Outside 0..255 it aborts — where chr(n) returns a one-byte *string*, this returns a char. |
NaN and the infinities do not round-trip. str hands those three to the host C library, which on glibc spells them -nan, inf and -inf — no decimal point, and not the same text on every platform. strings.parse_float refuses all three by design, so a float written with str and read back with parse_float survives only if it was finite. x == x is false for exactly NaN, and x - x == 0.0 is false for exactly NaN and the infinities, which is how to check before serialising.
(A newtype's to_int / to_float / to_str / to_bool / to_under unwrappers are on the Types page.)
Strings
Strings are byte buffers; len, s[i], substr, and find are all byte-oriented (not Unicode-aware). String escapes: \n \t \r \\ \" — those five and no others, with the char set and the rejected spellings on the Types page.
| Builtin | Type | Notes |
|---|---|---|
len(s) | string -> int | Byte length. |
substr(s, a, b) | (string, int, int) -> string | Substring [a, b), a fresh copy. Out-of-range bounds are clamped, not an error. |
find(s, sub) | (string, string) -> int | Byte index of the first sub, or -1. |
char_at(s, i) | (string, int) -> char | The byte at i as a char — the same read and the same bounds abort as s[i]. |
split(s, sep) | (string, string) -> [string] | Split on a non-empty separator; n separators yield n+1 fields. An empty separator aborts. |
Arrays
| Builtin | Type | Notes |
|---|---|---|
len(a) | [T] -> int | Element count. |
push(a, v) | ([T], T) -> void | Append in place (needs a mutable array). |
pop(a) | [T] -> T | Remove and return the last element; aborts if empty. |
reserve(a, n) | ([T], int) -> void | Capacity hint (also works on a map place m[k]): preallocate room for n. A hint, not a length — len is unchanged and pushing past n still grows; an unallocatable capacity aborts. |
Maps
len(m), m[k] = v, k in m, delete m[k], and keys(m) are documented on the Maps page. The default-valued read is a method on the map:
| Builtin | Type | Notes |
|---|---|---|
m.get(k, d) | (map, K, V) -> V | Value for k, or default d if absent. m.get(k) is m[k] (value-type zero on a miss). |
hash(x) | K -> int for any map-key type | The hash the maps themselves use, exposed: values equal by == hash equal. Returns the full 64-bit value. |
Concurrency
See Concurrency for semantics.
| Builtin | Type | Notes |
|---|---|---|
wait(t) | Task(T) -> T | Join a spawned task (the result is copied out). Each task can be waited exactly once. |
ncpu() | -> int | Online CPUs, or TYCHO_THREADS when set to an integer >= 1. The *requested* worker count, not the number of parallel tasks: parallel for caps at 64 chunk tasks. |
channel(T, cap) | -> Channel(T) | A bounded lock-free queue (cap rounds up to a power of two). Legal only as a declaration's direct right-hand side. |
send(ch, v) | (Channel(T), T) -> void | Copy v in; blocks when full; aborts if closed. |
recv(ch) | Channel(T) -> Option(T) | Blocking receive (copied out); None means closed and drained. |
close(ch) | Channel(T) -> void | Receivers drain then see None; a further send or second close aborts. |
close(h) | handle -> void | On a typed FFI handle variable: run its destructor now and null it, so the scope-exit free does not run it again. A handle parameter is a borrow and cannot be closed (a compile error); close it in the scope that owns it. Any later mention of h in the same block (a second close included) is a compile error; a close inside an if or loop reaches only that block's end. See typed handles. |
Filesystem and time
| Builtin | Type | Notes |
|---|---|---|
read_file(path) | string -> string | Whole file as a string, or "" if it can't be opened. |
write_file(path, s) | (string, string) -> bool | Write s's exact bytes (truncating); false if it can't be opened. |
list_dir(path) | string -> [string] | Entries excluding ./.. (filesystem order); empty if it can't be opened. |
clock() | -> int | Monotonic nanoseconds (differences are meaningful; the absolute value is not). |
now() | -> int | Wall-clock seconds since the UNIX epoch. |
read_file is fail-open by design: a missing file, an unreadable file and a genuinely empty file all return "". When that distinction matters, use core:io.read_text(p) -> Result(string, io.IoErr) or read_bytes(p) instead.
Float math (libm)
| Builtin | Type |
|---|---|
sqrt(x) / pow(x, y) / floor(x) / fabs(x) | float… -> float |
FFI and sized-integer helpers
Supporting the FFI boundary and the sized numeric types; always available, no import.
| Builtin | Type | Meaning |
|---|---|---|
eprint(s) | string -> void | Write s to standard error (no newline, no exit). |
eprintln(s) | string -> void | Write s and a trailing newline to standard error. |
is_null(p) | ptr -> bool, handle -> bool | Test an opaque FFI ptr, or a typed handle, for null. On a handle: true when the opener failed, and true after close(h). After a close(h) statement in the same block it is a compile error (it could only be true); after a close in a branch it cannot tell "closed" from "never opened". |
to_ptr(n) | int -> ptr | Make a sentinel ptr from an int (e.g. (void*)-1); Tycho never dereferences it. |
to_i32(n) | int -> i32 | Explicitly narrow to the signed 32-bit integer type. |
to_u32(x) / to_u64(x) / to_f32(x) | numeric -> u32/u64/f32 | Convert any numeric scalar to the sized type. |