Skip to content

Standard Library

Skein ships with 11 standard library modules available in every Skein program without imports. Stdlib calls compile to static function calls on the corresponding Skein.Runtime.Stdlib.* Elixir module.

Text operations. All functions operate on UTF-8 binaries.

let name = " Hello, World! "
String.trim(name) -- "Hello, World!"
String.upcase("hello") -- "HELLO"
String.length("hi") -- 2
String.contains("abc", "b") -- true
String.split("a,b,c", ",") -- ["a", "b", "c"]
String.replace("hi", "h", "H") -- "Hi"
FunctionSignatureDescription
length(String) -> IntNumber of Unicode graphemes
slice(String, Int, Int) -> StringSubstring from offset with length
contains(String, String) -> BoolWhether string contains substring
split(String, String) -> List[String]Split on delimiter
trim(String) -> StringRemove leading/trailing whitespace
upcase(String) -> StringConvert to uppercase
downcase(String) -> StringConvert to lowercase
starts_with(String, String) -> BoolCheck prefix
ends_with(String, String) -> BoolCheck suffix
replace(String, String, String) -> StringReplace all occurrences

Integer operations.

Int.parse("42") -- Ok(42)
Int.abs(-5) -- 5
Int.clamp(15, 0, 10) -- 10
Int.min(3, 7) -- 3
FunctionSignatureDescription
parse(String) -> Result[Int, String]Parse string to integer
to_string(Int) -> StringConvert to string
abs(Int) -> IntAbsolute value
min(Int, Int) -> IntSmaller of two values
max(Int, Int) -> IntLarger of two values
clamp(Int, Int, Int) -> IntConstrain to range [min, max]

Floating-point operations.

Float.parse("3.14") -- Ok(3.14)
Float.round(3.456, 2) -- 3.46
Float.ceil(2.1) -- 3
Float.floor(2.9) -- 2
FunctionSignatureDescription
parse(String) -> Result[Float, String]Parse string to float
to_string(Float) -> StringConvert to string
round(Float, Int) -> FloatRound to N decimal places
ceil(Float) -> IntRound up to the nearest integer
floor(Float) -> IntRound down to the nearest integer

Collection operations. Lists are ordered, homogeneous sequences: a List[T] holds elements that all share one type T, so a mixed literal like [1, "two"] is a compile error.

fn double(x: Int) -> Int { x * 2 }
fn over_three(x: Int) -> Bool { x > 3 }
fn add(acc: Int, x: Int) -> Int { acc + x }
let nums = [1, 2, 3, 4, 5]
List.map(nums, &double) -- [2, 4, 6, 8, 10]
List.filter(nums, &over_three) -- [4, 5]
List.reduce(nums, 0, &add) -- 15
List.first(nums) -- Some(1)
List.reverse(nums) -- [5, 4, 3, 2, 1]
FunctionSignatureDescription
length(List[T]) -> IntNumber of elements
map(List[T], (T) -> U) -> List[U]Transform each element
filter(List[T], (T) -> Bool) -> List[T]Keep elements matching predicate
reduce(List[T], U, (U, T) -> U) -> UFold left with accumulator
find(List[T], (T) -> Bool) -> Option[T]First matching element
first(List[T]) -> Option[T]First element
last(List[T]) -> Option[T]Last element
head(List[T]) -> Option[T]Alias for first
tail(List[T]) -> List[T]All elements except the first
take(List[T], Int) -> List[T]First N elements
drop(List[T], Int) -> List[T]All except first N elements
sort(List[T]) -> List[T]Sort in ascending order
sort_by(List[T], (T) -> U) -> List[T]Sort by key function
reverse(List[T]) -> List[T]Reverse order
flatten(List[List[T]]) -> List[T]Flatten one level of nesting
concat(List[T], List[T]) -> List[T]Join two lists
contains(List[T], T) -> BoolMembership test
any(List[T], (T) -> Bool) -> BoolTrue if any element matches
all(List[T], (T) -> Bool) -> BoolTrue if all elements match
none(List[T], (T) -> Bool) -> BoolTrue if no element matches
zip(List[T], List[U]) -> List[[T, U]]Pair elements from two lists
uniq(List[T]) -> List[T]Remove duplicates
count(List[T], (T) -> Bool) -> IntCount matching elements
group_by(List[T], (T) -> K) -> Map[K, List[T]]Group by key function

Key-value operations. Maps are unordered collections of unique keys to values.

let m = Map.put({}, "name", "Alice")
Map.get(m, "name") -- Some("Alice")
Map.keys(m) -- ["name"]
Map.has(m, "email") -- false

Record literals like { name: "Alice", age: 30 } compile with atom keys — they are payload values for typed fields, respond.json, and effect calls, and their keys do not match the string keys used by Map.get/Map.has. Build string-keyed maps with Map.put when you need keyed lookups.

FunctionSignatureDescription
get(Map[K,V], K) -> Option[V]Lookup by key
put(Map[K,V], K, V) -> Map[K,V]Insert or update key
delete(Map[K,V], K) -> Map[K,V]Remove key
keys(Map[K,V]) -> List[K]All keys
values(Map[K,V]) -> List[V]All values
entries(Map[K,V]) -> List[[K,V]]All key-value pairs
size(Map[K,V]) -> IntNumber of entries
has(Map[K,V], K) -> BoolKey existence check
merge(Map[K,V], Map[K,V]) -> Map[K,V]Merge maps (second wins on conflicts)
map_values(Map[K,V], (V) -> U) -> Map[K,U]Transform all values
filter(Map[K,V], (K,V) -> Bool) -> Map[K,V]Keep matching entries

There is no raising Map lookup. To read a key with a fallback, unwrap the Option: Option.unwrap(Map.get(m, key), default).

Unique collections. Sets are unordered collections of unique values.

let s = Set.from([1, 2, 3])
Set.contains(s, 2) -- true
Set.union(s, Set.from([3, 4])) -- Set{1, 2, 3, 4}
Set.size(s) -- 3
FunctionSignatureDescription
from(List[T]) -> Set[T]Create set from list
add(Set[T], T) -> Set[T]Add element
remove(Set[T], T) -> Set[T]Remove element
contains(Set[T], T) -> BoolMembership test
size(Set[T]) -> IntNumber of elements
union(Set[T], Set[T]) -> Set[T]Union of two sets
intersection(Set[T], Set[T]) -> Set[T]Intersection of two sets
difference(Set[T], Set[T]) -> Set[T]Elements in first but not second
to_list(Set[T]) -> List[T]Convert to list

Optional values. Used when a value may or may not exist.

Values are Some(value) or None. Functions like List.find and Map.get return Option.

fn increment(n: Int) -> Int { n + 1 }
let x = List.first([42]) -- Some(42)
Option.unwrap(x, 0) -- 42 (returns 0 on None)
Option.map(x, &increment) -- Some(43)
Option.is_some(x) -- true

There is no Some(...)/None constructor syntax: Option values come from stdlib functions such as List.first, List.find, and Map.get.

FunctionSignatureDescription
unwrap(Option[T], T) -> TExtract value, or the default on None
map(Option[T], (T) -> U) -> Option[U]Transform inner value if present
flat_map(Option[T], (T) -> Option[U]) -> Option[U]Chain optional operations
is_some(Option[T]) -> BoolTrue if Some
is_none(Option[T]) -> BoolTrue if None

Error handling. Used for operations that can succeed or fail.

Values are Ok(value) or Err(error). Effect calls and parsing functions return Result.

fn increment(n: Int) -> Int { n + 1 }
let r = Ok(42)
Result.unwrap(r, 0) -- 42 (returns 0 on Err)
Result.map(r, &increment) -- Ok(43)
Result.is_ok(r) -- true
FunctionSignatureDescription
unwrap(Result[T,E], T) -> TExtract value, or the default on Err
map(Result[T,E], (T) -> U) -> Result[U,E]Transform success value
map_err(Result[T,E], (E) -> F) -> Result[T,F]Transform error value
flat_map(Result[T,E], (T) -> Result[U,E]) -> Result[U,E]Chain fallible operations
is_ok(Result[T,E]) -> BoolTrue if Ok
is_err(Result[T,E]) -> BoolTrue if Err
ok(T) -> Result[T,E]Wrap value in Ok
err(E) -> Result[T,E]Wrap error in Err

UUID parsing and formatting (v4). Generating a UUID is nondeterministic, so it is the capability-gated uuid.new() effect (requires capability uuid), not a stdlib function — see the Effects reference.

let id = uuid.new() -- effect: "550e8400-e29b-41d4-a716-446655440000"
Uuid.parse("...") -- stdlib: Ok(uuid)
FunctionSignatureDescription
parse(String) -> Result[Uuid, String]Parse and validate UUID string
to_string(Uuid) -> StringFormat UUID as string

Timestamps. Represents a point in time. Reading the current time is nondeterministic, so it is the capability-gated instant.now() effect (requires capability instant), not a stdlib function; the operations below are pure stdlib.

let now = instant.now() -- effect (requires capability instant)
let later = Instant.add(now, Duration.hours(2))
Instant.is_before(now, later) -- true
Instant.diff(later, now) -- Duration (2 hours)
FunctionSignatureDescription
parse(String) -> Result[Instant, String]Parse ISO 8601 string
to_string(Instant) -> StringFormat as ISO 8601
add(Instant, Duration) -> InstantAdd duration to instant
subtract(Instant, Duration) -> InstantSubtract duration from instant
diff(Instant, Instant) -> DurationTime between two instants
is_before(Instant, Instant) -> BoolChronological comparison
is_after(Instant, Instant) -> BoolChronological comparison

Time spans. Used with Instant for time arithmetic and with timer.* for scheduling.

let d = Duration.minutes(30)
Duration.to_seconds(d) -- 1800
Duration.to_string(d) -- "30m"
FunctionSignatureDescription
seconds(Int) -> DurationCreate from seconds
minutes(Int) -> DurationCreate from minutes
hours(Int) -> DurationCreate from hours
days(Int) -> DurationCreate from days
to_seconds(Duration) -> IntConvert to total seconds
to_string(Duration) -> StringHuman-readable string

Stdlib calls compile to static function calls on the corresponding runtime module:

-- Skein source
String.upcase("hello")
-- Compiles to (Core Erlang)
call 'Elixir.Skein.Runtime.Stdlib.String':'upcase'("hello")

The code generator recognizes stdlib calls by matching the module name against @stdlib_modules in the code generator. No capabilities are required for stdlib calls since they perform no I/O effects and are deterministic. The two nondeterministic generators — uuid.new() and instant.now() — are not stdlib; they are capability-gated effects so their values can be controlled under test and replay.