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

String Map Set Time Random Path File Dir Env Regex Uuid Crypto Http Json Pg Float32Array Float64Array Int64Array

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