201 builtins that are always available via initial_env. Check a name's type with mere -te NAME.
Legend:
- ⚡ = may raise
Eval_error - ★ = polymorphic (builtin-level polymorphism, not let-poly)
- 🌐 = works in all 4 backends (interp + C + LLVM + Wasm) — added incrementally through Phases 22-31
Syntactic sugar (13): lexer / parser-level changes only; preserves 4-backend compatibility:
a..b— range literal (desugars torange a b; inclusive on both ends)(+ N)/(* N)etc. — operator section (11 operators;-is excluded)h :: t— cons (sugar forCons (h, t); right-associative)f <| x— reverse pipe (f x; RHS acceptsfn/let)f @@ x— OCaml-style alias for<|\x -> body/\a b c -> body— lambda shorthand (no type annotations)"hello {expr}"— string interpolation (desugars to++ (expr) ++;\{escapes a literal brace)let x = e? in body— Option early-returnlet x = e?! in body— Result early-return[expr | x <- xs, cond, y <- ys, ...]— list comprehension (multi-generator + filter in any order)if let pat = e then ... else ...— conditional destructurefor x in xs do body— sugar forlist_iterwhile cond do body— desugars to a recursive helper (codegen supported inside fn bodies only)
Prelude additions (16 of 34 entries added in Phase 36):
- range / list_filter / list_take / list_drop / list_find / list_append
- list_concat / list_flat_map / list_zip / list_for_all / list_any
- list_member / list_sum / list_product / list_max / list_min
| Name | Type | Description |
|---|---|---|
print |
str -> unit |
Write to stdout with newline |
print_no_nl |
str -> unit |
Without newline + flush (for prompts) |
print_int |
int -> unit |
Print integer with newline |
print_bool |
bool -> unit |
Print bool with newline |
print_err |
str -> unit |
Write to stderr with newline |
read_line |
unit -> str |
One line from stdin; empty string on EOF |
read_file ⚡ |
str -> str |
Read the whole file as text; raises on failure. On the C backend the str is NUL-terminated, so binary data silently truncates at the first 0x00 byte (the interpreter's strings carry NULs) — use read_file_bytes for binary (v0.1.43) |
read_file_bytes ⚡ |
str -> Vec[R, int] |
Read the whole file as raw bytes — one int (0..255) per byte, binary-safe on every supported backend. interp + C only (v0.1.43, CRC-32 probe) |
write_file ⚡ |
str -> str -> unit |
Write content to path (overwrite); raises on failure |
write_file_bytes ⚡ |
str -> Vec[R, int] -> unit |
Write an int vec as raw bytes (each element 0..255) — the write half of the binary path; PPM P6 etc. interp + C only (v0.1.44, Mandelbrot probe) |
read_lines ⚡ ★ |
str -> str list |
Read line by line, returns str list (Phase 19.6; depends on prelude) |
file_exists |
str -> bool |
Whether path exists (Phase 19.6; on C native since v0.1.15) |
file_mtime |
str -> float |
Modification time in seconds; raises if the path is missing (interp + C native) |
file_size |
str -> int |
File size in bytes (stat); binary-safe length where str_len (strlen) stops at a NUL. interp + C native (v0.1.21) |
env_var ★ |
str -> str option |
Fetch env var; None if unset (Phase 19.6; depends on prelude) |
args ★ |
unit -> str list |
The program's own args (after the script path / binary name); consistent interp ↔ native since v0.1.12 |
run |
str -> int |
Run a command line via the shell, inherit stdio, return its exit code (interp + C native; v0.1.13) |
stdin_byte |
unit -> int |
One byte from stdin without blocking; -1 when nothing is ready. read_key blocks, which a device emulator polling a line-status register cannot afford (interp + C native) |
file_exists "/etc/hosts" // → true
env_var "PATH" // → Some "..."
env_var "BOGUS" // → None
read_lines "data.txt" // → ["line1", "line2", ...]
args () // → ["foo", "bar"] (mere prog foo bar)
run "clang -O2 main.c -o app" // → 0 on success, nonzero exit code otherwise
★ Codegen status: print / print_no_nl / print_int / print_bool / print_err / read_file / write_file work in all 3 backends (Wasm goes through host imports; scripts/run_wasm.js provides puts / read_file / write_file). print_int / print_bool were the exception until v0.1.190 — this line claimed them for years while only the interpreter had them; C emitted a call to an undefined symbol and LLVM / Wasm refused outright. They now lower on all four (C through printf, LLVM and Wasm through the str_of_int they already had), locked by test/parity/print_int_bool.mere. read_lines / env_var are interpreter-only (codegen would need 'a list / 'a option construction + systematic outside-world access; not yet covered by Phases 22-31). args works on all four backends: C and LLVM read the argc/argv their main was handed, Wasm folds the host's arg_count / arg_get (v0.1.159 for Wasm, v0.1.169 for LLVM). The native-CLI / dogfood builtins run / print_err / file_exists / file_mtime / file_size / tty_raw / tty_restore / read_key / random_int also work on the C native backend (added for the mk / mrog / mwasm dogfoods, v0.1.13-v0.1.21).
let _ = print "Hello";
let _ = print_no_nl "Name: ";
let name = read_line () in print ("Hi, " ++ name);
// File round-trip
let _ = write_file "/tmp/out.txt" "hello lang";
let content = read_file "/tmp/out.txt" in print content;
| Name | Type | Description |
|---|---|---|
str_of_int |
int -> str |
Integer to string |
int_of_str ⚡ |
str -> int |
Parse after trim; raises on bad input |
bool_of_str ⚡ |
str -> bool |
Trim then "true"/"false" only; raises otherwise |
float_of_int |
int -> float |
int → float (no precision loss) |
int_of_float |
float -> int |
float → int (truncation) |
str_of_float |
float -> str |
Float to string (OCaml semantics) |
float_of_str ⚡ |
str -> float |
Parse after trim; raises on bad input |
str_of_int 42 // "42"
int_of_str " -7 " // -7
bool_of_str "true" // true
| Name | Type | Description |
|---|---|---|
str_len |
str -> int |
Byte length |
str_contains |
str -> str -> bool |
Substring containment |
str_starts_with |
str -> str -> bool |
Prefix test |
str_ends_with |
str -> str -> bool |
Suffix test |
str_count |
str -> str -> int |
Non-overlapping occurrence count |
str_index_of ★ |
str -> str -> int |
First position of needle; -1 if not found. Empty needle returns 0 (Phase 19.1) |
str_split ★ |
str -> str -> str list |
Split by delimiter; returns str list. Requires type 'a list = ... declared. Empty delimiter returns a single-element list (Phase 19.1) |
utf8_len ★ |
str -> int |
Codepoint count (a str is bytes; str_len is the byte length). Invalid bytes count as single units (v0.1.38) |
utf8_chars ★ |
str -> str list |
Split into codepoints — the building block for text processing (v0.1.38) |
utf8_at |
str -> int -> str |
i-th codepoint (prelude, on utf8_chars) |
utf8_sub |
str -> int -> int -> str |
Codepoint-indexed substring (prelude) |
utf8_rev |
str -> str |
Codepoint-wise reverse — str_rev is byte-wise and scrambles multibyte text (prelude) |
utf8_width |
str -> int |
Display width (East Asian Width, wcwidth-lite): CJK / fullwidth / emoji = 2 columns, combining marks = 0, halfwidth katakana = 1. utf8_len counts codepoints; terminals draw columns — use this for alignment (prelude, v0.1.45) |
pad_right |
str -> int -> str |
Pad with spaces to a display width (table columns, left-aligned); no-op if already wide enough (prelude, v0.1.45) |
pad_left |
str -> int -> str |
Right-align to a display width — numbers in table columns (prelude, v0.1.45) |
str_join ★ |
str -> str list -> str |
Join with separator. Empty list → empty string (Phase 19.1) |
str_compare 🌐 |
str -> str -> int |
Lexicographic -1 / 0 / 1 (Phase 31.0 ported to 3 backends; sign-normalized) |
str_repeat ⚡ |
str -> int -> str |
Repeat N times; raises on N<0 |
str_replace |
str -> str -> str -> str |
Replace all; empty needle = no change |
str_rev |
str -> str |
Reverse string |
str_trim |
str -> str |
Strip leading/trailing whitespace |
str_unescape ⚡ |
str -> str |
Decode \n \t \r \\ \" \/; raises on unknown escape |
substring ⚡ |
str -> int -> int -> str |
s[start:end_excl]; raises on out of range |
char_at ⚡ |
str -> int -> str |
Index access (length-1 str); raises on OOB |
chr ⚡ |
int -> str |
int in 0..255 to single-char str; raises out of range |
ord ⚡ |
str -> int |
Single-char str to int code point; raises if length != 1 |
to_upper |
str -> str |
ASCII uppercase |
to_lower |
str -> str |
ASCII lowercase |
is_digit |
str -> bool |
True for single char in '0'..'9'; otherwise false |
is_alpha |
str -> bool |
True for single char that's a letter |
is_space |
str -> bool |
True for single char that's space/tab/\n/\r |
type 'a list = Nil | Cons of 'a * 'a list;
str_split "a,b,c" "," // ["a", "b", "c"]
str_join "-" ["alpha", "beta", "gamma"] // "alpha-beta-gamma"
str_index_of "hello world" "world" // 6
str_index_of "hello" "xyz" // -1
★ Codegen status: str_index_of / str_split / str_join / str_count / str_compare / str_trim / str_starts_with / str_ends_with / str_contains / str_replace / str_repeat / str_rev all work across all 4 backends (Phase 19.1.1 added str_index_of; Phase 22 added str_split / str_join; Phase 26.5 added all Wasm str ops; Phase 31.0 added str_compare; Phase 36 added str_trim / starts_with / ends_with / contains / replace / repeat / rev). not / abs / min / max / clamp / chr / ord / to_upper / to_lower / even / odd / gcd / bool_of_str also reached the 3 backends in Phase 36. The fn (_: unit) -> body wildcard parameter was also parser-fixed in Phase 36.
str_replace "foo bar foo" "foo" "X" // "X bar X"
substring "hello world" 6 11 // "world"
char_at "abcdef" 2 // "c"
"world" |> str_contains "hello world" // true (pipe + curry)
str_unescape "a\\nb" // a + newline + b (3 chars)
| Name | Type | Description |
|---|---|---|
min |
int -> int -> int |
Smaller |
max |
int -> int -> int |
Larger |
abs |
int -> int |
Absolute value |
sign |
int -> int |
-1 / 0 / 1 |
clamp |
int -> int -> int -> int |
clamp lo hi x restricts to [lo, hi] |
pow ⚡ |
int -> int -> int |
base^exp; raises on negative exp |
square |
int -> int |
x * x |
cube |
int -> int |
x * x * x |
incr |
int -> int |
+1 |
decr |
int -> int |
-1 |
even |
int -> bool |
n mod 2 == 0 |
odd |
int -> bool |
n mod 2 != 0 |
gcd |
int -> int -> int |
Euclid (handles negatives and 0 correctly) |
lcm |
int -> int -> int |
` |
divmod ⚡ |
int -> int -> (int * int) |
(quotient, remainder); raises on 0 div |
sum_range |
int -> int -> int |
Sum over lo..hi (Gauss formula, O(1)) |
not |
bool -> bool |
Logical negation |
bit_and |
int -> int -> int |
Bitwise AND on the backend's native int width (v0.1.42) |
bit_or |
int -> int -> int |
Bitwise OR (v0.1.42) |
bit_xor |
int -> int -> int |
Bitwise XOR (v0.1.42) |
bit_not |
int -> int |
Bitwise complement; numerically -x - 1 on every backend (v0.1.42) |
bit_shl |
int -> int -> int |
Shift left. Keep counts in 0..62 for portable code — int is 64-bit on C, LLVM and Wasm (widened in v0.1.96 / v0.1.127), 63-bit on interp |
bit_shr |
int -> int -> int |
Arithmetic (sign-propagating) shift right; bit_shr x n equals floor division by 2^n on every backend (v0.1.42) |
Note (v0.1.44): the infix operators
+ - * /, all comparisons, and unary-are numeric-overloaded and work directly on floats, on every backend — prefer them. Thef_functions below remain as ordinary function values (useful for passing to higher-order functions). The overload resolves to float only when an operand is concretely float; annotate fn params (fn (x: float) -> ...) in float-heavy code.
| Name | Type | Description |
|---|---|---|
f_add |
float -> float -> float |
Addition |
f_sub |
float -> float -> float |
Subtraction |
f_mul |
float -> float -> float |
Multiplication |
f_div |
float -> float -> float |
Division (IEEE 754: 0 div is inf/nan) |
f_lt |
float -> float -> bool |
Less than |
f_le |
float -> float -> bool |
Less than or equal |
f_gt |
float -> float -> bool |
Greater than |
f_ge |
float -> float -> bool |
Greater than or equal |
f_neg |
float -> float |
Unary minus (Neg is int-only, so use this for float) |
f_abs |
float -> float |
Absolute value |
sqrt |
float -> float |
Square root (NaN for negatives) |
floor |
float -> float |
Floor |
ceil |
float -> float |
Ceiling |
round |
float -> float |
Round |
f_min ★ |
float -> float -> float |
Smaller (Phase 19.7) |
f_max ★ |
float -> float -> float |
Larger (Phase 19.7) |
f_pow ★ |
float -> float -> float |
Power base ^ exp (Phase 19.7) |
log ★ |
float -> float |
Natural log (Phase 19.7) |
exp ★ |
float -> float |
e^x (Phase 19.7) |
sin ★ |
float -> float |
Sine (radians; Phase 19.7) |
cos ★ |
float -> float |
Cosine (Phase 19.7) |
tan ★ |
float -> float |
Tangent (Phase 19.7) |
atan2 ★ |
float -> float -> float |
atan2 y x for angle (Phase 19.7) |
random_int ★ ⚡ |
int -> int |
random_int n returns int in 0..n-1; raises if n<=0 (Phase 19.7) |
random_float ★ |
unit -> float |
Float in [0.0, 1.0) (Phase 19.7) |
pi |
float |
π ≈ 3.14159265 (constant builtin) |
e |
float |
e ≈ 2.71828183 (constant builtin) |
★ Codegen status: the 11 entries added in Phase 19.7 are interpreter-only. Codegen support requires libm linking or per-backend wiring of built-in math functions, planned for a follow-up slice (19.7.1).
f_add 1.5 2.5 // 4.0
f_div 10.0 4.0 // 2.5
3.14 |> f_mul 2.0 // 6.28
clamp 0 100 150 // 100
pow 2 10 // 1024
gcd 12 18 // 6
sum_range 1 100 // 5050
fst (divmod 100 7) + snd (divmod 100 7) // 14 + 2
| Name | Type | Description |
|---|---|---|
fail ⚡ ★ |
str -> 'a |
Panic that unifies with any type |
assert ⚡ |
bool -> str -> unit |
On false, raises "assertion failed: MSG" |
try_or ★ |
(unit -> 'a) -> 'a -> 'a |
Evaluate the thunk; catch Eval_error and return default |
let safe = fn s -> try_or (fn () -> int_of_str s) (- 1);
safe "42" // 42
safe "abc" // -1
if x < 0 then fail "negative" else x
fail is polymorphic, so type inference works at branch merges (if c then fail msg else int_val → int).
| Name | Type | Description |
|---|---|---|
show ★ |
'a -> str |
Stringify any value via to_string |
id ★ |
'a -> 'a |
Identity function |
fst ★ |
('a * 'b) -> 'a |
Tuple first |
snd ★ |
('a * 'b) -> 'b |
Tuple second |
pair ★ |
'a -> 'b -> ('a * 'b) |
Tuple constructor (curried) |
swap ★ |
('a * 'b) -> ('b * 'a) |
Tuple swap |
const ★ |
'a -> 'b -> 'a |
Drop second arg, return first |
flip ★ |
('a -> 'b -> 'c) -> ('b -> 'a -> 'c) |
Reverse arg order of a curried fn (higher-order) |
show 42 // "42"
show (Some 5) // "Some 5"
show [1, 2, 3] // "[1, 2, 3]" (Cons/Nil chains shown as [..])
show [Some 1, None, Some 3] // "[Some 1, None, Some 3]"
fst (pair "hi" 42) // "hi"
let always_7 = const 7 in always_7 "anything" // 7
let sub = fn a -> fn b -> a - b in (flip sub) 3 10 // 7 (= sub 10 3)
Structural JSON, compile-time-specialized per type (no trait machinery),
like show. to_json works on all four backends — on LLVM it shares
the emitter with show, since the two differ only in literals (v0.1.184).
of_json and its siblings are interp / C / Wasm: decoding needs a JSON
parser in the target language, and LLVM has no hand-written one.
| Name | Type | Description |
|---|---|---|
to_json ★ |
'a -> str |
Serialize any value to JSON structurally |
of_json ★ |
str -> 'a |
Parse JSON into a typed value; fails fast on error (trusted input) |
of_json_opt ★ |
str -> 'a option |
Same, but returns None on any error (safe for untrusted input) |
of_json_like ★ |
'a -> str -> 'a |
Target type from a witness value instead of an annotation (v0.1.183) |
of_json_opt_like ★ |
'a -> str -> 'a option |
The non-crashing witness form |
of_json reads the target type off the call node, which is fine at a use
site with an annotation and impossible inside a generic helper: there the
node's type is a variable, and the interpreter has no runtime types to
resolve it with. So a generic "decode it back" had to name the record type,
and every record needed its own copy.
A witness supplies the type instead. The interpreter reads it off the value's runtime shape — a record carries its type's name — and the compiled backends read it off the witness's static type, which is the same variable the result unifies with:
let with_field = fn (rec_) -> fn (name: str) -> fn (v: str) ->
... of_json_opt_like rec_ (rebuilt_json) ...
The witness is a value the caller already has whenever this comes up:
replacing one field of a record means holding the record. contrib/schema
is this, and examples/claims generates its whole form from it.
A polymorphic record still needs the annotation — a value carries its
type's name but not its type arguments, so a witness cannot describe
Box[int].
The of_json result type comes from the use site — annotate the
expression: (of_json s : T). A JSON object maps to a record's fields (by
name), an array to a list or tuple, null/value to option (None /
Some), and a string / {"Ctor": payload} to a variant. to_json uses
the same mapping in reverse, so (of_json (to_json x) : T) == x.
type User = { id: int, name: str, bio: str option };
to_json (User { id = 1, name = "ada", bio = None })
// {"id":1,"name":"ada","bio":null}
let u = (of_json body : User); // fails fast if body is malformed
match (of_json_opt body : User option) with
| Some u -> u.name // decoded
| None -> "bad request" // malformed / missing field — no crash
== / != (structural equality) and < <= > >= (structural ordering)
are compile-time-specialized per operand type — the same no-trait
mechanism as show / to_json. Both work on interp / C / Wasm.
- Scalars:
int/float/bool/strcompare directly (strlexicographically). - Compound: tuples and records compare field-by-field in declared order; lists compare element-wise (a shorter prefix is smaller); variants order by declaration order (the constructor listed first is smallest), then by payload. All backends agree byte-for-byte, so a value sorts the same under the interpreter, a native binary, and Wasm.
(1, 2) < (1, 3) // true (tuple, lexicographic)
[1,2] < [1,2,3] // true (prefix is smaller)
type C = Red | Green | Blue; Red < Blue // true (declaration order)
list_sort_by (fn (a: float) -> fn (b: float) -> a < b) [3.1, 1.2] // [1.2, 3.1]
Honest edges. float uses a total order where NaN sorts as least.
Comparing two functions is defined but meaningless (they order as equal).
The bare default list_sort still bakes in an int comparison — its
comparator's type variables default to int, the same rule that keeps
fn a -> fn b -> a < b monomorphic — so sorting a non-int list needs
list_sort_by with an annotated comparator (as above). A
fully-polymorphic list_sort over any orderable element would need
ad-hoc-polymorphism resolution (deferred).
| Name | Type | Description |
|---|---|---|
iter_n ★ |
int -> (unit -> unit) -> unit |
Apply thunk N times (side-effect loop); no-op when N≤0 |
Used by the effect system (see effects.mere). The Logger and Metrics cap types are pre-registered as builtins. Users can also override with their own type Logger = ....
type Logger = { info: str -> unit, warn: str -> unit, error: str -> unit };
type Metrics = { inc: str -> unit, record: str -> int -> unit };
| Name | Type | Description |
|---|---|---|
mk_logger |
str -> Logger |
Create a prefixed Logger. Each field prints as prefix [LEVEL] msg |
mk_metrics |
unit -> Metrics |
Create a Metrics. inc / record print as [METRIC] ... |
let lg = mk_logger "app" in
{ lg.info "started";
lg.warn "slow query";
lg.error "abort" }
let m = mk_metrics () in
{ m.inc "users";
m.record "latency_ms" 23 }
For a complete cap-passing example see examples/effects.mere.
A Raw is a window onto physical memory — the one capability that is not a
record of functions, because its operations lower to load and store
instructions. It is the escape hatch a device driver needs, and it is a value
rather than an ambient builtin so that "this function cannot touch raw memory"
is something you read off a signature.
Raw is opaque: nothing constructs one, and there is no function that mints
one. The only source is the argument mere -rv --bare hands to the program's
top-level main, and raw_window can only narrow it. Offsets are relative
to the window, so a driver holding a UART window cannot express an address
outside it; every access bounds-checks the offset, and widening faults.
| Name | Type | Description |
|---|---|---|
raw_window |
Raw -> int -> int -> Raw |
A window over [off, off+len) of another. Faults if that is not inside it |
csr_read |
int -> int |
A machine CSR by number — the number must be a literal (it is an immediate field of the instruction) |
csr_write |
int -> int -> unit |
Write a machine CSR. Not behind a capability: a CSR has no base and length to narrow, and the hardware's privilege modes are what separate a kernel from a user process |
raw_base |
Raw -> int |
A window's base as a number. Not authority — touching anything still needs a window — but a stack pointer is an address and hardware wants the number |
trap_save |
Raw -> Raw |
The trap trampoline's 31-word register save area. A context switch is a copy through this: outgoing registers to a TCB, incoming registers back |
machine_scratch |
Raw -> Raw |
Reserved RAM the runtime is not using — where task stacks come from. A bare program owns no fixed address of its own: the heap grows up from 2MB and the stack down from the top |
closure_code |
(unit -> unit) -> int |
A closure's entry point. A task IS a closure, so starting one means building a context whose PC is this |
closure_env |
(unit -> unit) -> int |
Its environment — the value the first argument register must hold when that PC is entered. ABI knowledge, which a kernel has |
set_trap_handler |
(int -> int) -> unit |
Install a trap handler. The argument is mcause; the result is the PC to resume at. Anything else (mepc 0x341, mtval 0x343) is a csr_read away. A closure, not a named function: a handler needs the machine capability to do anything useful and an interrupt has no caller to hand it one, so it captures instead. Codegen emits the trampoline that saves the register set and returns with mret |
raw_peek8 |
Raw -> int -> int |
The byte at that offset |
raw_peek32 |
Raw -> int -> int |
The 32-bit word at that offset |
raw_poke8 |
Raw -> int -> int -> unit |
Store a byte |
raw_poke32 |
Raw -> int -> int -> unit |
Store a 32-bit word |
let putc = fn (uart: Raw) -> fn (c: int) -> raw_poke8 uart 0 c;
let main = fn (mach: Raw) ->
let uart = raw_window mach 0x10000000 256 in // the UART, and nothing else
putc uart 65;
A context switch needs no new mechanism: the trampoline saves the interrupted
register set to the area trap_save hands back and restores from it before
mret, so a handler swaps tasks by copying through it and returning the
incoming task's PC. gp is the one word to leave alone — the heap is machine
state, not task state, and switching it makes two tasks allocate over each
other. See examples/riscv_bare_sched.mere.
Device MMIO sits above any RAM (the UART data register is at 0x10000000, the
address QEMU's virt machine uses), so a device address does not move when
--ram does. On every other backend these refuse: there is no honest physical
address in a hosted process. See
examples/riscv_bare_uart.mere.
| Name | Type | Description |
|---|---|---|
time |
unit -> float |
Unix epoch seconds (gettimeofday). For benchmarks / timestamps |
exit ★ |
int -> 'a |
Exit the process with an exit code (never returns; polymorphic return) |
int_max |
int |
Max int value (OCaml runtime dependent; 2^62-1 on 64-bit) — constant builtin |
int_min |
int |
Min int value — constant builtin |
let start = time () in
{ run_heavy_computation ();
print ("elapsed: " ++ str_of_float (f_sub (time ()) start) ++ " sec") }
if config_invalid then exit 1 else continue ()
iter_n 3 (fn () -> print "===") // prints === three times
abs args assert atan2 bit_and bit_not bit_or bit_shl bit_shr bit_xor
bool_of_str ceil char_at chr clamp const
cos cube decr divmod e env_var even exit exp f_abs f_add
f_div f_ge f_gt f_le f_lt f_max f_min f_mul f_neg f_pow
f_sub fail file_exists flip float_of_int float_of_str floor
fst gcd id incr int_max int_min int_of_float int_of_str
is_alpha is_digit is_space iter_n lcm log max min mk_logger
mk_metrics not odd ord pair pi pow print print_bool
print_err print_int print_no_nl random_float random_int
closure_code closure_env csr_read csr_write machine_scratch
raw_base raw_peek32 raw_peek8 raw_poke32 raw_poke8 raw_window trap_save
stdin_byte
read_file read_file_bytes read_line read_lines round show sign sin snd sqrt
square str_compare str_contains str_count str_ends_with
str_index_of str_join str_len str_of_float str_of_int
str_repeat str_replace str_rev str_split str_starts_with
set_trap_handler str_trim str_unescape substring sum_range swap tan time
to_lower to_upper try_or write_file write_file_bytes
Q-010 collection builtins (vec_* / owned_vec_* / strbuf_* / map_* / len) are registered builtins outside this table; see language-reference / tutorial. Phase 19.2 added map_iter : Map[R, K, V] -> (K -> V -> unit) -> unit (works in all 4 backends).
- Operators (
+ * == ++ |> << >>etc.) are language syntax, not builtins; see language-reference.md. - Idioms: patterns.md.
- Real-world example:
contrib/json/json.merecombines many stdlib functions in a 140-line JSON parser (promoted fromexamples/tocontrib/in Phase 40).