# > | = ~ @ ?

Standard Library

Built into the compiler — no imports needed. Every function here is available in every .runa file. User-defined functions with the same name shadow builtins.


Display

FunctionSignatureDescription
showa -> StringConvert any value to its string representation
printString -> ()Print to stdout (use via @ print(...))
@ print(show(42))           -- "42"
@ print(show([1, 2, 3]))    -- "[1, 2, 3]"

Math

FunctionSignatureDescription
absInt -> IntAbsolute value
sqrtFloat -> FloatSquare root
pow(Float, Float) -> FloatExponentiation
expFloat -> FloatNatural exponential (e^x)
lnFloat -> FloatNatural logarithm
roundFloat -> IntRound to nearest integer
floorFloat -> IntFloor (round down)
to_floatInt -> FloatConvert integer to float
max_int(Int, Int) -> IntMaximum of two integers
min_int(Int, Int) -> IntMinimum of two integers
max_f(Float, Float) -> FloatMaximum of two floats
min_f(Float, Float) -> FloatMinimum of two floats
clamp(Int, Int, Int) -> IntClamp value to range [lo, hi]
= x = abs(-7)               -- 7
= r = sqrt(16.0)            -- 4.0
= p = pow(2.0, 10.0)        -- 1024.0
= n = round(3.7)            -- 4
= f = floor(3.7)            -- 3
= c = clamp(15, 0, 10)      -- 10
= big = max_int(3, 7)       -- 7

String Operations

String positions and lengths use Unicode scalar values, matching Rust char iteration. They are not UTF-8 byte offsets and not grapheme clusters: string_length("å🙂b") == 3, char_at("å🙂b", 1) == "🙂", and index_of("å🙂b", "b") == 2. When length is applied to a String, it has the same scalar-count behavior as string_length; for lists, length still counts elements. substring clamps negative starts/lengths to zero and stops at the string end. char_at returns "" when the index is out of range.

FunctionSignatureDescription
string_lengthString -> IntNumber of Unicode scalar values in string
split(String, String) -> List(String)Split by separator
join(List(String), String) -> StringJoin with separator
trimString -> StringRemove leading/trailing whitespace
contains(String, String) -> BoolSubstring test
starts_with(String, String) -> BoolPrefix test
ends_with(String, String) -> BoolSuffix test
replace(String, String, String) -> StringReplace all occurrences
to_upperString -> StringConvert to uppercase
to_lowerString -> StringConvert to lowercase
substring(String, Int, Int) -> StringExtract by scalar start index and scalar length
char_at(String, Int) -> StringSingle Unicode scalar value by index
index_of(String, String) -> IntFind substring scalar position (-1 if absent)
format_float(Float, Int) -> StringFormat float with N decimal places
parse_intString -> IntParse string to integer (0 on failure)
parse_floatString -> FloatParse string to float (0.0 on failure)
string_charsString -> List(String)Explode into Unicode scalar values
= parts = split("a,b,c", ",")         -- ["a", "b", "c"]
= joined = join(["x", "y"], "-")       -- "x-y"
= clean = trim("  hello  ")            -- "hello"
= has = contains("hello world", "world")  -- true
= upper = to_upper("hello")            -- "HELLO"
= sub = substring("the cat sat", 4, 5)  -- "cat s"
= ch = char_at("hello", 0)             -- "h"
= pos = index_of("hello", "ll")        -- 2
= fmt = format_float(3.14159, 2)        -- "3.14"
= n = parse_int("42")                  -- 42
= chars = string_chars("abc")           -- ["a", "b", "c"]
= replaced = replace("foo bar foo", "foo", "baz")  -- "baz bar baz"

List Operations

FunctionSignatureDescription
lengthList(a) -> IntList length
headList(a) -> aFirst element
tailList(a) -> List(a)All but first
push(List(a), a) -> List(a)Append element
concat(List(a), List(a)) -> List(a)Concatenate two lists
reverseList(a) -> List(a)Reverse a list
map(List(a), a -> b) -> List(b)Transform each element
filter(List(a), a -> Bool) -> List(a)Keep elements matching predicate
foldl(List(a), b, (b, a) -> b) -> bLeft fold
range(Int, Int) -> List(Int)Range [start, end)
= xs = [3, 1, 4, 1, 5]
= len = length(xs)                     -- 5
= first = head(xs)                     -- 3
= rest = tail(xs)                      -- [1, 4, 1, 5]
= bigger = push(xs, 9)                 -- [3, 1, 4, 1, 5, 9]
= both = concat([1, 2], [3, 4])        -- [1, 2, 3, 4]
= rev = reverse(xs)                    -- [5, 1, 4, 1, 3]
= doubled = map(xs, |x| x * 2)        -- [6, 2, 8, 2, 10]
= big = filter(xs, |x| x > 2)         -- [3, 4, 5]
= total = foldl(xs, 0, |acc, x| acc + x)  -- 14
= nums = range(1, 5)                   -- [1, 2, 3, 4]

head([]) is a runtime error. tail([]) returns [].

Out-of-range direct indexing (xs[i]) and nth(xs, i) are runtime errors.


Collection Operations (Kotlin-inspired)

Higher-order operations on lists. All work in both interpreter and compiled mode.

FunctionSignatureDescription
sortList(a) -> List(a)Sort by string representation (lexicographic)
sort_by(List(a), a -> b) -> List(a)Sort by key function
any(List(a), a -> Bool) -> BoolTrue if any element matches
all(List(a), a -> Bool) -> BoolTrue if all elements match
find(List(a), a -> Bool) -> Option(a)First matching element
flat_map(List(a), a -> List(b)) -> List(b)Map then flatten
zip(List(a), List(b)) -> List(Pair(a, b))Pair elements from two lists
enumerateList(a) -> List(Pair(Int, a))Index-value pairs
take_while(List(a), a -> Bool) -> List(a)Take while predicate holds
drop_while(List(a), a -> Bool) -> List(a)Drop while predicate holds
sum_listList(Int) -> IntSum of integer list
distinctList(a) -> List(a)Remove duplicates (preserves order)
count_by(List(a), a -> Bool) -> IntCount elements matching predicate
partition(List(a), a -> Bool) -> (List(a), List(a))Split by predicate
chunked(List(a), Int) -> List(List(a))Split into chunks of size N
subscribe(List(a), a -> ()) -> ()Iterate and apply callback
= xs = [5, 2, 8, 1, 9, 3]

= sorted = sort(xs)                       -- [1, 2, 3, 5, 8, 9]
= words = sort(["banana", "apple", "cherry"])  -- ["apple", "banana", "cherry"]
-- Note: sort is lexicographic. sort([10, 2, 3]) gives [10, 2, 3] because "10" < "2".
-- Use sort_by for numeric sorting: sort_by([10, 2, 3], |x| x) sorts numerically.
= has_big = any(xs, |x| x > 7)            -- true
= all_pos = all(xs, |x| x > 0)            -- true
= found = find(xs, |x| x > 4)             -- Some(5)
= total = sum_list(xs)                     -- 28
= uniq = distinct([1, 2, 2, 3, 1])        -- [1, 2, 3]
= evens = count_by(xs, |x| x % 2 == 0)   -- 2
= pairs = zip(["a", "b"], [1, 2])          -- [("a", 1), ("b", 2)]
= indexed = enumerate(["x", "y", "z"])     -- [(0, "x"), (1, "y"), (2, "z")]
= prefix = take_while(xs, |x| x > 1)      -- [5, 2, 8]
= chunks = chunked([1,2,3,4,5], 2)         -- [[1,2], [3,4], [5]]
= halves = partition(xs, |x| x > 4)        -- ([5, 8, 9], [2, 1, 3])

Tuple Accessors

FunctionSignatureDescription
fst(a, b) -> aFirst element of a tuple/pair
snd(a, b) -> bSecond element of a tuple/pair
trd(a, b, c) -> cThird element of a tuple
= p = (10, "hello")
= x = fst(p)          -- 10
= y = snd(p)          -- "hello"
= r = (10, "hello", true)
= z = trd(r)          -- true

Map Operations

FunctionSignatureDescription
map_new() -> Map(k, v)Create empty map
map_insert(Map(k, v), k, v) -> Map(k, v)Return new map with key→value added
map_get(Map(k, v), k) -> Option(v)Lookup key, return Some(v) or None
map_get_or(Map(k, v), k, v) -> vLookup key with default if missing
map_contains(Map(k, v), k) -> BoolCheck if key exists
map_remove(Map(k, v), k) -> Map(k, v)Return new map without key
map_keysMap(k, v) -> List(k)All keys as a list
map_valuesMap(k, v) -> List(v)All values as a list
map_entriesMap(k, v) -> List((k, v))All entries as list of tuples
map_lenMap(k, v) -> IntNumber of entries
map_merge(Map(k, v), Map(k, v)) -> Map(k, v)Merge maps (second overwrites first)
map_fromList((k, v)) -> Map(k, v)Create map from list of tuples
= m = map_new()
= m = map_insert(m, "name", "Alice")
= m = map_insert(m, "age", "30")
@ print(map_get_or(m, "name", "?"))       -- Alice
@ print(map_contains(m, "age"))           -- true
@ print(map_len(m))                       -- 2
= keys = map_keys(m)                      -- ["name", "age"]

Set Operations

FunctionSignatureDescription
set_new() -> Set(a)Create empty set
set_insert(Set(a), a) -> Set(a)Return new set with value added (dedup)
set_contains(Set(a), a) -> BoolCheck membership
set_remove(Set(a), a) -> Set(a)Return new set without value
set_lenSet(a) -> IntNumber of elements
set_to_listSet(a) -> List(a)Convert to list
set_union(Set(a), Set(a)) -> Set(a)Union of two sets
set_intersect(Set(a), Set(a)) -> Set(a)Intersection
set_diff(Set(a), Set(a)) -> Set(a)Difference (first minus second)
set_from_listList(a) -> Set(a)Create set from list (deduplicates)
= s = set_from_list(["red", "green", "blue", "red"])
@ print(set_len(s))                       -- 3
@ print(set_contains(s, "green"))         -- true
= s2 = set_insert(s, "yellow")           -- 4 elements
= colors = set_to_list(s2)               -- list of all colors

Stream Operators

These operators work on reactive streams (declared with ~). They complement the core stream operators (map, filter, scan, merge, zip, etc.) documented in streams.md.

FunctionSignatureDescription
tap(Stream(a), a -> ()) -> Stream(a)Side-effect observation: calls fn for each element, returns stream unchanged
catch(Stream(a), Err -> Stream(a)) -> Stream(a)Error recovery: in sync mode, pass-through (no errors in Vec)
firstStream(a) -> aFirst element; raises first: empty list when empty
reduce(Stream(a), b, (b, a) -> b) -> bTerminal fold: reduce stream to a single value
start_with(Stream(a), a) -> Stream(a)Prepend a value to the front of a stream
concat(Stream(a), Stream(a)) -> Stream(a)Concatenate two streams sequentially
pairwiseStream(a) -> Stream((a, a))Emit consecutive pairs: [1,2,3] becomes [(1,2),(2,3)]
~ nums = from_list([1, 2, 3, 4, 5])

~ observed = nums |> tap(|x| @ print("saw: " + show(x)))  -- prints each, returns stream
= head = nums |> first                                     -- 1
= total = nums |> reduce(0, |acc, x| acc + x)              -- 15
~ prefixed = nums |> start_with(0)                          -- [0, 1, 2, 3, 4, 5]
~ both = concat(from_list([1, 2]), from_list([3, 4]))       -- [1, 2, 3, 4]
~ pairs = nums |> pairwise                                  -- [(1,2), (2,3), (3,4), (4,5)]
~ firsts = pairs |> map(|p| fst(p))                         -- [1, 2, 3, 4]

Option / Result

FunctionSignatureDescription
unwrap_or(Option(a), a) -> aUnwrap with default
is_someOption(a) -> BoolTrue if Some
is_noneOption(a) -> BoolTrue if None
= val = unwrap_or(Some(42), 0)     -- 42
= val2 = unwrap_or(None, 0)        -- 0
= check = is_some(Some("hi"))      -- true

For error propagation, use monadic bind:

> safe_divide(a: Int, b: Int) -> Result(Int, String) {
    if b == 0 { Err("division by zero") }
    else { Ok(a / b) }
}

> compute() -> Result(Int, String) {
    = x <- safe_divide(10, 2)    -- unwraps Ok, early-returns Err
    = y <- safe_divide(x, 0)     -- early-returns Err("division by zero")
    Ok(x + y)
}

Logic

FunctionSignatureDescription
notBool -> BoolLogical NOT
assertBool -> ()Runtime assertion (panics on false)
identitya -> aIdentity function
= flag = not(true)          -- false
assert(2 + 2 == 4)          -- passes
= x = identity(42)          -- 42

File I/O

File operations are invoked with the @ rune (effect boundary).

FunctionSignatureDescription
read_fileString -> StringRead entire file contents
write_file(String, String) -> ()Write/overwrite file
append_file(String, String) -> ()Append to file
file_existsString -> BoolCheck if file exists
read_linesString -> List(String)Read file as list of lines
env_varString -> StringRead environment variable
@ write_file("output.txt", "hello world")
= exists = file_exists("output.txt")       -- true
= content = read_file("output.txt")        -- "hello world"
= lines = read_lines("output.txt")         -- ["hello world"]
@ append_file("output.txt", "\nline 2")
= home = env_var("HOME")                   -- "/Users/..."

Process Execution

Process execution is argv-based, not shell-based.

FunctionSignatureDescription
process_runList(String) -> (Int, String, String)Run argv without a shell, returning exit code, stdout, stderr
= result = process_run(["git", "status", "--short"])
= code = result.0
= out = result.1
= err = result.2

process_run does not invoke a shell. Pass the executable and each argument as separate list elements.


JSON

JSON values are represented as String (serialized JSON text). Auto-adds serde_json dependency on first use.

FunctionSignatureDescription
json_parseString -> StringValidate and return JSON string
json_get(String, String) -> StringAccess object field (returns JSON text)
json_stringString -> StringExtract string value (unquoted)
json_numberString -> FloatExtract number
json_boolString -> BoolExtract boolean
json_arrayString -> List(String)Extract array elements as JSON strings
json_emitString -> StringPass through (identity for JSON)
json_objectList(List(String)) -> StringBuild JSON from key-value pairs
= raw = "{\"name\": \"Alice\", \"age\": 30, \"tags\": [\"dev\", \"runa\"]}"
= parsed = json_parse(raw)
= name = json_string(json_get(parsed, "name"))    -- "Alice"
= age = json_number(json_get(parsed, "age"))       -- 30.0
= tags = json_array(json_get(parsed, "tags"))       -- ["\"dev\"", "\"runa\""]

-- Build JSON
= obj = json_object([["city", "\"Copenhagen\""], ["temp", "22"]])
-- {"city":"Copenhagen","temp":22}

HTTP

HTTP client and server. Auto-adds ureq (client) and tiny_http (server) dependencies.

FunctionSignatureDescription
http_getString -> StringGET request, return response body
http_post(String, String) -> StringPOST with body, return response
http_serve(Int, (String, String, String) -> (Int, String, String)) -> ()Start HTTP server
http_respond(Int, String, String) -> (Int, String, String)Build response tuple
http_request_pathRequest -> StringExtract request URL path
http_request_methodRequest -> StringExtract HTTP method
http_request_bodyRequest -> StringExtract request body

Client

= body = http_get("https://httpbin.org/get")
@ print(body)

= response = http_post("https://httpbin.org/post", "{\"key\": \"value\"}")
@ print(response)

Server

@ http_serve(8080, |path, method, body| {
    if path == "/hello" {
        http_respond(200, "text/plain", "Hello from Futuruna!")
    } else {
        http_respond(404, "text/plain", "Not found")
    }
})

The handler receives three string arguments: request path, HTTP method, and request body. Return a response tuple via http_respond(status, content_type, body).


Database (SQLite)

SQLite access via rusqlite. Auto-adds dependency. Connection is thread-safe (Arc<Mutex<Connection>>).

FunctionSignatureDescription
db_openString -> DbOpen SQLite database (:memory: for in-memory)
db_exec(Db, String) -> ()Execute DDL/DML (CREATE, INSERT, UPDATE, DELETE)
db_query(Db, String) -> List(List(String))Query all rows
db_query_row(Db, String) -> List(String)Query single row
db_insert(Db, String) -> IntInsert and return last row ID
db_closeDb -> ()Close database connection
= db = db_open(":memory:")

@ db_exec(db, "CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, age INTEGER)")
@ db_exec(db, "INSERT INTO users (name, age) VALUES ('Alice', 30)")
@ db_exec(db, "INSERT INTO users (name, age) VALUES ('Bob', 25)")

= rows = db_query(db, "SELECT name, age FROM users")
for row in rows {
    @ print(show(row))    -- ["Alice", "30"], ["Bob", "25"]
}

= one = db_query_row(db, "SELECT name FROM users WHERE age = 30")
@ print(show(one))        -- ["Alice"]

@ db_close(db)

Concurrency

FunctionSignatureDescription
spawn(Actor, a) -> ActorHandleCreate actor with initial state
ask(ActorHandle, Msg) -> aSend message, get response
shareda -> shared(a)Wrap value in Arc for thread-safe sharing

Actors are defined with > actor, messages sent with <-:

> actor counter(state: Int) {
    | Increment -> state + 1
    | Decrement -> state - 1
    | Reset -> 0
}

= c = spawn(counter, 0)
c <- Increment
c <- Increment
= val = ask(c, Increment)
@ print(show(val))            -- 3

Comptime

FunctionSignatureDescription
struct_type(String, List(Field)) -> TypeDefGenerate struct type at compile time
enum_type(String, List(String)) -> TypeDefGenerate enum type at compile time
field(String, String) -> FieldBuild a field descriptor
@ comptime = MyPoint = struct_type("MyPoint", [field("x", "Float"), field("y", "Float")])
@ comptime = Color = enum_type("Color", ["Red", "Green", "Blue"])

These are comptime-only functions — they generate real Rust types (structs/enums) at compile time.