Built-in Functions
Complete reference for all built-in tasks available in Syntecnia programs.
1. Core Functions
Core functions are always available — no capability declaration required. They cover output, collection operations, type conversion, and introspection.
print(values...)
Write one or more values to standard output, separated by spaces. Accepts any number of arguments.
print("Hello, World!")
print("Name:", name, "Age:", age)
print(items) -- prints the list representation
length(collection)
Returns the number of elements in a list or map, or the number of characters in a text value.
| Input type | Returns |
|---|---|
text | Number of characters |
list | Number of items |
map | Number of key-value pairs |
length("hello") -- 5
length([1, 2, 3]) -- 3
length({"a": 1}) -- 1
text(value)
Convert any value to its text representation. Integers are shown without a decimal point.
text(42) -- "42" (no decimal)
text(3.14) -- "3.14"
text(true) -- "true"
text(nothing) -- "nothing"
number(value)
Convert a text or boolean to a number. Always returns a float.
number("42") -- 42.0
number("3.14") -- 3.14
number(true) -- 1.0
number(false) -- 0.0
append(list, item)
Returns a new list with the item added to the end. The original list is not mutated.
let nums be [1, 2, 3]
let more be append(nums, 4)
print(more) -- [1, 2, 3, 4]
keys(map) / values(map)
Return a list of all keys or all values from a map, in insertion order.
let config be {"host": "localhost", "port": 8080}
print(keys(config)) -- ["host", "port"]
print(values(config)) -- ["localhost", 8080]
contains(collection, item)
Check whether an item exists in a list, or a key exists in a map, or a substring exists in text. Returns a boolean.
contains([1, 2, 3], 2) -- true
contains({"a": 1}, "a") -- true (key lookup)
contains("hello world", "world") -- true
split(text, separator) / join(list, separator)
split breaks a text string into a list of parts. join assembles a list of values into a single text string using the given separator.
let parts be split("a,b,c", ",")
print(parts) -- ["a", "b", "c"]
let back be join(parts, " | ")
print(back) -- "a | b | c"
range(end) / range(start, end) / range(start, end, step)
Generate a list of numbers. The end value is exclusive. Negative steps produce descending sequences.
range(5) -- [0, 1, 2, 3, 4]
range(1, 6) -- [1, 2, 3, 4, 5]
range(0, 10, 2) -- [0, 2, 4, 6, 8]
range(5, 0, -1) -- [5, 4, 3, 2, 1]
-- common pattern: iterate with index
each i in range(length(items))
print(i, items[i])
type_of(value)
Returns the runtime type of a value as a text string. Useful for dynamic dispatch and debugging.
| Value | Returns |
|---|---|
42 | "number" |
"hello" | "text" |
true | "bool" |
[1,2] | "list" |
{"a":1} | "map" |
| a task/function | "task" |
nothing | "nothing" |
print(type_of(42)) -- "number"
print(type_of("hi")) -- "text"
when type_of(val) == "list"
print("got", length(val), "items")
slice(collection, start, end?)
Return a sub-section of a list or text. start is inclusive, end is exclusive and optional (defaults to the end of the collection). Negative indices count from the end.
slice([10, 20, 30, 40], 1, 3) -- [20, 30]
slice([10, 20, 30], 1) -- [20, 30]
slice("hello", 1, 4) -- "ell"
slice("world", -3) -- "rld"
2. String Functions
Functions for text manipulation. All are pure (no capability required) and return new text values — they never mutate in place.
fmt(template, map)
Interpolate a template string using a map of named values. Placeholders use {name} syntax.
let msg be fmt("Hello {name}, you have {count} messages", {
"name": "Alice",
"count": 5
})
print(msg) -- "Hello Alice, you have 5 messages"
fmt over string concatenation when building messages with multiple dynamic parts — it is more readable and easier to localize.upper(text) / lower(text)
Convert a string to all upper-case or all lower-case characters.
upper("hello") -- "HELLO"
lower("WORLD") -- "world"
-- case-insensitive comparison
when lower(input) == "yes"
print("confirmed")
trim(text)
Remove leading and trailing whitespace (spaces, tabs, newlines) from a text value.
trim(" hello ") -- "hello"
trim("\n data\t") -- "data"
starts_with(text, prefix) / ends_with(text, suffix)
Return true if the text starts with the given prefix or ends with the given suffix.
starts_with("https://example.com", "https") -- true
ends_with("report.csv", ".csv") -- true
let secure_urls be where(urls, task(u) starts_with(u, "https"))
replace_text(text, old, new)
Replace all occurrences of a literal substring with a new value. For pattern-based replacement, see replace_re.
replace_text("foo bar foo", "foo", "baz") -- "baz bar baz"
replace_text("hello world", " ", "_") -- "hello_world"
3. Regex Functions pure
Regular-expression functions require no capability. They use Python's re module under the hood.
(a+)+) can cause catastrophically slow matching. Never use untrusted input as a pattern argument without sanitising it first. Untrusted text input is safe.
matches(text, pattern)
Full match — the entire text must conform to the pattern. Designed for input validation; an unanchored pattern is already implicitly anchored at both ends.
matches("12345", "[0-9]+") -- true (full string is digits)
matches("a 5 b", "[0-9]+") -- false (not fully digits)
-- validate an email-like format
let valid be matches(email, "[^@]+@[^@]+\.[^@]+")
when not valid
print("invalid email")
find_all(text, pattern)
Partial (substring) search — returns a list of all non-overlapping matches found anywhere in the text.
find_all("a1b2c3", "[0-9]") -- ["1", "2", "3"]
find_all("cat bat rat", "[a-z]at") -- ["cat", "bat", "rat"]
find_all("no numbers", "[0-9]+") -- []
capture(text, pattern)
Find the first match (partial search). If the pattern has capture groups, returns a list of the group values. Without groups, returns the whole match as text. Returns nothing on no match.
-- no groups: returns the matched text
capture("order-42", "[0-9]+") -- "42"
-- with groups: returns list of group values
capture("2025-06-18", "([0-9]{4})-([0-9]{2})-([0-9]{2})")
-- ["2025", "06", "18"]
-- no match
capture("hello", "[0-9]+") -- nothing
replace_re(text, pattern, replacement)
Replace all matches of a regex pattern. Supports \1, \2, … backreferences to capture groups in the replacement string.
replace_re("hello world", " +", "_")
-- "hello_world"
replace_re("2025-06-18", "([0-9]{4})-([0-9]{2})-([0-9]{2})", "\3/\2/\1")
-- "18/06/2025"
replace_re("foo123bar", "[0-9]+", "NUM")
-- "fooNUMbar"
4. Intentional Operations
These functional primitives replace explicit loops. They express what you want rather than how to iterate, making code more readable, composable, and safe for concurrent execution.
|>.
let result be orders
|> where(is_active)
|> sort_by(get_date)
|> collect("id")
apply(function, list)
Transform every element of a list by applying a function to each one. Returns a new list of the same length.
task double(x)
give x * 2
let doubled be apply(double, [1, 2, 3])
print(doubled) -- [2, 4, 6]
-- inline task
let squares be apply(task(n) n * n, range(1, 6))
print(squares) -- [1, 4, 9, 16, 25]
where(list, predicate)
Return a new list containing only the elements for which the predicate task returns true.
task is_adult(person)
give age of person >= 18
let adults be where(users, is_adult)
-- inline
let evens be where([1,2,3,4,5], task(n) n % 2 == 0)
print(evens) -- [2, 4]
collect(list, "property")
Extract a single named property from each map in a list (a "pluck"). The property name is passed as a text string.
let users be [
{"name": "Alice", "age": 30},
{"name": "Bob", "age": 25}
]
let names be collect(users, "name")
print(names) -- ["Alice", "Bob"]
transform(list, function, predicate?)
Like apply, but with an optional filter predicate. Elements that do not satisfy the predicate are passed through unchanged.
task apply_discount(product)
give {...product, "price": price of product * 0.9}
task is_expensive(product)
give price of product > 100
-- only discount products priced > 100
let discounted be transform(products, apply_discount, is_expensive)
reduce(list, function, initial)
Fold a list into a single value. The function receives the accumulator and the current element; the initial value seeds the accumulation.
task add(acc, n)
give acc + n
let total be reduce([1, 2, 3, 4], add, 0)
print(total) -- 10
-- sum of prices
let revenue be reduce(orders, task(acc, o) acc + amount of o, 0)
sort_by(list, key_function)
Return a new sorted list. The key function extracts the comparison value from each element. Sort is ascending by default.
task get_price(p)
give price of p
let sorted be sort_by(products, get_price)
-- sort by text field
let by_name be sort_by(users, task(u) name of u)
group_by(list, key_function)
Partition a list into a map where keys are produced by the key function and values are lists of elements sharing that key.
let groups be group_by(orders, task(o) status of o)
-- {"pending": [...], "shipped": [...], "done": [...]}
let by_dept be group_by(employees, task(e) department of e)
find_first(list, predicate)
Return the first element that satisfies the predicate, or nothing if none matches.
let admin be find_first(users, task(u) role of u == "admin")
when admin == nothing
print("no admin found")
otherwise
print("admin:", name of admin)
every(list, predicate) / some(list, predicate)
every returns true if all elements satisfy the predicate. some returns true if at least one does. Both short-circuit.
let all_paid be every(invoices, task(i) paid of i)
let any_late be some(invoices, task(i) overdue of i)
print(every([2,4,6], task(n) n % 2 == 0)) -- true
print(some([1,2,3], task(n) n > 2)) -- true
count_where(list, predicate)
Count the elements that satisfy the predicate. Equivalent to length(where(list, predicate)) but avoids materialising the filtered list.
let active_count be count_where(users, task(u) active of u)
print(count_where([1,2,3,4], task(n) n > 2)) -- 2
flatten(list_of_lists)
Flatten one level of nesting — a list of lists becomes a single list. Does not recurse into deeper nesting.
flatten([[1,2], [3,4], [5]]) -- [1, 2, 3, 4, 5]
-- collect nested results from multiple queries
let all_tags be flatten(apply(get_tags, posts))
zip_with(list_a, list_b, combiner)
Pair corresponding elements from two lists and apply a combiner function. The result length matches the shorter list.
let names be ["Alice", "Bob", "Carol"]
let scores be [88, 92, 77]
let records be zip_with(names, scores,
task(n, s) {"name": n, "score": s})
-- [{"name":"Alice","score":88}, {"name":"Bob","score":92}, ...]
5. I/O Functions requires capabilities
All I/O functions require explicit capability declarations. Without the matching require statement the runtime raises a capability error before the function is called.
fetch(url, method?, headers?, body?) net
Make an HTTP request. Returns a map with status, headers, and body.
require net("api.example.com")
let resp be fetch("https://api.example.com/data")
print(status of resp) -- 200
print(body of resp) -- raw body text
-- POST with JSON body
let r be fetch("https://api.example.com/items",
"POST",
{"Content-Type": "application/json"},
'{"name":"widget"}'
)
read_file(path) / write_file(path, content) file
read_file returns the entire file as text. write_file creates or overwrites a file and returns true on success.
require file("/data/*")
let csv be read_file("/data/report.csv")
write_file("/data/output.txt", "processed")
list_dir(path) / file_exists(path) file
list_dir returns a list of filenames (not full paths) in the directory. file_exists returns a boolean.
require file("/data/*")
let files be list_dir("/data")
print(files) -- ["report.csv", "output.txt", ...]
when file_exists("/data/config.json")
print("config found")
run(command, args?, timeout?) exec
Execute a shell command. Returns a map with exit_code, stdout, and stderr. Optional args is a list of argument strings; optional timeout is in seconds.
require exec()
let result be run("git", ["log", "--oneline", "-5"])
print(stdout of result)
let r be run("ping", ["-c", "1", "example.com"], 5)
when exit_code of r != 0
print("command failed:", stderr of r)
get_env(name) env
Read an environment variable by name. Returns the value as text, or nothing if the variable is not set.
require env()
let api_key be get_env("API_KEY")
when api_key == nothing
print("API_KEY not set")
6. Time & Random requires capabilities
now() time / sleep(seconds) time
now() returns the current Unix timestamp as a float. sleep(seconds) pauses execution for the given duration — useful for pacing loops or SSE streams.
require time()
let start be now()
sleep(1.5)
let elapsed be now() - start
print("elapsed:", elapsed, "seconds")
format_time(timestamp, pattern?) / parse_time(text, pattern?) time
Format a Unix timestamp as text or parse text back to a timestamp. Default format is ISO-8601 UTC. Accepts strftime patterns for custom formatting.
require time()
-- default: ISO-8601
format_time(0) -- "1970-01-01T00:00:00Z"
format_time(now(), "%Y-%m-%d") -- "2025-06-18"
format_time(now(), "%H:%M:%S") -- "14:30:00"
-- parse back to timestamp
let ts be parse_time("2025-06-18T12:00:00Z")
let ts2 be parse_time("2025-06-18", "%Y-%m-%d")
date_parts(timestamp) time
Decompose a Unix timestamp into a map with individual date/time fields (UTC).
| Key | Type | Description |
|---|---|---|
year | number | e.g. 2025 |
month | number | 1–12 |
day | number | 1–31 |
hour | number | 0–23 |
minute | number | 0–59 |
second | number | 0–59 |
require time()
let parts be date_parts(now())
print(year of parts) -- 2025
print(month of parts) -- 6
print(day of parts) -- 18
random() random / random_int(min, max) random
random() returns a float between 0 (inclusive) and 1 (exclusive). random_int(min, max) returns an integer in the closed range [min, max].
require random()
print(random()) -- e.g. 0.7342...
print(random_int(1, 6)) -- dice roll: 1..6
print(random_int(0, 99)) -- 0..99 inclusive
7. HTTP Client Functions requires net
A full-featured HTTP client. All functions return a response map with the fields below. The net capability must be declared for the target host.
| Response field | Type | Description |
|---|---|---|
status | number | HTTP status code |
ok | bool | true if status 2xx |
body | text | Raw response body |
json | map/list | Parsed JSON (if content-type is JSON) |
headers | map | Response headers (lowercase keys) |
error | text/nothing | Network or timeout error message |
http(method, url, headers?, query?, body?, timeout?)
Full control over the request. query is a map of query parameters appended to the URL. timeout is in seconds.
require net("api.example.com")
let resp be http("GET", "https://api.example.com/search",
nothing, -- no extra headers
{"q": "syntecnia", "page": 1}, -- query params
nothing, -- no body
10 -- 10s timeout
)
print(json of resp)
Shorthand methods
| Function | Signature |
|---|---|
http_get |
http_get(url, headers?, query?) |
http_post |
http_post(url, body, headers?) |
http_put |
http_put(url, body, headers?) |
http_delete |
http_delete(url, headers?) |
require net("api.example.com")
-- GET with query params
let users be http_get("https://api.example.com/users",
nothing, {"active": "true"})
-- POST JSON
let r be http_post("https://api.example.com/users",
{"name": "Alice", "role": "admin"},
{"Authorization": "Bearer " + token})
when not ok of r
print("request failed:", status of r)
-- DELETE
http_delete("https://api.example.com/users/42")
8. Database Functions (SQLite) requires db
Native SQLite support. Connections are keyed by path and shared within a program. Parameterised queries prevent SQL injection — always use params for user-supplied values.
? placeholders + params list) for any value that comes from user input. Never interpolate values directly into a query string.
db_open(path, mode?) / db_close(path?)
Open a database file. mode defaults to "readwrite". Other modes: "readonly", "memory". Call db_close without arguments to close all connections.
require db("/data/store.db")
db_open("/data/store.db")
db_open("/reports/archive.db", "readonly")
db_open(":memory:", "memory") -- in-memory DB
db_close("/data/store.db") -- close one
db_close() -- close all
sql(query, params?)
Execute a SELECT query. Returns a list of row maps where each key is a column name.
require db("/data/store.db")
db_open("/data/store.db")
-- simple query
let products be sql("SELECT id, name, price FROM products")
-- with parameters
let cheap be sql(
"SELECT * FROM products WHERE price < ? AND active = ?",
[50, 1]
)
print(length(cheap), "results")
sql_exec(statement, params?)
Execute a data-modifying statement (INSERT, UPDATE, DELETE, CREATE TABLE, etc.). Returns a map with rows_affected and last_id.
let res be sql_exec(
"INSERT INTO products (name, price) VALUES (?, ?)",
["widget", 9.99]
)
print("inserted id:", last_id of res)
sql_exec("UPDATE products SET active = 0 WHERE price > ?", [1000])
sql_exec("CREATE TABLE IF NOT EXISTS logs (id INTEGER PRIMARY KEY, msg TEXT)")
sql_batch(statement, params_list)
Execute the same statement with multiple parameter sets in a single transaction. Returns {rows_affected} as the total across all rows.
let new_items be [
["alpha", 10],
["beta", 20],
["gamma", 30]
]
sql_batch(
"INSERT INTO products (name, price) VALUES (?, ?)",
new_items
)
sql_tables() / paged(query, params?)
sql_tables() returns a list of table names in the open database. paged() is designed for use inside a serve route — it pushes LIMIT/OFFSET and COUNT(*) to SQL, returning a paginated envelope without loading all rows into memory.
print(sql_tables()) -- ["products", "users", "orders"]
-- inside a serve route
route "GET /products"
give paged("SELECT id, name, price FROM products ORDER BY name")
9. Cron / Scheduling Functions
Schedule tasks to run in the background. Job names are returned so they can be cancelled later.
cron_every(seconds, task)
Register a repeating background job. The task runs every seconds seconds until cancelled. Returns the generated job name.
task check_health()
let r be http_get("https://api.example.com/health")
when not ok of r
print("health check FAILED")
let job be cron_every(30, check_health) -- every 30 seconds
cron_after(seconds, task)
Run a task once after the given delay. Returns the job name.
task send_reminder()
print("Reminder: review the report!")
cron_after(3600, send_reminder) -- fires once in 1 hour
cron_cancel(name) / cron_list() / cron_status()
Manage scheduled jobs. cron_cancel returns true if the job existed. cron_list() returns a list of job-info maps; cron_status() returns a formatted text summary.
cron_cancel(job) -- stop a job by name
let jobs be cron_list()
print(length(jobs), "active jobs")
print(cron_status())
10. Agent Memory & Rules
Persistent memory, progress tracking, and owner rules are built-in primitives for AI agent programs. Memory is stored across runs; rules constrain agent behaviour.
Progress Tracking
Track long-running multi-step tasks. If an agent crashes and restarts, resume_point returns the step where it left off.
| Function | Description |
|---|---|
create_progress(task_name, [steps]) | Create a progress tracker with named steps |
start_step(task_name, step_name) | Mark a step as in progress |
complete_step(task_name, step_name, result?) | Mark a step done with optional result |
fail_step(task_name, step_name, error?) | Mark a step as failed |
resume_point(task_name) | Returns the next incomplete step, or nothing |
progress_display(task_name) | Formatted text summary |
progress_percent(task_name) | Completion percentage (0–100) |
create_progress("sync", ["fetch", "validate", "update"])
start_step("sync", "fetch")
let data be fetch("https://api.example.com/items")
complete_step("sync", "fetch", text(length(data)) + " items")
start_step("sync", "validate")
-- ... if agent crashes here and restarts ...
let next be resume_point("sync") -- "validate"
print(progress_display("sync"))
print(progress_percent("sync"), "%")
Persistent Memory
Store observations, learnings, and decisions that persist across agent runs. Memory is categorised and searchable.
| Category | Use for |
|---|---|
preference | User or owner preferences |
rule | Operational constraints |
learning | Observations made during runs |
decision | Decisions made and their rationale |
context | Situational information |
| Function | Description |
|---|---|
remember(category, content, tags?) | Store a memory entry; returns entry_id |
recall(category?, tags?, search?) | Retrieve matching entries |
forget_memory(entry_id) | Remove a specific memory entry |
memory_summary() | Formatted overview of all stored memory |
-- store observations
remember("preference", "Customer prefers formal tone", ["communication"])
remember("learning", "API is slow on Mondays", ["api", "performance"])
remember("decision", "Chose retry over fallback for order #42")
-- retrieve by category and tag
let prefs be recall("preference", ["communication"])
let all be recall() -- everything
-- search by keyword
let api_notes be recall(nothing, nothing, "API")
print(memory_summary())
Owner Rules
Rules constrain what an agent may do. They are checked against context maps at runtime and produce violations for hard blocks or warnings.
| Level | Effect |
|---|---|
must | Hard block — violation stops execution |
should | Warning — logged but does not block |
avoid | Preference against — agent should look for alternatives |
prefer | Preference for — used when all options are otherwise equal |
| Function | Description |
|---|---|
add_rule(name, level, description, category?) | Register a rule |
check_rules(category?, context_map?) | Returns a list of violation maps |
get_rules(category?) | Returns all registered rules |
-- define rules
add_rule("max_discount", "must", "discount <= 0.20", "pricing")
add_rule("formal_tone", "prefer", "Use formal tone", "communication")
add_rule("no_pii_log", "must", "Never log PII data", "privacy")
-- check before acting
let violations be check_rules("pricing", {"discount": 0.25})
when length(violations) > 0
print("rule violation:", violations[0])
-- list rules for a category
let pricing_rules be get_rules("pricing")
Capability Summary
Functions that access external resources require a matching require declaration. Zero access by default.
| Capability | Syntax | Functions unlocked |
|---|---|---|
net |
require net("host.com") |
fetch, http, http_get, http_post, http_put, http_delete |
file |
require file("/path/*") |
read_file, write_file, list_dir, file_exists |
exec |
require exec() |
run |
env |
require env() |
get_env |
time |
require time() |
now, sleep, format_time, parse_time, date_parts |
random |
require random() |
random, random_int |
db |
require db("/path/db") |
db_open, db_close, sql, sql_exec, sql_batch, sql_tables, paged |
serve |
require serve(PORT) |
serve on PORT, response helpers (ok, created, not_found, fail, html, render, content) |
task fetch_orders()
require net("api.shop.com")
give http_get("https://api.shop.com/orders")
-- this task cannot access any other host,
-- even if the program grants net("*")