String Interpolation
Embed any expression inside a string with ${...}. The expression is evaluated and converted to a string at runtime.
let name = "world"
print("hello ${name}") // hello world
print("2 + 2 = ${2 + 2}") // 2 + 2 = 4
print("${name.to_upper()}") // WORLD
print("len: ${[1,2,3].len()}") // len: 3
print("escaped: \${literal}") // escaped: ${literal}
Phase Constraints
Annotate function parameters with flux (or ~) to require a fluid argument, or fix (or *) to require a crystal argument. Passing an argument with an incompatible phase produces a runtime error. Parameters without a phase annotation accept any phase.
fn mutate(data: flux Map) { data.set("k", "v") }
fn inspect(data: fix Map) { print(data.get("k")) }
flux m = Map::new()
mutate(m) // ok — fluid matches flux
fix frozen = freeze(m)
inspect(frozen) // ok — crystal matches fix
mutate(frozen) // error — crystal rejected by flux
The sigils ~ and * are shorthand for flux and fix in type annotations.
fn update(data: ~Map) { data.set("x", 1) } // same as flux Map
fn read(data: *Map) { print(data) } // same as fix Map
Phase-Dependent Dispatch
Define multiple functions with the same name but different phase annotations. The runtime dispatches to the best-matching overload based on the phases of the arguments at call time.
fn process(data: ~Map) { print("mutable path") }
fn process(data: *Map) { print("immutable path") }
flux m = Map::new()
process(m) // "mutable path"
freeze(m)
process(m) // "immutable path"
An overload with no phase annotation acts as a fallback. It is selected when the argument has no specific phase or when no phase-annotated overload matches.
fn handle(data: ~Map) { print("flux") }
fn handle(data: Map) { print("fallback") }
let m = Map::new()
handle(m) // "fallback" (unphased arg)
Core
Read a line of input from stdin, optionally displaying a prompt.
input("Name: ") // reads user input
Returns the type name of a value as a string.
typeof(42) // "Int"
Returns the phase of a value ("flux", "fix", or "crystal").
phase_of(freeze([1, 2])) // "crystal"
Convert any value to its string representation.
to_string(42) // "42"
Return the repr string of a value. Strings are quoted, structs with a `repr` closure field use the custom representation.
repr(42) // "42"
repr("hello") // "\"hello\""
Returns the length of a string, array, or map.
len("hello") // 5
len([1, 2, 3]) // 3
Exit the program with an optional exit code (default 0).
exit(1) // exits with code 1
Return the Lattice interpreter version string.
version() // "0.1.0"
Print values separated by spaces without a trailing newline.
print_raw("hello", "world") // prints: hello world
Print values to stderr with a trailing newline.
eprint("warning:", msg) // prints to stderr
Assert that a condition is truthy, or raise an error with an optional message.
assert(1 == 1, "math works")
Assert that a condition is truthy (no-op when assertions are disabled via --no-assertions).
debug_assert(x > 0, "x must be positive")
Print values separated by spaces with a trailing newline.
print("hello", "world") // prints: hello world
Phase Transitions
Freeze a variable and validate any pending seed contracts.
grow(config) // freeze + validate seeds
Transition a value to the crystal (immutable) phase.
freeze([1, 2, 3]) // crystal [1, 2, 3]
Transition a crystal value back to the flux (mutable) phase.
thaw(freeze([1, 2])) // flux [1, 2]
Create a deep copy of a value.
clone(my_array) // independent copy
Atomically thaw a crystal value, apply a transformation, and refreeze.
anneal(frozen_map) |m| { m["key"] = "value"; m }
Type Constructors
Create a new empty map.
Map::new() // {}
Create a new channel for concurrent communication.
Channel::new() // <Channel>
Create a new empty set.
Set::new() // Set{}
Create a set from an array (duplicates removed).
Set::from([1, 2, 2, 3]) // Set{1, 2, 3}
Generate an array of integers from start (inclusive) to end (exclusive).
range(0, 5) // [0, 1, 2, 3, 4]
range(0, 10, 2) // [0, 2, 4, 6, 8]
Type Conversion
Return the Unicode code point of the first character.
ord("A") // 65
Return the character for a Unicode code point.
chr(65) // "A"
Parse a string as an integer.
parse_int("42") // 42
Parse a string as a floating-point number.
parse_float("3.14") // 3.14
Convert a value to an integer (truncates floats, parses strings).
to_int(3.9) // 3
Convert a value to a floating-point number.
to_float(42) // 42.0
Error Handling
Create an error value with the given message.
error("something went wrong") // "EVAL_ERROR:something went wrong"
Check if a value is an error value.
is_error(error("oops")) // true
Reflection
Returns the type name of a struct instance.
struct_name(user) // "User"
Returns an array of field name strings from a struct instance.
struct_fields(user) // ["name", "age"]
Converts a struct instance to a Map of {field_name: value}.
struct_to_map(user).get("name") // "Alice"
Creates a struct instance from a type name and a Map of field values. Missing fields default to nil.
struct_from_map("User", m)
Math
Return the absolute value of a number.
abs(-5) // 5
Round down to the nearest integer.
floor(3.7) // 3
Round up to the nearest integer.
ceil(3.2) // 4
Round to the nearest integer.
round(3.5) // 4
Return the square root of a number.
sqrt(16) // 4.0
Raise base to the power of exp.
pow(2, 10) // 1024.0
Return the smaller of two numbers.
min(3, 7) // 3
Return the larger of two numbers.
max(3, 7) // 7
Return a random float between 0.0 (inclusive) and 1.0 (exclusive).
random() // 0.7231...
Return a random integer in the range [min, max).
random_int(1, 100) // 42
Return the natural logarithm (base e) of a number.
log(math_e()) // 1.0
Return the base-2 logarithm of a number.
log2(8) // 3.0
Return the base-10 logarithm of a number.
log10(1000) // 3.0
Return the sine of an angle in radians.
sin(0) // 0.0
Return the cosine of an angle in radians.
cos(0) // 1.0
Return the tangent of an angle in radians.
tan(0) // 0.0
Return the two-argument arctangent in radians.
atan2(1, 1) // 0.7853...
Clamp a value between a minimum and maximum.
clamp(15, 0, 10) // 10
Return the mathematical constant pi.
math_pi() // 3.14159265358979...
Return Euler's number (e).
math_e() // 2.71828182845904...
Return the arcsine in radians.
asin(1) // 1.5707...
Return the arccosine in radians.
acos(1) // 0.0
Return the arctangent in radians.
atan(1) // 0.7853...
Return e raised to the power of x.
exp(1) // 2.71828...
Return -1, 0, or 1 indicating the sign of a number.
sign(-42) // -1
Return the greatest common divisor of two integers.
gcd(12, 8) // 4
Return the least common multiple of two integers.
lcm(4, 6) // 12
Check if a value is NaN (not a number).
is_nan(0.0 / 0.0) // true
Check if a value is positive or negative infinity.
is_inf(1.0 / 0.0) // true
Return the hyperbolic sine.
sinh(1) // 1.1752...
Return the hyperbolic cosine.
cosh(0) // 1.0
Return the hyperbolic tangent.
tanh(0) // 0.0
Linear interpolation between a and b by factor t.
lerp(0, 10, 0.5) // 5.0
String Formatting
Format a string with placeholders replaced by arguments.
format("{} is {}", "sky", "blue") // "sky is blue"
Regex
Test if a string matches a regular expression pattern.
regex_match("^[0-9]+$", "123") // true
Find all matches of a pattern in a string, returning an array.
regex_find_all("[0-9]+", "a1b2c3") // ["1", "2", "3"]
Replace all matches of a pattern in a string.
regex_replace("[0-9]", "a1b2", "X") // "aXbX"
JSON
Parse a JSON string into a Lattice value.
json_parse("{\"a\": 1}") // {a: 1}
Serialize a Lattice value to a JSON string.
json_stringify([1, 2, 3]) // "[1,2,3]"
CSV
Parse a CSV string into an array of arrays (rows of fields).
csv_parse("a,b\n1,2") // [["a", "b"], ["1", "2"]]
Convert an array of arrays into a CSV string.
csv_stringify([["a", "b"], ["1", "2"]]) // "a,b\n1,2\n"
URL
Percent-encode a string for use in URLs.
url_encode("hello world") // "hello%20world"
Decode a percent-encoded URL string.
url_decode("hello%20world") // "hello world"
HTTP
Perform an HTTP GET request. Returns a map with "status", "headers", and "body".
http_get("https://httpbin.org/get")
Perform an HTTP POST request. Options map may contain "headers" (Map), "body" (String), and "timeout" (Int ms).
http_post("https://httpbin.org/post", {"body": "hello"})
Perform an HTTP request with a custom method. Options may contain "headers", "body", and "timeout".
http_request("PUT", "https://api.example.com/data", {"body": "{}"})
Data Formats
Parse a TOML string into a Lattice Map.
toml_parse("[server]\nhost = \"localhost\"\nport = 8080")
Serialize a Lattice Map to a TOML string.
toml_stringify({"host": "localhost", "port": 8080})
Parse a YAML string into a Lattice value.
yaml_parse("name: Alice\nage: 30")
Serialize a Lattice value to a YAML string.
yaml_stringify({"name": "Alice", "age": 30})
Functional
Return the argument unchanged.
identity(42) // 42
Thread a value through a series of functions left to right.
pipe(5, |x| { x * 2 }, |x| { x + 1 }) // 11
Compose two functions: compose(f, g)(x) calls f(g(x)).
compose(|x| { x + 1 }, |x| { x * 2 })(3) // 7
Metaprogramming
Check if a source string is a complete expression (balanced brackets).
is_complete("{ 1 + 2 }") // true
Load and execute a Lattice source file, importing its definitions.
require("stdlib.lat") // true
Load a native extension (.dylib/.so) and return a Map of its functions.
let pg = require_ext("pg")
Parse and execute a string as Lattice source code, returning the result.
lat_eval("1 + 2") // 3
Tokenize a source string, returning an array of Token structs.
tokenize("1 + 2") // [{type: "INT_LIT", text: "1"}, ...]
File System
Read the entire contents of a file as a string.
read_file("data.txt") // "file contents..."
Write a string to a file, creating or overwriting it.
write_file("out.txt", "hello") // true
Check if a file or directory exists at the given path.
file_exists("data.txt") // true
Delete a file at the given path.
delete_file("temp.txt") // true
List entries in a directory, returning an array of filenames.
list_dir(".") // ["file1.txt", "dir1", ...]
Append a string to the end of a file.
append_file("log.txt", "new line\n") // true
Create a directory at the given path.
mkdir("new_dir") // true
Rename or move a file or directory.
rename("old.txt", "new.txt") // true
Check if the path points to a directory.
is_dir("/tmp") // true
Check if the path points to a regular file.
is_file("data.txt") // true
Remove a directory (must be empty).
rmdir("old_dir") // true
Find files matching a glob pattern, returning an array of paths.
glob("*.txt") // ["a.txt", "b.txt"]
Get file metadata (size, mtime, type, permissions) as a map.
stat("file.txt") // {size: 1024, mtime: ..., type: "file", permissions: 644}
Copy a file from source path to destination path.
copy_file("a.txt", "b.txt") // true
Resolve a path to its absolute canonical form.
realpath("./src/../src") // "/home/user/src"
Create a temporary directory and return its path.
tempdir() // "/tmp/lat_XXXXXX"
Create a temporary file and return its path.
tempfile() // "/tmp/lat_XXXXXX"
Change file permissions using a numeric mode.
chmod("script.sh", 755) // true
Return the size of a file in bytes.
file_size("data.bin") // 4096
Path
Join path components into a single path string.
path_join("/home", "user", "file.txt") // "/home/user/file.txt"
Return the directory component of a path.
path_dir("/home/user/file.txt") // "/home/user"
Return the filename component of a path.
path_base("/home/user/file.txt") // "file.txt"
Return the file extension of a path (including the dot).
path_ext("file.txt") // ".txt"
Environment
Get an environment variable's value, or unit if not set.
env("HOME") // "/home/user"
Set an environment variable.
env_set("MY_VAR", "hello")
Return an array of all environment variable names.
env_keys() // ["HOME", "PATH", ...]
Process
Return the current working directory.
cwd() // "/home/user/project"
Execute a command directly (no shell), returning {stdout, stderr, status}.
exec("ls -la") // {stdout: "...", stderr: "", status: 0}
Execute a command via the system shell, returning {stdout, stderr, status}.
shell("echo hello") // {stdout: "hello\n", stderr: "", status: 0}
Return command-line arguments as an array of strings.
args() // ["script.lat", "--flag"]
Return the operating system name ("darwin", "linux", etc.).
platform() // "darwin"
Return the system hostname.
hostname() // "my-machine"
Return the current process ID.
pid() // 12345
Date & Time
Return the current Unix timestamp in milliseconds.
time() // 1700000000000
Pause execution for the given number of milliseconds.
sleep(1000) // sleeps for 1 second
Format a Unix timestamp (ms) using a strftime format string.
time_format(0, "%Y-%m-%d") // "1970-01-01"
Parse a datetime string into a Unix timestamp (ms).
time_parse("2024-01-01", "%Y-%m-%d") // 1704067200000
Crypto
Compute the SHA-256 hash of a string, returned as hex.
sha256("hello") // "2cf24dba..."
Compute the MD5 hash of a string, returned as hex.
md5("hello") // "5d41402a..."
Encode a string to Base64.
base64_encode("hello") // "aGVsbG8="
Decode a Base64 string.
base64_decode("aGVsbG8=") // "hello"
Networking
Create a TCP server socket listening on host:port, returning a file descriptor.
tcp_listen("0.0.0.0", 8080) // 3
Accept an incoming TCP connection, returning a new client file descriptor.
tcp_accept(server_fd) // 4
Connect to a TCP server, returning a file descriptor.
tcp_connect("localhost", 8080) // 3
Read data from a TCP socket as a string.
tcp_read(client_fd) // "HTTP/1.1 200 OK..."
Read exactly n bytes from a TCP socket.
tcp_read_bytes(fd, 1024) // "..."
Write a string to a TCP socket.
tcp_write(fd, "GET / HTTP/1.1\r\n\r\n") // true
Close a TCP socket.
tcp_close(fd)
Get the remote address of a connected TCP socket.
tcp_peer_addr(client_fd) // "192.168.1.1:54321"
Set read/write timeout on a TCP socket in seconds.
tcp_set_timeout(fd, 30) // true
Establish a TLS connection to a server, returning a handle.
tls_connect("example.com", 443) // 1
Read data from a TLS connection as a string.
tls_read(handle) // "HTTP/1.1 200 OK..."
Read exactly n bytes from a TLS connection.
tls_read_bytes(handle, 512) // "..."
Write a string to a TLS connection.
tls_write(handle, "GET / HTTP/1.1\r\n\r\n") // true
Close a TLS connection.
tls_close(handle)
Check if TLS support is available (OpenSSL linked).
tls_available() // true
String Methods
Check if the string contains a substring.
"hello world".contains("world") // true
Check if the string starts with the given prefix.
"hello".starts_with("he") // true
Check if the string ends with the given suffix.
"hello".ends_with("lo") // true
Remove leading and trailing whitespace.
" hello ".trim() // "hello"
Convert the string to uppercase.
"hello".to_upper() // "HELLO"
Convert the string to lowercase.
"HELLO".to_lower() // "hello"
Replace all occurrences of a substring.
"hello world".replace("world", "there") // "hello there"
Split the string by a separator, returning an array of parts.
"a,b,c".split(",") // ["a", "b", "c"]
Return the index of the first occurrence of substr, or -1 if not found.
"hello".index_of("ll") // 2
Extract a substring from start (inclusive) to end (exclusive).
"hello".substring(1, 4) // "ell"
Split the string into an array of single-character strings.
"abc".chars() // ["a", "b", "c"]
Return an array of byte values (integers) for the string.
"AB".bytes() // [65, 66]
Return the string with characters in reverse order.
"hello".reverse() // "olleh"
Repeat the string n times.
"ab".repeat(3) // "ababab"
Remove leading whitespace.
" hello".trim_start() // "hello"
Remove trailing whitespace.
"hello ".trim_end() // "hello"
Pad the string on the left to reach length n using character ch.
"42".pad_left(5, "0") // "00042"
Pad the string on the right to reach length n using character ch.
"42".pad_right(5, "0") // "42000"
Count non-overlapping occurrences of a substring.
"ababa".count("ab") // 2
Check if the string is empty.
"".is_empty() // true
Array Methods
Append a value to the end of the array (mutates in place).
arr.push(42)
@method String.len() -> Int @method Map.len() -> Int Return the number of elements or characters.
[1, 2, 3].len() // 3
Apply a function to each element, returning a new array of results.
[1, 2, 3].map(|x| { x * 2 }) // [2, 4, 6]
Join array elements into a string with an optional separator.
["a", "b", "c"].join(", ") // "a, b, c"
Return a new array containing only elements for which fn returns true.
[1, 2, 3, 4].filter(|x| { x > 2 }) // [3, 4]
Call a function for each element (for side effects).
[1, 2, 3].for_each(|x| { print(x) })
Return the first element for which fn returns true, or unit if not found.
[1, 2, 3].find(|x| { x > 1 }) // 2
Check if the array contains a value.
[1, 2, 3].contains(2) // true
Return a new array with elements in reverse order.
[1, 2, 3].reverse() // [3, 2, 1]
Return an array of [index, value] pairs.
["a", "b"].enumerate() // [[0, "a"], [1, "b"]]
Return a new sorted array (elements must be comparable).
[3, 1, 2].sort() // [1, 2, 3]
Flatten one level of nested arrays.
[[1, 2], [3, 4]].flat() // [1, 2, 3, 4]
Reduce an array to a single value by applying fn(acc, elem) for each element.
[1, 2, 3].reduce(|a, b| { a + b }, 0) // 6
Return a sub-array from start (inclusive) to end (exclusive).
[1, 2, 3, 4, 5].slice(1, 4) // [2, 3, 4]
Return the first n elements of the array.
[1, 2, 3, 4].take(2) // [1, 2]
Return the array with the first n elements removed.
[1, 2, 3, 4].drop(2) // [3, 4]
Remove and return the last element of the array.
[1, 2, 3].pop() // 3
Return the index of the first occurrence of val, or -1 if not found.
[10, 20, 30].index_of(20) // 1
Return true if fn returns true for any element.
[1, 2, 3].any(|x| { x > 2 }) // true
Return true if fn returns true for all elements.
[2, 4, 6].all(|x| { x % 2 == 0 }) // true
Combine two arrays into an array of [a, b] pairs.
[1, 2].zip(["a", "b"]) // [[1, "a"], [2, "b"]]
Return a new array with duplicate elements removed.
[1, 2, 2, 3, 1].unique() // [1, 2, 3]
Insert a value at the given index (mutates in place).
arr.insert(1, "x")
Remove and return the element at the given index.
[1, 2, 3].remove_at(1) // 2
Sort using a custom comparator that returns a negative, zero, or positive Int.
["bb", "a", "ccc"].sort_by(|a, b| { len(a) - len(b) }) // ["a", "bb", "ccc"]
Map each element to an array, then flatten one level.
[1, 2].flat_map(|x| { [x, x * 10] }) // [1, 10, 2, 20]
Split the array into sub-arrays of the given size.
[1, 2, 3, 4, 5].chunk(2) // [[1, 2], [3, 4], [5]]
Group elements by the result of fn, returning a map of key to arrays.
[1, 2, 3, 4].group_by(|x| { x % 2 }) // {0: [2, 4], 1: [1, 3]}
Return the sum of all numeric elements.
[1, 2, 3].sum() // 6
Return the minimum element (all elements must be numeric).
[3, 1, 2].min() // 1
Return the maximum element (all elements must be numeric).
[3, 1, 2].max() // 3
Return the first element, or unit if the array is empty.
[1, 2, 3].first() // 1
Return the last element, or unit if the array is empty.
[1, 2, 3].last() // 3
Map Methods
Set a key-value pair in the map (mutates in place).
m.set("name", "Alice")
Remove a key from the map (mutates in place).
m.remove("name")
Get the value for a key, or unit if not found.
m.get("name") // "Alice"
Check if the map contains the given key.
m.has("name") // true
Return an array of all keys in the map.
m.keys() // ["name", "age"]
Return an array of all values in the map.
m.values() // ["Alice", 30]
Return the number of key-value pairs in the map.
m.len() // 2
Return an array of [key, value] pairs.
m.entries() // [["name", "Alice"], ["age", 30]]
Merge another map into this one (mutates in place).
m.merge(other_map)
Call fn(key, value) for each entry in the map.
m.for_each(|k, v| { print(k, v) })
Return a new map with only entries where fn(key, value) returns true.
m.filter(|k, v| { v > 0 })
Return a new map with values transformed by fn(key, value).
m.map(|k, v| { v * 2 })
Set Methods
Add an element to the set (mutates in place).
s.add(42)
Remove an element from the set (mutates in place).
s.remove(42)
Check if the set contains the value.
s.has(42)
Return the number of elements in the set.
s.len()
Convert the set to an array of its elements.
s.to_array()
Return a new set containing all elements from both sets.
s1.union(s2)
Return a new set containing only elements in both sets.
s1.intersection(s2)
Return a new set with elements in this set but not in other.
s1.difference(s2)
Check if this set is a subset of other.
s1.is_subset(s2)
Check if this set is a superset of other.
s1.is_superset(s2)
Channel Methods
Send a crystal (frozen) value on the channel.
ch.send(freeze(42))
Receive a value from the channel, blocking until available. Returns unit if closed.
ch.recv() // 42
Close the channel, preventing further sends.
ch.close()
Lattice