Standard Library
Nostos comes with a comprehensive standard library providing utilities for strings, collections, time, file system, cryptography, and more. All modules are available by default without imports.
Available Modules
List functions (map, filter, fold, etc.) are available as methods on lists directly.
String Module
String manipulation functions for common text operations.
main() = {
# Length and case
println("hello".length()) # 5
println("hello".toUpper()) # "HELLO"
println("WORLD".toLower()) # "world"
# Searching
println("hello".contains("ell")) # true
println("hello".startsWith("he")) # true
println("hello".endsWith("lo")) # true
println("hello".indexOf("l")) # 2
# Extraction
println("hello".substring(1, 4)) # "ell"
chars = "hello".chars() # ['h', 'e', 'l', 'l', 'o']
# Transformation
println(" hi ".trim()) # "hi"
println("hello".replace("l", "L")) # "heLlo" (first only)
println("ab".repeat(3)) # "ababab"
println("hello".reverse()) # "olleh"
# Split and join (use Regex)
parts = Regex.split(",", "a,b,c") # ["a", "b", "c"]
println("hello".lines()) # Split by newlines
println("hello world".words()) # Split by whitespace
}
Map Module
Immutable key-value maps with efficient lookups.
main() = {
# Create a map
m = %{"a": 1, "b": 2, "c": 3}
# Basic operations
println(m.get("a")) # 1
println(m.get("x")) # () (unit if missing)
println(m.contains("b")) # true
println(m.size()) # 3
# Modification (returns new map)
m2 = m.insert("d", 4)
m3 = m2.remove("a")
# Keys and values
println(m.keys()) # ["a", "b", "c"]
println(m.values()) # [1, 2, 3]
println(m.toList()) # [("a", 1), ("b", 2), ("c", 3)]
# From list of tuples
pairs = [("x", 10), ("y", 20)]
m4 = Map.fromList(pairs)
}
Set Module
Immutable sets with unique elements.
main() = {
# Create a set
s = #{1, 2, 3, 4, 5}
# Basic operations
println(s.contains(3)) # true
println(s.size()) # 5
# Modification (returns new set)
s2 = s.insert(6)
s3 = s2.remove(1)
# Set operations
a = #{1, 2, 3}
b = #{2, 3, 4}
println(a.union(b)) # #{1, 2, 3, 4}
println(a.intersection(b)) # #{2, 3}
println(a.difference(b)) # #{1}
# Convert to list
println(s.toList()) # [1, 2, 3, 4, 5]
}
Time Module
Date and time operations with Unix timestamps.
main() = {
# Current time
now = Time.now()
println("Timestamp: " ++ now.show())
# Format as string
println(now.format("%Y-%m-%d %H:%M:%S"))
# Extract components
println("Year: " ++ now.year().show())
println("Month: " ++ now.month().show())
println("Day: " ++ now.day().show())
println("Hour: " ++ now.hour().show())
println("Minute: " ++ now.minute().show())
println("Second: " ++ now.second().show())
# Parse from string
ts = Time.parse("2024-06-15 10:30:00", "%Y-%m-%d %H:%M:%S")
# Arithmetic (seconds)
tomorrow = now.add(86400)
yesterday = now.subtract(86400)
}
Random Module
Random number generation and selection.
main() = {
# Random integers in range [min, max]
n = Random.int(1, 100)
println("Random int: " ++ show(n))
# Random float in [0, 1)
f = Random.float()
println("Random float: " ++ show(f))
# Random boolean
b = Random.bool()
println("Random bool: " ++ show(b))
# Pick random element from list
colors = ["red", "green", "blue"]
color = Random.choice(colors)
println("Random color: " ++ color)
# Shuffle a list
nums = [1, 2, 3, 4, 5]
shuffled = Random.shuffle(nums)
println("Shuffled: " ++ show(shuffled))
}
Path Module
File path manipulation utilities.
main() = {
# Join path components
println(Path.join("/home", "user")) # "/home/user"
println(Path.join("/home", "user", "docs")) # "/home/user/docs"
# Extract components
println(Path.dirname("/home/user/file.txt")) # "/home/user"
println(Path.basename("/home/user/file.txt")) # "file.txt"
println(Path.extension("document.pdf")) # "pdf"
# Path type checks
println(Path.isAbsolute("/home")) # true
println(Path.isAbsolute("./file")) # false
println(Path.isRelative("./file")) # true
# Normalize path
println(Path.normalize("/home/../etc")) # "/etc"
}
Env Module
Access environment variables.
main() = {
# Get environment variable (returns String if set, () if not)
home = Env.get("HOME")
println("Home: " ++ home)
# Check with show() for potentially unset vars
editor = Env.get("EDITOR")
println("Editor: " ++ show(editor)) # Prints "nano" or "()"
# Current working directory
cwd = Env.cwd()
println("CWD: " ++ cwd)
# Command line arguments
args = Env.args()
println("Args: " ++ show(args))
}
Regex Module
Regular expression pattern matching.
main() = {
text = "The quick brown fox"
# Check if pattern matches: matches(text, pattern)
println(Regex.matches(text, "quick")) # true
println(Regex.matches(text, "slow")) # false
# Find first match: find(text, pattern)
match Regex.find(text, "[a-z]+") {
Some(m) -> println("Found: " ++ m) # "he"
None -> println("No match")
}
# Find all matches: findAll(text, pattern)
matches = Regex.findAll(text, "[a-zA-Z]+")
println(matches) # ["The", "quick", "brown", "fox"]
# Replace: replace(text, pattern, replacement)
result = Regex.replace(text, "fox", "dog")
println(result) # "The quick brown dog"
# Split: split(text, pattern)
parts = Regex.split(text, " ")
println(parts) # ["The", "quick", "brown", "fox"]
}
UUID Module
Generate and validate UUIDs.
main() = {
# Generate a random UUID v4
id = Uuid.v4()
println("Generated UUID: " ++ id)
# Example: "550e8400-e29b-41d4-a716-446655440000"
# Validate UUID format
println(Uuid.isValid(id)) # true
println(Uuid.isValid("not-a-uuid")) # false
println(Uuid.isValid("550e8400-e29b-41d4-a716-446655440000")) # true
}
Crypto Module
Cryptographic hashing and password functions.
main() = {
# Hash functions (return hex strings)
println(Crypto.sha256("hello"))
# "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824"
println(Crypto.sha512("hello"))
# "9b71d224bd62f3785d96d46ad3ea3d73..."
println(Crypto.md5("hello")) # Insecure, for compatibility
# "5d41402abc4b2a76b9719d911017c592"
# Password hashing with bcrypt
password = "secret123"
hash = Crypto.bcryptHash(password, 10) # cost factor 10
println("Hash: " ++ hash)
# Verify password
isValid = Crypto.bcryptVerify(password, hash)
println("Valid: " ++ show(isValid)) # true
isWrong = Crypto.bcryptVerify("wrong", hash)
println("Wrong: " ++ show(isWrong)) # false
# Generate random bytes (as hex)
randomHex = Crypto.randomBytes(16)
println("Random: " ++ randomHex) # 32 hex chars
}
Security Note: Use bcrypt for password storage. MD5 is provided only for legacy compatibility and should not be used for security purposes. SHA-256/512 are suitable for data integrity checks.
File Module
File system operations for reading and writing files. All operations throw exceptions on error - use try/catch for error handling.
main() = {
# Write to file (throws on error)
File.writeAll("/tmp/test.txt", "Hello, World!")
# Read entire file (throws if file doesn't exist)
content = File.readAll("/tmp/test.txt")
println(content) # "Hello, World!"
# Check if file exists (returns Bool, doesn't throw)
println(File.exists("/tmp/test.txt")) # true
# Get file size in bytes
println(File.size("/tmp/test.txt")) # 13
# Delete file
File.remove("/tmp/test.txt")
# Error handling with try/catch
try {
data = File.readAll("/nonexistent.txt")
println(data)
} catch { e ->
println("File error: " ++ e)
}
# File handles for streaming access
handle = File.open("/tmp/data.txt", "w") # modes: "r", "w", "a", "rw"
File.write(handle, "Line 1\n")
File.write(handle, "Line 2\n")
File.close(handle)
}
Dir Module
Directory operations. Like file operations, these throw exceptions on error.
main() = {
# Create directory (throws if parent doesn't exist)
Dir.create("/tmp/mydir")
# Create nested directories (creates all parents)
Dir.createAll("/tmp/nested/path/here")
# List directory contents (returns [String])
files = Dir.list("/tmp")
println(files) # List of filenames
# Check if directory exists (returns Bool)
println(Dir.exists("/tmp")) # true
# Remove empty directory
Dir.remove("/tmp/mydir")
# Remove directory and all contents recursively
Dir.removeAll("/tmp/nested")
# Error handling
try {
Dir.create("/root/forbidden")
} catch { e ->
println("Permission denied: " ++ e)
}
}
List Functions
List operations are available as functions that support method chaining. These are defined in the standard library and automatically available.
main() = {
xs = [1, 2, 3, 4, 5]
# Access (head/tail are builtins)
println(head(xs)) # 1
println(tail(xs)) # [2, 3, 4, 5]
println(last(xs)) # 5
println(xs.get(2)) # 3 (0-indexed)
# Transform with method chaining
doubled = xs.map(x => x * 2) # [2, 4, 6, 8, 10]
evens = xs.filter(x => x % 2 == 0) # [2, 4]
sum = xs.fold(0, (acc, x) => acc + x) # 15
# More list operations
println(xs.reverse()) # [5, 4, 3, 2, 1]
println(xs.take(3)) # [1, 2, 3]
println(xs.drop(2)) # [3, 4, 5]
# Combine
ys = [6, 7, 8]
println(concat(xs, ys)) # [1, 2, 3, 4, 5, 6, 7, 8]
println(flatten([[1,2], [3,4]])) # [1, 2, 3, 4]
# Search
println(xs.contains(3)) # true
println(xs.find(x => x > 3)) # Some(4)
# Chained operations
result = [1, 2, 3, 4, 5]
.filter(x => x > 2)
.map(x => x * 10)
.fold(0, (a, b) => a + b)
println(result) # 120
}
Pg Module (PostgreSQL)
Connect to PostgreSQL databases including cloud providers like Supabase and Neon.
# Connect to local PostgreSQL
conn = Pg.connect("host=localhost user=postgres password=secret dbname=mydb")
# Connect to cloud providers (Supabase, Neon) with TLS
conn = Pg.connect("postgresql://user:pass@db.xxx.supabase.co:5432/postgres?sslmode=require")
conn = Pg.connect("postgresql://user:pass@ep-xxx.neon.tech/neondb?sslmode=require")
# Query returns list of tuples - access fields with .0, .1, etc.
rows = Pg.query(conn, "SELECT id, name FROM users WHERE age > $1", 18)
# rows is: [(1, "Alice"), (2, "Bob")]
# Access: head(rows).0 => 1, head(rows).1 => "Alice"
# Params auto-normalize: () for none, bare value for one, tuple/list for multiple
Pg.execute(conn, "INSERT INTO users (name) VALUES ($1)", "Charlie")
# Tuples support mixed types
Pg.execute(conn, "INSERT INTO users (id, name, score) VALUES ($1, $2, $3)", (1, "Alice", 95.5))
# No parameters - use unit ()
rows = Pg.query(conn, "SELECT id, name, email FROM users", ())
rows.each(row => {
println("User " ++ show(row.0) ++ ": " ++ row.1 ++ " <" ++ row.2 ++ ">")
})
# Transactions
Pg.begin(conn)
Pg.execute(conn, "UPDATE accounts SET balance = balance - 100 WHERE id = $1", 1)
Pg.commit(conn) # or Pg.rollback(conn)
# JSON support (for JSONB columns)
Pg.execute(conn, "INSERT INTO docs (data) VALUES ($1)", ["{\"key\": \"value\"}"])
# Vector support (pgvector extension) - use Float32Array for native f32 format
embedding = Float32Array.fromList([0.1, 0.2, 0.3])
Pg.execute(conn, "INSERT INTO items (embedding) VALUES ($1)", [embedding])
# Query vectors - returned as Float32Array, access via tuple field
rows = Pg.query(conn, "SELECT embedding FROM items WHERE id = $1", 1)
vec = head(rows).0 # Float32Array (first field of first row)
values = Float32Array.toList(vec) # Convert to list
# Similarity search (L2 distance)
query = Float32Array.fromList([0.1, 0.2, 0.3])
results = Pg.query(conn, "SELECT id, embedding <-> $1 as distance FROM items ORDER BY distance LIMIT 5", [query])
# Cosine similarity search
results = Pg.query(conn, "SELECT id, 1 - (embedding <=> $1) as similarity FROM items ORDER BY similarity DESC LIMIT 5", [query])
# Close connection
Pg.close(conn)
Connection Pooling
Nostos automatically pools PostgreSQL connections. When you call Pg.close(), the connection returns to the pool for reuse. Subsequent Pg.connect() calls to the same URL reuse pooled connections instead of creating new ones.
# Connection reuse example
conn1 = Pg.connect("postgresql://localhost/mydb")
Pg.query(conn1, "SELECT 1", [])
Pg.close(conn1) # Returns to pool
conn2 = Pg.connect("postgresql://localhost/mydb") # Reuses pooled connection
Pg.query(conn2, "SELECT 2", [])
Pg.close(conn2)
- Automatic: No configuration needed
- Per-URL pools: Each connection string gets its own pool
- TLS support: Works with local and cloud databases
Float32Array Module
32-bit float arrays, ideal for AI/ML embeddings and pgvector. Native format for vector databases.
# Create from list
arr = Float32Array.fromList([1.0, 2.0, 3.0])
# Create with size and initial value
zeros = Float32Array.make(128, 0.0) # 128-dim zero vector
# Access elements
len = Float32Array.length(arr) # 3
val = Float32Array.get(arr, 0) # 1.0
# Set returns new array (functional style)
arr2 = Float32Array.set(arr, 0, 99.0)
# Convert back to list
list = Float32Array.toList(arr) # [1.0, 2.0, 3.0]
Float64Array Module
64-bit float arrays for high-precision numerical computing.
# Create from list
arr = Float64Array.fromList([1.0, 2.0, 3.0])
# Create with size and initial value
arr = Float64Array.make(100, 0.0)
# Access elements
len = Float64Array.length(arr) # 3
val = Float64Array.get(arr, 0) # 1.0
# Set returns new array (immutable)
arr2 = Float64Array.set(arr, 0, 99.0)
# Convert back to list
list = Float64Array.toList(arr) # [1.0, 2.0, 3.0]
Standard Library Summary
The Nostos standard library provides everything needed for practical application development:
- Text: String, Regex for text processing
- Collections: Map, Set, and list functions (map, filter, fold)
- I/O: File, Dir for file system access
- System: Env, Path for system integration
- Time: Time for dates and timestamps
- Security: Crypto, Uuid for cryptographic operations
- Network: Http, Json for web services
- Database: Pg for PostgreSQL (with TLS, JSON, vectors)
- Random: Random for random number generation
- Arrays: Float32Array, Float64Array, Int64Array for typed arrays