AGL Standard Library Reference

1. I/O

print

print(args...) -> nil

Prints each argument to stdout, separated by nothing, followed by a newline. Accepts any number of arguments of any type. Arrays are printed as [a, b, c], structs as <struct TypeName>, results as ok(...) or err(...).

print("hello")          // hello
print(42)               // 42
print("x =", 10)        // x =10
print([1, 2, 3])        // [1, 2, 3]

Errors: None.


2. Type Inspection

type

type(val: any) -> string

Returns the type name of val as a string.

type(42)            // "int"
type(3.14)          // "float"
type(true)          // "bool"
type("hello")       // "string"
type([1, 2])        // "array"
type(fn() {})       // "fn"
type(ok(1))         // "result"
type(nil)           // "nil"
type({})            // "map"

Errors: Requires exactly 1 argument.

len

len(x: array | string | map) -> int

Returns the number of elements in an array or map, or the length of a string.

len([1, 2, 3])      // 3
len("hello")         // 5
len([])              // 0
// Practical: validate input length before processing
let password = "abc"
if len(password) < 8 {
    print("Password must be at least 8 characters")
}

let items = ["a", "b", "c"]
print(f"Processing {len(items)} items")  // Processing 3 items

Errors:

  • Requires exactly 1 argument.
  • TypeError if argument is not an array, string, or map.

3. Type Conversion

str

str(val: any) -> string

Converts any value to its string representation. If the value is already a string, returns it unchanged.

str(42)             // "42"
str(3.14)           // "3.14"
str(true)           // "true"
str(nil)            // "nil"
str([1, 2])         // "<array[2]>"
// Practical: build log messages from mixed types
let count = 42
let msg = "Found " + str(count) + " results"
print(msg)          // Found 42 results

Errors: Requires exactly 1 argument.

int

int(x: string | float | int) -> int

Converts a value to an integer. Strings are parsed as base-10 integers. Floats are truncated toward zero. Integers are returned unchanged.

int("123")          // 123
int(3.7)            // 3
int(42)             // 42
// Practical: parse numeric user input
let port_str = "8080"
let port = int(port_str)
print(f"Listening on port {port}")  // Listening on port 8080

Errors:

  • Requires exactly 1 argument.
  • RuntimeError if string is not a valid integer ("abc", "").
  • TypeError if argument is not a string, float, or int.

float

float(x: string | int | float) -> float

Converts a value to a float. Strings are parsed as floating-point numbers. Integers are widened. Floats are returned unchanged.

float("2.5")        // 2.5
float(42)           // 42.0
float(3.14)         // 3.14

Errors:

  • Requires exactly 1 argument.
  • RuntimeError if string is not a valid number.
  • TypeError if argument is not a string, int, or float.

4. Arrays

push

push(arr: array, val: any) -> array

Returns a new array with val appended to the end of arr. The original array is not modified.

let a = [1, 2, 3]
let b = push(a, 4)     // [1, 2, 3, 4]
print(a)                // [1, 2, 3]  (unchanged)
// Practical: accumulate results in a loop
let results = []
for i in [1, 2, 3, 4, 5] {
    if i % 2 == 0 {
        let results = push(results, i * 10)
    }
}
print(results)          // [20, 40]

Errors:

  • Requires exactly 2 arguments.
  • TypeError if first argument is not an array.
  • RuntimeError if array size would exceed 1024 elements.

map

map(arr: array, fn: fn) -> array

Returns a new array where each element is the result of calling fn on the corresponding element of arr.

let nums = [1, 2, 3]
let doubled = map(nums, fn(x: int) -> int { return x * 2 })
print(doubled)          // [2, 4, 6]
// Practical: transform string data
let names = ["alice", "bob", "charlie"]
let upper = map(names, fn(n: string) -> string { return to_upper(n) })
print(upper)            // ["ALICE", "BOB", "CHARLIE"]

// Practical: extract fields from structured data
let prices = [100, 250, 50]
let with_tax = map(prices, fn(p: int) -> float {
    return float(p) * 1.1
})
print(with_tax)         // [110.0, 275.0, 55.0]

Errors:

  • Requires exactly 2 arguments.
  • TypeError if first argument is not an array or second is not a function.
  • Propagates any error raised by fn.

filter

filter(arr: array, fn: fn) -> array

Returns a new array containing only the elements of arr for which fn returns a truthy value.

let nums = [1, 2, 3, 4, 5]
let evens = filter(nums, fn(x: int) -> bool { return x % 2 == 0 })
print(evens)            // [2, 4]
// Practical: filter strings by condition
let names = ["alice", "bob", "charlie", "dan"]
let long_names = filter(names, fn(n: string) -> bool { return len(n) > 3 })
print(long_names)       // ["alice", "charlie"]

// Practical: chain map and filter
let scores = [85, 42, 91, 67, 73, 55]
let passing = filter(scores, fn(s: int) -> bool { return s >= 70 })
let grades = map(passing, fn(s: int) -> string {
    if s >= 90 { return "A" }
    if s >= 80 { return "B" }
    return "C"
})
print(grades)           // ["B", "A", "C"]

Errors:

  • Requires exactly 2 arguments.
  • TypeError if first argument is not an array or second is not a function.
  • Propagates any error raised by fn.

5. Strings

split

split(s: string, sep: string) -> array

Splits a string into an array of substrings at each occurrence of sep.

split("a,b,c", ",")    // ["a", "b", "c"]
split("hello world", " ")  // ["hello", "world"]
// Practical: parse CSV-style data
let csv = "name,age,city"
let headers = split(csv, ",")
print(headers)          // ["name", "age", "city"]
print(len(headers))     // 3

// Practical: parse key=value pairs
let line = "host=localhost"
let parts = split(line, "=")
print(parts[0])         // host
print(parts[1])         // localhost

Errors:

  • Requires exactly 2 arguments.
  • TypeError if either argument is not a string.
  • RuntimeError if separator is empty.

trim

trim(s: string) -> string

Removes leading and trailing whitespace (spaces, tabs, newlines, carriage returns) from a string.

trim("  hello  ")       // "hello"
trim("\thello\n")        // "hello"
// Practical: clean user input or command output
let text = "  Hello, World!  "
print(trim(text))       // Hello, World!

let raw = "\n  some data \r\n"
let clean = trim(raw)
print(clean)            // some data

Errors:

  • Requires exactly 1 argument.
  • TypeError if argument is not a string.

contains

contains(s: string, sub: string) -> bool

Returns true if s contains the substring sub, false otherwise. An empty substring always returns true.

contains("hello world", "world")   // true
contains("hello world", "xyz")     // false
contains("hello", "")              // true
// Practical: search and branch on content
let log_line = "2026-03-25 ERROR: connection refused"
if contains(log_line, "ERROR") {
    print("Error detected in log")
}

Errors:

  • Requires exactly 2 arguments.
  • TypeError if either argument is not a string.

replace

replace(s: string, old: string, new: string) -> string

Returns a new string with all occurrences of old replaced by new. If old is empty, returns the original string unchanged.

replace("hello world", "world", "ago")   // "hello ago"
replace("aaa", "a", "bb")                // "bbbbbb"
// Practical: URL and path manipulation
let url = "https://example.com/path"
print(replace(url, "https", "http"))  // http://example.com/path

// Practical: template substitution
let template = "Hello, {name}! Welcome to {place}."
let msg = replace(replace(template, "{name}", "Alice"), "{place}", "Ago")
print(msg)  // Hello, Alice! Welcome to Ago.

Errors:

  • Requires exactly 3 arguments.
  • TypeError if any argument is not a string.

starts_with

starts_with(s: string, prefix: string) -> bool

Returns true if s begins with prefix.

starts_with("hello", "hel")    // true
starts_with("hello", "xyz")    // false
// Practical: protocol detection
let url = "https://example.com/api"
if starts_with(url, "https") {
    print("Secure connection")  // Secure connection
}

Errors:

  • Requires exactly 2 arguments.
  • TypeError if either argument is not a string.

ends_with

ends_with(s: string, suffix: string) -> bool

Returns true if s ends with suffix.

ends_with("hello.txt", ".txt")   // true
ends_with("hello.txt", ".csv")   // false
// Practical: file type checking
let filename = "report.json"
if ends_with(filename, ".json") {
    print("JSON file detected")  // JSON file detected
}

Errors:

  • Requires exactly 2 arguments.
  • TypeError if either argument is not a string.

to_upper

to_upper(s: string) -> string

Returns a new string with all ASCII lowercase letters converted to uppercase.

to_upper("hello")       // "HELLO"
to_upper("Hello World") // "HELLO WORLD"
to_upper("abc123")      // "ABC123"

Errors:

  • Requires exactly 1 argument.
  • TypeError if argument is not a string.

to_lower

to_lower(s: string) -> string

Returns a new string with all ASCII uppercase letters converted to lowercase.

to_lower("HELLO")       // "hello"
to_lower("Hello World") // "hello world"
to_lower("ABC123")      // "abc123"

Errors:

  • Requires exactly 1 argument.
  • TypeError if argument is not a string.

join

join(arr: array, sep: string) -> string

Joins an array of strings into a single string with sep between each element.

join(["a", "b", "c"], ",")      // "a,b,c"
join(["hello", "world"], " ")   // "hello world"
join(["one"], "-")               // "one"
// Practical: build a CSV line
let fields = ["Alice", "30", "NYC"]
let csv_line = join(fields, ",")
print(csv_line)         // Alice,30,NYC

// Practical: build a file path
let parts = ["home", "user", "docs", "file.txt"]
let path = join(parts, "/")
print(path)             // home/user/docs/file.txt

Errors:

  • Requires exactly 2 arguments.
  • TypeError if first argument is not an array or second is not a string.
  • TypeError if any array element is not a string.

substr

substr(s: string, start: int, length: int) -> string

Returns a substring of s starting at index start with the given length. Out-of-range values are clamped to valid bounds.

substr("hello world", 0, 5)   // "hello"
substr("hello world", 6, 5)   // "world"
substr("hello", 1, 3)         // "ell"
// Practical: extract parts of a formatted string
let timestamp = "2026-03-25T14:30:00"
let date = substr(timestamp, 0, 10)
let time = substr(timestamp, 11, 8)
print(date)             // 2026-03-25
print(time)             // 14:30:00

Errors:

  • Requires exactly 3 arguments.
  • TypeError if first argument is not a string or second/third are not integers.

6. Math

abs

abs(n: int | float) -> int | float

Returns the absolute value of a number. The return type matches the input type.

abs(-42)            // 42
abs(3.14)           // 3.14
abs(-2.5)           // 2.5

Errors:

  • Requires exactly 1 argument.
  • TypeError if argument is not a number.

min

min(a: int | float, b: int | float) -> int | float

Returns the smaller of two numbers. Both arguments must be the same numeric type.

min(10, 3)          // 3
min(2.5, 1.0)       // 1.0

Errors:

  • Requires exactly 2 arguments.
  • TypeError if arguments are not two numbers of the same type (no int/float mixing).

max

max(a: int | float, b: int | float) -> int | float

Returns the larger of two numbers. Both arguments must be the same numeric type.

max(10, 3)          // 10
max(2.5, 1.0)       // 2.5

Errors:

  • Requires exactly 2 arguments.
  • TypeError if arguments are not two numbers of the same type (no int/float mixing).

7. Result Constructors

ok and err are syntax forms, not functions. They wrap a value into a result type for structured error handling.

ok

ok(expr) -> result

Wraps a value as a successful result.

err

err(expr) -> result

Wraps a value as an error result.

match

Results are unwrapped with the match expression:

fn safe_div(a: int, b: int) -> result {
    if b == 0 {
        return err("division by zero")
    }
    return ok(a / b)
}

match safe_div(10, 3) {
    ok(v) -> print(v)       // 3
    err(e) -> print(e)
}

match safe_div(10, 0) {
    ok(v) -> print(v)
    err(e) -> print(e)      // division by zero
}

Errors:

  • match requires the subject to be a result value (TypeError otherwise).

8. Maps

map_get

map_get(m: map, key: string) -> any

Returns the value associated with key in map m, or nil if the key does not exist.

let m = map_set(map_set({}, "name", "Alice"), "age", 30)
print(map_get(m, "name"))     // Alice
print(map_get(m, "age"))      // 30
print(map_get(m, "missing"))  // nil

Errors:

  • Requires exactly 2 arguments.
  • TypeError if first argument is not a map or second is not a string.

map_set

map_set(m: map, key: string, val: any) -> map

Returns a new map with key set to val. If the key already exists, its value is updated. The original map is not modified.

let m = {}
let m = map_set(m, "host", "localhost")
let m = map_set(m, "port", 8080)
print(map_get(m, "host"))     // localhost
print(map_get(m, "port"))     // 8080
// Practical: build a configuration map step by step
let config = {}
let config = map_set(config, "host", "localhost")
let config = map_set(config, "port", 8080)
let config = map_set(config, "debug", true)
print(map_keys(config))       // ["host", "port", "debug"]

// Updating an existing key returns a new map
let config = map_set(config, "port", 9090)
print(map_get(config, "port"))  // 9090

Errors:

  • Requires exactly 3 arguments.
  • TypeError if first argument is not a map or second is not a string.

map_has

map_has(m: map, key: string) -> bool

Returns true if key exists in map m, false otherwise.

let m = map_set({}, "name", "Alice")
print(map_has(m, "name"))     // true
print(map_has(m, "email"))    // false
// Practical: conditional logic based on map contents
let headers = map_set({}, "Content-Type", "application/json")
if map_has(headers, "Authorization") {
    print("Authenticated request")
} else {
    print("Anonymous request")  // Anonymous request
}

Errors:

  • Requires exactly 2 arguments.
  • TypeError if first argument is not a map or second is not a string.

map_keys

map_keys(m: map) -> array

Returns an array of all keys in the map as strings.

let m = map_set(map_set({}, "a", 1), "b", 2)
print(map_keys(m))            // ["a", "b"]
print(map_keys({}))           // []
// Practical: iterate over map entries
let scores = map_set(map_set(map_set({}, "Alice", 95), "Bob", 87), "Carol", 92)
let names = map_keys(scores)
for name in names {
    print(f"{name}: {map_get(scores, name)}")
}
// Alice: 95
// Bob: 87
// Carol: 92

Errors:

  • Requires exactly 1 argument.
  • TypeError if argument is not a map.

map_del

map_del(m: map, key: string) -> map

Returns a new map with key removed. If the key does not exist, returns a copy of the original map. The original map is not modified.

let m = map_set(map_set({}, "a", 1), "b", 2)
let m2 = map_del(m, "a")
print(map_keys(m2))           // ["b"]
print(map_keys(m))            // ["a", "b"]  (unchanged)
// Practical: remove sensitive data before logging
let request = map_set(map_set({}, "user", "alice"), "token", "secret123")
let safe = map_del(request, "token")
print(safe)                   // {"user": "alice"}

Errors:

  • Requires exactly 2 arguments.
  • TypeError if first argument is not a map or second is not a string.

9. File I/O

read_file

read_file(path: string) -> result

Reads the entire contents of a file. Returns ok(content) on success or err(message) on failure.

match read_file("data.txt") {
    ok(content) -> print(content)
    err(e) -> print("Error: " + e)
}
// Practical: read, parse, and use JSON config
write_file("/tmp/config.json", "{\"key\": \"value\", \"count\": 42}")?
let content = read_file("/tmp/config.json")?
let config = json_parse(content)?
print(map_get(config, "key"))      // value
print(map_get(config, "count"))    // 42
// Practical: handle missing file gracefully
let result = read_file("/nonexistent")
let msg = match result {
    ok(data) -> data
    err(e) -> f"File error: {e}"
}
print(msg)  // File error: cannot read file

Errors (returned as err):

  • "cannot read file" – file does not exist or is not readable.
  • "file too large" – file exceeds 10 MB.

Errors (raised as runtime errors):

  • TypeError if argument is not a string.
  • Requires exactly 1 argument.

write_file

write_file(path: string, content: string) -> result

Writes content to a file, overwriting any existing content. Returns ok(true) on success or err(message) on failure.

match write_file("out.txt", "hello world") {
    ok(_) -> print("written")
    err(e) -> print("Error: " + e)
}
// Practical: write structured data to a file
let data = map_set(map_set({}, "name", "Alice"), "score", 95)
let json = json_stringify(data)
write_file("/tmp/result.json", json)?
print("Saved: " + json)  // Saved: {"name":"Alice","score":95}

Errors (returned as err):

  • "cannot write file" – path is not writable.

Errors (raised as runtime errors):

  • TypeError if either argument is not a string.
  • Requires exactly 2 arguments.

file_exists

file_exists(path: string) -> bool

Returns true if the file at path exists and is readable, false otherwise.

if file_exists("config.agl") {
    print("found config")
}
// Practical: load config with a fallback
let config_path = "settings.json"
if file_exists(config_path) {
    let raw = read_file(config_path)?
    let config = json_parse(raw)?
    print("Loaded config")
} else {
    print("Using defaults")
}

Errors:

  • Requires exactly 1 argument.
  • TypeError if argument is not a string.

10. JSON

json_parse

json_parse(s: string) -> result

Parses a JSON string into an AGL value. Objects become maps, arrays become arrays, strings/numbers/booleans/null map to their AGL equivalents. Returns a result.

let data = json_parse("{\"name\": \"Alice\", \"age\": 30}")?
print(map_get(data, "name"))   // Alice
print(map_get(data, "age"))    // 30
// Practical: parse nested JSON structures
let json = "{\"users\": [{\"name\": \"Alice\", \"age\": 30}, {\"name\": \"Bob\", \"age\": 25}]}"
let data = json_parse(json)?
let users = map_get(data, "users")
print(len(users))             // 2
print(map_get(users[0], "name"))  // Alice
print(map_get(users[1], "age"))   // 25

// Practical: parse a JSON array
let arr = json_parse("[1, 2, 3, 4, 5]")?
print(len(arr))               // 5
print(arr[0])                 // 1

Errors:

  • Requires exactly 1 argument.
  • TypeError if argument is not a string.

json_stringify

json_stringify(val: any) -> string

Converts an AGL value to a JSON string. Maps become objects, arrays become arrays, strings/numbers/booleans/nil map to their JSON equivalents.

let obj = map_set(map_set({}, "status", "ok"), "count", 42)
print(json_stringify(obj))    // {"status":"ok","count":42}
// Practical: serialize data for file storage or API calls
let payload = map_set(map_set({}, "action", "greet"), "message", "hello")
let json = json_stringify(payload)
print(json)                   // {"action":"greet","message":"hello"}

// Practical: pretty-print arrays
let items = [1, "two", true, nil]
print(json_stringify(items))  // [1,"two",true,null]

Errors:

  • Requires exactly 1 argument.

11. Environment

env

env(name: string) -> result

Reads an environment variable. Returns ok(value) if the variable is set, or err("not set") if it is not.

match env("HOME") {
    ok(home) -> print("Home: " + home)
    err(e) -> print("HOME not set")
}
// Practical: require an environment variable
let api_key = match env("API_KEY") {
    ok(key) -> key
    err(_) -> {
        print("ERROR: API_KEY must be set")
        return
    }
}
print("Using API key: " + substr(api_key, 0, 4) + "...")

Errors:

  • Requires exactly 1 argument.
  • TypeError if argument is not a string.

env_default

env_default(name: string, fallback: string) -> string

Reads an environment variable, returning fallback if the variable is not set. Unlike env(), this returns a plain string (not a result).

let host = env_default("HOST", "localhost")
let port = env_default("PORT", "8080")
print(f"Server: {host}:{port}")  // Server: localhost:8080
// Practical: application configuration from environment
let db_url = env_default("DATABASE_URL", "postgres://localhost/dev")
let debug = env_default("DEBUG", "false")
let log_level = env_default("LOG_LEVEL", "info")
print(f"DB: {db_url}")         // DB: postgres://localhost/dev
print(f"Debug: {debug}")       // Debug: false
print(f"Log level: {log_level}")  // Log level: info

Errors:

  • Requires exactly 2 arguments.
  • TypeError if either argument is not a string.

12. HTTP

http_get

http_get(url: string, headers: map) -> result

Sends an HTTP GET request. Returns ok(response_map) on success or err(message) on failure. The response map contains:

  • "status" – HTTP status code (int)
  • "body" – response body (string)
  • "headers" – response headers (map)
let resp = http_get("https://httpbin.org/get", {})?
print(map_get(resp, "status"))  // 200
print(map_get(resp, "body"))    // {"url": "https://httpbin.org/get", ...}
// Practical: API call with authentication
let headers = map_set({}, "Authorization", "Bearer token123")
let headers = map_set(headers, "Accept", "application/json")
let resp = http_get("https://api.example.com/users", headers)?

let status = map_get(resp, "status")
if status == 200 {
    let users = json_parse(map_get(resp, "body"))?
    print(f"Found {len(users)} users")
} else {
    print(f"Request failed with status {status}")
}

Errors (returned as err):

  • Network errors, DNS resolution failures, timeouts (30s), TLS errors.

Errors (raised as runtime errors):

  • Requires exactly 2 arguments.
  • TypeError if first argument is not a string or second is not a map.

Note: Requires libcurl. If not available, returns err("HTTP not available: libcurl required").

http_post

http_post(url: string, headers: map, body: string) -> result

Sends an HTTP POST request. Returns ok(response_map) on success or err(message) on failure. The response map has the same structure as http_get.

let headers = map_set({}, "Content-Type", "application/json")
let body = json_stringify(map_set({}, "name", "Alice"))
let resp = http_post("https://httpbin.org/post", headers, body)?
print(map_get(resp, "status"))  // 200
// Practical: send data to an API
let headers = map_set({}, "Content-Type", "application/json")
let headers = map_set(headers, "Authorization", "Bearer mytoken")
let payload = map_set(map_set({}, "title", "New Task"), "done", false)
let body = json_stringify(payload)

let resp = http_post("https://api.example.com/tasks", headers, body)?
let status = map_get(resp, "status")
if status == 201 {
    let created = json_parse(map_get(resp, "body"))?
    print(f"Created task with ID: {map_get(created, \"id\")}")
} else {
    print(f"Failed: HTTP {status}")
}

Errors (returned as err):

  • Same as http_get.

Errors (raised as runtime errors):

  • Requires exactly 3 arguments.
  • TypeError if first argument is not a string, second is not a map, or third is not a string.

Note: Requires libcurl. If not available, returns err("HTTP not available: libcurl required").


13. Process Execution

exec

exec(cmd: string, args: array) -> result

Executes an external command with the given arguments. Returns ok(result_map) on success or err(message) on failure. The result map contains:

  • "stdout" – captured standard output (string)
  • "stderr" – captured standard error (string)
  • "status" – exit code (int, 0 = success)
let result = exec("echo", ["hello"])?
print(map_get(result, "stdout"))   // hello\n
print(map_get(result, "status"))   // 0
// Practical: capture and use command output
let result = exec("date", ["+%Y-%m-%d"])?
let today = trim(map_get(result, "stdout"))
print(f"Today is {today}")        // Today is 2026-03-25
// Practical: check git status
let result = exec("git", ["status", "--porcelain"])?
let stdout = trim(map_get(result, "stdout"))
if map_get(result, "status") == 0 {
    if len(stdout) == 0 {
        print("Git repo is clean")
    } else {
        print("Uncommitted changes detected")
    }
}
// Practical: handle command failure
let result = exec("ls", ["/nonexistent"])
match result {
    ok(r) -> {
        if map_get(r, "status") != 0 {
            print("Command failed: " + trim(map_get(r, "stderr")))
        }
    }
    err(e) -> print("Exec error: " + e)
}

Errors (returned as err):

  • "fork failed" – system could not create a child process.
  • "failed to create pipes" – pipe creation failure.
  • "out of memory" – memory allocation failure.

Errors (raised as runtime errors):

  • Requires exactly 2 arguments.
  • TypeError if first argument is not a string or second is not an array.

14. Time

now

now() -> int

Returns the current time as Unix epoch milliseconds.

let start = now()
// ... do some work ...
let elapsed = now() - start
print(f"Took {elapsed} ms")

Errors: Requires exactly 0 arguments.

sleep

sleep(ms: int) -> nil

Pauses execution for ms milliseconds.

print("Starting...")
sleep(1000)
print("1 second later")
// Practical: retry with backoff
fn fetch_with_retry(url: string, max_retries: int) -> result {
    for i in [0, 1, 2] {
        let resp = http_get(url, {})
        let val = match resp {
            ok(r) -> {
                if map_get(r, "status") == 200 {
                    return ok(r)
                }
                nil
            }
            err(_) -> nil
        }
        if i < max_retries - 1 {
            sleep(1000 * (i + 1))
        }
    }
    return err("max retries exceeded")
}

Errors:

  • Requires exactly 1 argument.
  • TypeError if argument is not an integer.