Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Standard Library Reference

All standard library functions are available in the prelude without import.

String Functions

Construction

str(value) -> String

Convert any value to its string representation.

str(42)        // "42"
str(true)      // "true"
str([1, 2, 3]) // "[1, 2, 3]"

repeat(s, n) -> String

Repeat a string n times.

repeat("ab", 3)  // "ababab"
repeat("-", 10)  // "----------"

Inspection

len(s) -> Int

Get the length of a string in characters (Unicode-aware).

len("hello")  // 5
len("héllo")  // 5 (not bytes!)
len("")       // 0

is_empty(s) -> Bool

Check if a string is empty.

is_empty("")      // true
is_empty("hello") // false

contains(s, sub) -> Bool

Check if a string contains a substring.

contains("hello world", "world")  // true
contains("hello", "xyz")          // false

starts_with(s, prefix) -> Bool

Check if a string starts with a prefix.

starts_with("hello", "hel")  // true
starts_with("hello", "world") // false

ends_with(s, suffix) -> Bool

Check if a string ends with a suffix.

ends_with("hello.txt", ".txt")  // true
ends_with("hello", "world")     // false

index_of(s, sub) -> Option<Int>

Find the index of a substring. Returns None if not found.

index_of("hello", "ll")   // Some(2)
index_of("hello", "xyz")  // None

Transformation

trim(s) -> String

Remove whitespace from both ends.

trim("  hello  ")  // "hello"
trim("\n\thi\n")   // "hi"

trim_start(s) -> String

Remove whitespace from the start.

trim_start("  hello")  // "hello"

trim_end(s) -> String

Remove whitespace from the end.

trim_end("hello  ")  // "hello"

to_upper(s) -> String

Convert to uppercase.

to_upper("hello")  // "HELLO"

to_lower(s) -> String

Convert to lowercase.

to_lower("HELLO")  // "hello"

replace(s, from, to) -> String

Replace all occurrences of a substring.

replace("hello world", "world", "sage")  // "hello sage"
replace("aaa", "a", "b")                 // "bbb"

replace_first(s, from, to) -> String

Replace the first occurrence of a substring.

replace_first("aaa", "a", "b")  // "baa"

Splitting and Joining

split(s, delim) -> List<String>

Split a string by a delimiter.

split("a,b,c", ",")     // ["a", "b", "c"]
split("hello", "")      // ["h", "e", "l", "l", "o"]

lines(s) -> List<String>

Split a string into lines.

lines("a\nb\nc")  // ["a", "b", "c"]

join(parts, sep) -> String

Join strings with a separator.

join(["a", "b", "c"], ", ")  // "a, b, c"
join(["hello"], "-")         // "hello"

Slicing

slice(s, start, end) -> String

Extract a substring by character indices (Unicode-aware).

slice("hello", 1, 4)   // "ell"
slice("héllo", 0, 3)   // "hél"

chars(s) -> List<String>

Split a string into individual characters.

chars("hello")  // ["h", "e", "l", "l", "o"]

Parsing

parse_int(s) -> Int fails

Parse a string as an integer.

let n = try parse_int("42");   // 42
let n = try parse_int("-10");  // -10
let n = try parse_int("abc");  // Error!

parse_float(s) -> Float fails

Parse a string as a float.

let f = try parse_float("3.14");  // 3.14
let f = try parse_float("42");    // 42.0

parse_bool(s) -> Bool fails

Parse a string as a boolean.

let b = try parse_bool("true");   // true
let b = try parse_bool("false");  // false

List Functions

Construction

range(start, end) -> List<Int>

Create a list of integers from start (inclusive) to end (exclusive).

range(0, 5)   // [0, 1, 2, 3, 4]
range(1, 4)   // [1, 2, 3]

range_step(start, end, step) -> List<Int>

Create a list with a custom step.

range_step(0, 10, 2)  // [0, 2, 4, 6, 8]
range_step(10, 0, -2) // [10, 8, 6, 4, 2]

Inspection

len(list) -> Int

Get the length of a list.

len([1, 2, 3])  // 3
len([])         // 0

is_empty(list) -> Bool

Check if a list is empty.

is_empty([])       // true
is_empty([1, 2])   // false

contains(list, value) -> Bool

Check if a list contains a value.

contains([1, 2, 3], 2)  // true
contains([1, 2, 3], 5)  // false

first(list) -> Option<T>

Get the first element.

first([1, 2, 3])  // Some(1)
first([])         // None

last(list) -> Option<T>

Get the last element.

last([1, 2, 3])  // Some(3)
last([])         // None

get(list, index) -> Option<T>

Get an element by index.

get([1, 2, 3], 1)   // Some(2)
get([1, 2, 3], 10)  // None

Transformation

map(list, f) -> List<U>

Transform each element.

map([1, 2, 3], |x: Int| x * 2)  // [2, 4, 6]

filter(list, f) -> List<T>

Keep elements that satisfy a predicate.

filter([1, 2, 3, 4], |x: Int| x > 2)  // [3, 4]

reduce(list, init, f) -> U

Reduce a list to a single value.

reduce([1, 2, 3], 0, |acc: Int, x: Int| acc + x)  // 6

flat_map(list, f) -> List<U>

Map and flatten.

flat_map([1, 2], |x: Int| [x, x * 10])  // [1, 10, 2, 20]

flatten(list) -> List<T>

Flatten a list of lists.

flatten([[1, 2], [3, 4]])  // [1, 2, 3, 4]

Ordering

sort(list) -> List<T>

Sort a list in ascending order.

sort([3, 1, 2])  // [1, 2, 3]

reverse(list) -> List<T>

Reverse a list.

reverse([1, 2, 3])  // [3, 2, 1]

Slicing

slice(list, start, end) -> List<T>

Extract a sublist.

slice([1, 2, 3, 4, 5], 1, 4)  // [2, 3, 4]

take(list, n) -> List<T>

Take the first n elements.

take([1, 2, 3, 4], 2)  // [1, 2]

drop(list, n) -> List<T>

Drop the first n elements.

drop([1, 2, 3, 4], 2)  // [3, 4]

Aggregation

any(list, f) -> Bool

Check if any element satisfies a predicate.

any([1, 2, 3], |x: Int| x > 2)  // true

all(list, f) -> Bool

Check if all elements satisfy a predicate.

all([1, 2, 3], |x: Int| x > 0)  // true

count(list, f) -> Int

Count elements satisfying a predicate.

count([1, 2, 3, 4], |x: Int| x > 2)  // 2

sum(list) -> Int

Sum integers.

sum([1, 2, 3])  // 6

sum_float(list) -> Float

Sum floats.

sum_float([1.5, 2.5])  // 4.0

Mutation Helpers

push(list, value) -> List<T>

Add an element to the end (returns new list).

push([1, 2], 3)  // [1, 2, 3]

concat(a, b) -> List<T>

Concatenate two lists.

concat([1, 2], [3, 4])  // [1, 2, 3, 4]

unique(list) -> List<T>

Remove duplicates.

unique([1, 2, 2, 3, 1])  // [1, 2, 3]

zip(a, b) -> List<(T, U)>

Combine two lists into pairs.

zip([1, 2], ["a", "b"])  // [(1, "a"), (2, "b")]

enumerate(list) -> List<(Int, T)>

Pair each element with its index.

enumerate(["a", "b"])  // [(0, "a"), (1, "b")]

Math Functions

Basic

abs(n) -> Int

Absolute value of an integer.

abs(-5)  // 5
abs(5)   // 5

abs_float(n) -> Float

Absolute value of a float.

abs_float(-3.14)  // 3.14

min(a, b) -> Int

Minimum of two integers.

min(3, 7)  // 3

max(a, b) -> Int

Maximum of two integers.

max(3, 7)  // 7

min_float(a, b) -> Float

Minimum of two floats.

max_float(a, b) -> Float

Maximum of two floats.

clamp(value, low, high) -> Int

Clamp a value to a range.

clamp(5, 0, 10)   // 5
clamp(-5, 0, 10)  // 0
clamp(15, 0, 10)  // 10

Rounding

floor(n) -> Int

Round down to nearest integer.

floor(3.7)   // 3
floor(-3.7)  // -4

ceil(n) -> Int

Round up to nearest integer.

ceil(3.2)   // 4
ceil(-3.2)  // -3

round(n) -> Int

Round to nearest integer.

round(3.5)  // 4
round(3.4)  // 3

Powers and Roots

pow(base, exp) -> Int

Integer power.

pow(2, 10)  // 1024
pow(3, 3)   // 27

pow_float(base, exp) -> Float

Float power.

pow_float(2.0, 0.5)  // 1.414...

sqrt(n) -> Float

Square root.

sqrt(16.0)  // 4.0
sqrt(2.0)   // 1.414...

log(n) -> Float

Natural logarithm.

log(E)  // 1.0

log2(n) -> Float

Base-2 logarithm.

log2(8.0)  // 3.0

log10(n) -> Float

Base-10 logarithm.

log10(100.0)  // 2.0

Conversion

int_to_float(n) -> Float

Convert integer to float.

int_to_float(42)  // 42.0

float_to_int(n) -> Int

Convert float to integer (truncates).

float_to_int(3.9)  // 3

Constants

const PI: Float = 3.141592653589793
const E: Float = 2.718281828459045

I/O Functions

File Operations

read_file(path) -> String fails

Read entire file contents.

let contents = try read_file("data.txt");

write_file(path, content) fails

Write string to file (creates or truncates).

try write_file("output.txt", "Hello, world!");

append_file(path, content) fails

Append string to file.

try append_file("log.txt", "New entry\n");

file_exists(path) -> Bool

Check if a file or directory exists.

if file_exists("config.json") {
    // ...
}

delete_file(path) fails

Delete a file.

try delete_file("temp.txt");

list_dir(path) -> List<String> fails

List directory contents.

let files = try list_dir(".");

make_dir(path) fails

Create a directory (and parents).

try make_dir("output/data");

Standard Streams

read_line() -> String fails

Read a line from stdin.

print("Enter your name: ");
let name = try read_line();

read_all() -> String fails

Read all input from stdin until EOF.

let input = try read_all();

Time Functions

now_ms() -> Int

Current time in milliseconds since Unix epoch.

let timestamp = now_ms();

now_s() -> Int

Current time in seconds since Unix epoch.

let timestamp = now_s();

format_timestamp(ms, fmt) -> String

Format a timestamp.

format_timestamp(now_ms(), "%Y-%m-%d")  // "2024-01-15"
format_timestamp(now_ms(), "%H:%M:%S")  // "10:30:45"

Format codes:

  • %Y — year (4 digits)
  • %m — month (01-12)
  • %d — day (01-31)
  • %H — hour (00-23)
  • %M — minute (00-59)
  • %S — second (00-59)
  • %F — ISO date (YYYY-MM-DD)
  • %T — ISO time (HH:MM:SS)

parse_timestamp(s, fmt) -> Int fails

Parse a timestamp string.

let ms = try parse_timestamp("2024-01-15 10:30:00 +0000", "%Y-%m-%d %H:%M:%S %z");

Constants

const MS_PER_SECOND: Int = 1000
const MS_PER_MINUTE: Int = 60000
const MS_PER_HOUR: Int = 3600000
const MS_PER_DAY: Int = 86400000

Option Functions

is_some(opt) -> Bool

Check if option has a value.

is_some(Some(42))  // true
is_some(None)      // false

is_none(opt) -> Bool

Check if option is empty.

is_none(None)      // true
is_none(Some(42))  // false

unwrap(opt) -> T fails

Extract value or fail.

let x = try unwrap(Some(42));  // 42
let y = try unwrap(None);      // Error!

unwrap_or(opt, default) -> T

Extract value or return default.

unwrap_or(Some(42), 0)  // 42
unwrap_or(None, 0)      // 0

unwrap_or_else(opt, f) -> T

Extract value or compute default.

unwrap_or_else(None, || expensive_default())

map_option(opt, f) -> Option<U>

Transform the value if present.

map_option(Some(2), |x: Int| x * 2)  // Some(4)
map_option(None, |x: Int| x * 2)     // None

str_truncate(s, max_len) -> String

Truncate a string to a maximum length, appending “…” if truncated. Unicode-aware.

str_truncate("hello", 10)          // "hello" (no truncation)
str_truncate("hello world", 8)     // "hello..." (5 chars + "...")
str_truncate("hello", 5)           // "hello" (exact length, no truncation)
str_truncate("héllo wörld", 8)     // "héllo..." (Unicode-aware)

Environment Functions

env(key) -> Option<String>

Get an environment variable. Returns None if not set.

let home = env("HOME");          // Some("/Users/alice")
let missing = env("NONEXISTENT"); // None

env_or(key, default) -> String

Get an environment variable, returning a default if not set.

let port = env_or("PORT", "8080");       // "8080" if PORT not set
let home = env_or("HOME", "/home/user"); // actual HOME value

JSON Functions

json_parse(s) -> String fails

Validate JSON and return if valid.

let json = try json_parse("{\"name\": \"Alice\"}");

json_get(json, key) -> Option<String>

Get a field as a string.

json_get("{\"name\": \"Alice\"}", "name")  // Some("Alice")
json_get("{\"age\": 30}", "name")          // None

json_get_int(json, key) -> Option<Int>

Get a field as an integer.

json_get_int("{\"age\": 30}", "age")  // Some(30)

json_get_float(json, key) -> Option<Float>

Get a field as a float.

json_get_float("{\"price\": 9.99}", "price")  // Some(9.99)

json_get_bool(json, key) -> Option<Bool>

Get a field as a boolean.

json_get_bool("{\"active\": true}", "active")  // Some(true)

json_get_list(json, key) -> Option<List<String>>

Get a field as a list of strings.

json_get_list("{\"tags\": [\"a\", \"b\"]}", "tags")  // Some(["a", "b"])

json_stringify(value) -> String

Convert a value to JSON string.

json_stringify("hello")  // "\"hello\""

json_escape(s) -> String

Escape JSON special characters in a string without wrapping in quotes.

json_escape("hello")            // "hello"
json_escape("say \"hi\"")       // "say \\\"hi\\\""
json_escape("line\nbreak")      // "line\\nbreak"
json_escape("tab\there")        // "tab\\there"

Generic Deserialization

Note: Generic from_json<T> deserialization is not currently available in Sage. This would require runtime type information, which isn’t supported by the current architecture where Sage compiles to Rust with monomorphised generics.

Workaround: Use the json_get_* functions to extract typed fields from JSON strings:

// Instead of: let user: User = from_json(json);
// Do this:
let name = unwrap_or(json_get(json, "name"), "");
let age = unwrap_or(json_get_int(json, "age"), 0);
let active = unwrap_or(json_get_bool(json, "active"), false);

// Build your record manually
let user = User { name: name, age: age, active: active };

For complex nested structures, extract fields level by level or use Oracle<T> with LLM parsing if appropriate.


Map Functions

map_get(map, key) -> Option<V>

Get a value by key.

let ages = {"alice": 30, "bob": 25};
map_get(ages, "alice")  // Some(30)
map_get(ages, "charlie") // None

map_set(map, key, value)

Set a key-value pair (mutates map).

let ages = {"alice": 30};
map_set(ages, "bob", 25);

map_has(map, key) -> Bool

Check if key exists.

map_has({"a": 1}, "a")  // true
map_has({"a": 1}, "b")  // false

map_delete(map, key)

Remove a key (mutates map).

let m = {"a": 1, "b": 2};
map_delete(m, "a");

map_keys(map) -> List<K>

Get all keys.

map_keys({"a": 1, "b": 2})  // ["a", "b"]

map_values(map) -> List<V>

Get all values.

map_values({"a": 1, "b": 2})  // [1, 2]

Output

print(message)

Print to stdout with newline.

print("Hello, world!");
print("Value: " ++ str(42));