Skip to content

Standard Library

Floe ships with built-in functions for common operations. These are known to the compiler and inlined during codegen.

All stdlib functions are pipe-friendly: the first argument is the data, so they work naturally with |>.

[3, 1, 2]
|> Array.sort
|> Array.map(fn(n) n * 10)
|> Array.reverse
// [30, 20, 10]

All array functions return new arrays. They never mutate the original.

FunctionSignatureDescription
Array.sortArray<number> -> Array<number>Sort numerically (returns new array)
Array.sortByArray<T>, fn(T) -> number -> Array<T>Sort by key function
Array.mapArray<T>, fn(T) -> U -> Array<U>Transform each element
Array.filterArray<T>, fn(T) -> boolean -> Array<T>Keep elements matching predicate
Array.findArray<T>, fn(T) -> boolean -> Option<T>First element matching predicate
Array.findIndexArray<T>, fn(T) -> boolean -> Option<number>Index of first match
Array.flatMapArray<T>, fn(T) -> Array<U> -> Array<U>Map then flatten one level
Array.atArray<T>, number -> Option<T>Safe index access
Array.containsArray<T>, T -> booleanCheck if element exists (structural equality)
Array.headArray<T> -> Option<T>First element
Array.lastArray<T> -> Option<T>Last element
Array.takeArray<T>, number -> Array<T>First n elements
Array.dropArray<T>, number -> Array<T>All except first n elements
Array.reverseArray<T> -> Array<T>Reverse order (returns new array)
Array.reduceArray<T>, U, fn(U, T) -> U -> UFold into a single value
Array.lengthArray<T> -> numberNumber of elements
Array.anyArray<T>, fn(T) -> boolean -> booleanTrue if any element matches predicate
Array.allArray<T>, fn(T) -> boolean -> booleanTrue if all elements match predicate
Array.sumArray<number> -> numberSum all elements
Array.joinArray<string>, string -> stringJoin elements with separator
Array.isEmptyArray<T> -> booleanTrue if array has no elements
Array.chunkArray<T>, number -> Array<Array<T>>Split into chunks of given size
Array.uniqueArray<T> -> Array<T>Remove duplicate elements
Array.groupByArray<T>, fn(T) -> string -> RecordGroup elements by key function
Array.zipArray<T>, Array<U> -> Array<[T, U]>Pair elements from two arrays
// Sort returns a new array, original unchanged
const nums = [3, 1, 2]
const sorted = Array.sort(nums) // [1, 2, 3]
// nums is still [3, 1, 2]
// Safe access returns Option
const first = Array.head([1, 2, 3]) // Some(1)
const empty = Array.head([]) // None
// Structural equality for contains
const user1 = User(name: "Ryan")
const found = Array.contains(users, user1) // true if any user matches by value
// Pipe chains with dot shorthand
const result = users
|> Array.filter(.active)
|> Array.sortBy(.name)
|> Array.take(10)
|> Array.map(.email)
// Check predicates
const hasAdmin = users |> Array.any(.role == "admin") // true/false
const allActive = users |> Array.all(.active) // true/false
// Aggregate
const total = [1, 2, 3] |> Array.sum // 6
const csv = ["a", "b", "c"] |> Array.join(", ") // "a, b, c"
// Utilities
const empty = Array.isEmpty([]) // true
const chunks = [1, 2, 3, 4, 5] |> Array.chunk(2) // [[1, 2], [3, 4], [5]]
const deduped = [1, 2, 2, 3] |> Array.unique // [1, 2, 3]
const grouped = users |> Array.groupBy(.role) // { admin: [...], user: [...] }

Functions for working with Option<T> (Some(v) / None) values.

FunctionSignatureDescription
Option.mapOption<T>, fn(T) -> U -> Option<U>Transform the inner value if present
Option.flatMapOption<T>, fn(T) -> Option<U> -> Option<U>Chain option-returning operations
Option.unwrapOrOption<T>, T -> TExtract value or use default
Option.isSomeOption<T> -> booleanCheck if value is present
Option.isNoneOption<T> -> booleanCheck if value is absent
Option.toResultOption<T>, E -> Result<T, E>Convert to Result with error for None
// Transform without unwrapping
const upper = user.nickname
|> Option.map(fn(n) String.toUpper(n))
// Some("RYAN") or None
// Chain lookups
const avatar = user.nickname
|> Option.flatMap(fn(n) findAvatar(n))
// Extract with fallback
const display = user.nickname
|> Option.unwrapOr(user.name)
// Convert to Result for error handling
const name = user.nickname
|> Option.toResult("User has no nickname")

Functions for working with Result<T, E> (Ok(v) / Err(e)) values.

FunctionSignatureDescription
Result.mapResult<T, E>, fn(T) -> U -> Result<U, E>Transform the Ok value
Result.mapErrResult<T, E>, fn(E) -> F -> Result<T, F>Transform the Err value
Result.flatMapResult<T, E>, fn(T) -> Result<U, E> -> Result<U, E>Chain result-returning operations
Result.unwrapOrResult<T, E>, T -> TExtract Ok value or use default
Result.isOkResult<T, E> -> booleanCheck if result is Ok
Result.isErrResult<T, E> -> booleanCheck if result is Err
Result.toOptionResult<T, E> -> Option<T>Convert to Option (drops error)
// Transform success value
const doubled = fetchCount()
|> Result.map(fn(n) n * 2)
// Handle errors
const result = fetchUser(id)
|> Result.mapErr(fn(e) AppError(e))
// Chain operations
const profile = fetchUser(id)
|> Result.flatMap(fn(u) fetchProfile(u.profileId))
// Extract with fallback
const count = fetchCount()
|> Result.unwrapOr(0)

Pipe-friendly string operations.

FunctionSignatureDescription
String.trimstring -> stringRemove whitespace from both ends
String.trimStartstring -> stringRemove leading whitespace
String.trimEndstring -> stringRemove trailing whitespace
String.splitstring, string -> Array<string>Split by separator
String.replacestring, string, string -> stringReplace first occurrence
String.startsWithstring, string -> booleanCheck prefix
String.endsWithstring, string -> booleanCheck suffix
String.containsstring, string -> booleanCheck if substring exists
String.toUpperstring -> stringConvert to uppercase
String.toLowerstring -> stringConvert to lowercase
String.lengthstring -> numberCharacter count
String.slicestring, number, number -> stringExtract substring
String.padStartstring, number, string -> stringPad from the start
String.padEndstring, number, string -> stringPad from the end
String.repeatstring, number -> stringRepeat n times
// Pipe-friendly
const cleaned = " Hello, World! "
|> String.trim
|> String.toLower
|> String.replace("world", "floe")
// "hello, floe!"
// Split and process
const words = "one,two,three"
|> String.split(",")
|> Array.map(fn(w) String.toUpper(w))
// ["ONE", "TWO", "THREE"]

Safe numeric operations. Parsing returns Result instead of NaN.

FunctionSignatureDescription
Number.parsestring -> Result<number, ParseError>Strict parse (no partial, no NaN)
Number.clampnumber, number, number -> numberClamp between min and max
Number.isFinitenumber -> booleanCheck if finite
Number.isIntegernumber -> booleanCheck if integer
Number.toFixednumber, number -> stringFormat with fixed decimals
Number.toStringnumber -> stringConvert to string
// Safe parsing - no more NaN surprises
const result = "42" |> Number.parse
// Ok(42)
const bad = "not a number" |> Number.parse
// Err(ParseError)
// Must handle the Result
match Number.parse(input) {
Ok(n) -> processNumber(n),
Err(_) -> showError("Invalid number"),
}
// Clamp to range
const score = rawScore |> Number.clamp(0, 100)

Output functions for debugging. These compile directly to their JavaScript console equivalents.

FunctionSignatureDescription
Console.logT -> ()Log a value
Console.warnT -> ()Log a warning
Console.errorT -> ()Log an error
Console.infoT -> ()Log info
Console.debugT -> ()Log debug info
Console.timestring -> ()Start a named timer
Console.timeEndstring -> ()End a named timer and print duration
Console.log("hello")
Console.warn("careful")
// Timing
Console.time("fetch")
const data = try fetchData()?
Console.timeEnd("fetch")

Standard math functions. Compile directly to JavaScript Math methods.

FunctionSignatureDescription
Math.floornumber -> numberRound down
Math.ceilnumber -> numberRound up
Math.roundnumber -> numberRound to nearest integer
Math.absnumber -> numberAbsolute value
Math.minnumber, number -> numberSmaller of two values
Math.maxnumber, number -> numberLarger of two values
Math.pownumber, number -> numberExponentiation
Math.sqrtnumber -> numberSquare root
Math.signnumber -> numberSign (-1, 0, or 1)
Math.truncnumber -> numberRemove fractional digits
Math.lognumber -> numberNatural logarithm
Math.sinnumber -> numberSine
Math.cosnumber -> numberCosine
Math.tannumber -> numberTangent
Math.randomfn() -> numberRandom number between 0 (inclusive) and 1 (exclusive)
const rounded = 3.7 |> Math.floor // 3
const clamped = Math.max(0, Math.min(score, 100))
const hyp = Math.sqrt(a * a + b * b)

JSON serialization and parsing. JSON.parse returns Result instead of throwing.

FunctionSignatureDescription
JSON.stringifyT -> stringSerialize a value to JSON
JSON.parsestring -> Result<T, ParseError>Parse JSON string safely
const json = user |> JSON.stringify
// '{"name":"Alice","age":30}'
const parsed = json |> JSON.parse
// Ok({name: "Alice", age: 30})
match JSON.parse(input) {
Ok(data) -> process(data),
Err(e) -> Console.error(e),
}

Immutable key-value map operations. All functions return new maps — they never mutate the original.

FunctionSignatureDescription
Map.empty() -> Map<K, V>Create an empty map
Map.fromArrayArray<[K, V]> -> Map<K, V>Create a map from key-value pairs
Map.getMap<K, V>, K -> Option<V>Look up a value by key
Map.setMap<K, V>, K, V -> Map<K, V>Add or update a key-value pair
Map.removeMap<K, V>, K -> Map<K, V>Remove a key-value pair
Map.hasMap<K, V>, K -> booleanCheck if a key exists
Map.keysMap<K, V> -> Array<K>Get all keys
Map.valuesMap<K, V> -> Array<V>Get all values
Map.entriesMap<K, V> -> Array<[K, V]>Get all key-value pairs
Map.sizeMap<K, V> -> numberNumber of entries
Map.isEmptyMap<K, V> -> booleanTrue if map has no entries
Map.mergeMap<K, V>, Map<K, V> -> Map<K, V>Merge two maps (second wins on conflict)
// Create a map from key-value pairs
const config = Map.fromArray([("host", "localhost"), ("port", "8080")])
// All operations are immutable
const updated = config
|> Map.set("port", "3000")
|> Map.set("debug", "true")
// Safe lookup returns Option
const port = Map.get(config, "port") // Some("8080")
const missing = Map.get(config, "foo") // None
// Check membership
const hasHost = config |> Map.has("host") // true
// Convert to arrays
const keys = config |> Map.keys // ["host", "port"]
const values = config |> Map.values // ["localhost", "8080"]
// Merge maps (second map's values win on key conflict)
const defaults = Map.fromArray([("port", "80"), ("host", "0.0.0.0")])
const merged = Map.merge(defaults, config)
// Map { "port" => "8080", "host" => "localhost" }

Immutable unique collection operations. All functions return new sets — they never mutate the original.

FunctionSignatureDescription
Set.empty() -> Set<T>Create an empty set
Set.fromArrayArray<T> -> Set<T>Create a set from an array
Set.toArraySet<T> -> Array<T>Convert a set to an array
Set.addSet<T>, T -> Set<T>Add an element
Set.removeSet<T>, T -> Set<T>Remove an element
Set.hasSet<T>, T -> booleanCheck if an element exists
Set.sizeSet<T> -> numberNumber of elements
Set.isEmptySet<T> -> booleanTrue if set has no elements
Set.unionSet<T>, Set<T> -> Set<T>Union of two sets
Set.intersectSet<T>, Set<T> -> Set<T>Intersection of two sets
Set.diffSet<T>, Set<T> -> Set<T>Difference (elements in first but not second)
// Create a set from an array
const tags = Set.fromArray(["urgent", "bug", "frontend"])
// All operations are immutable
const updated = tags
|> Set.add("backend")
|> Set.remove("frontend")
// Check membership
const isUrgent = tags |> Set.has("urgent") // true
// Set operations
const teamA = Set.fromArray(["alice", "bob", "carol"])
const teamB = Set.fromArray(["bob", "carol", "dave"])
const everyone = Set.union(teamA, teamB) // {"alice", "bob", "carol", "dave"}
const overlap = Set.intersect(teamA, teamB) // {"bob", "carol"}
const onlyA = Set.diff(teamA, teamB) // {"alice"}
// Convert back to array
const tagList = tags |> Set.toArray

Pipe-friendly HTTP functions that return Result natively. No try wrapper needed — errors are captured automatically.

FunctionSignatureDescription
Http.getstring -> Result<Response, Error>GET request
Http.poststring, unknown -> Result<Response, Error>POST request with JSON body
Http.putstring, unknown -> Result<Response, Error>PUT request with JSON body
Http.deletestring -> Result<Response, Error>DELETE request
Http.jsonResponse -> Result<unknown, Error>Parse response body as JSON
Http.textResponse -> Result<string, Error>Read response body as text
// Simple GET and parse JSON
const data = await Http.get("https://api.example.com/users")? |> Http.json?
// POST with a body
const result = await Http.post("https://api.example.com/users", { name: "Alice" })?
// Full pipeline
const users = await Http.get(url)?
|> Http.json?
|> Result.map(fn(data) Array.filter(data, .active))
// Error handling with match
match await Http.get(url) {
Ok(response) -> Http.json(response),
Err(e) -> Console.error(e),
}

All Http functions are async and return Result. Use await and ? for ergonomic error handling in pipelines.


Utility functions for pipeline debugging and control flow.

FunctionSignatureDescription
tapT, fn(T) -> () -> TCall a function for side effects, return value unchanged
// Debug a pipeline without breaking the chain
const result = orders
|> Array.filter(.active)
|> tap(Console.log) // logs filtered orders, passes them through
|> Array.map(.total)
|> Array.reduce(fn(sum, n) sum + n, 0)
// Use a closure for custom logging
const processed = data
|> transform
|> tap(fn(x) Console.log("after transform:", x))
|> validate
// Works with any type
const name = " Alice "
|> String.trim
|> tap(Console.log) // logs "Alice"
|> String.toUpper // "ALICE"

tap is the pipeline equivalent of a console.log that doesn’t interrupt the flow. The function you pass receives the value but its return is ignored — the original value passes through unchanged.