Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Batteries Included: Seq Standard Library

Philosophy

Inspired by Go’s “batteries included” approach:

  • Opinionated: One obvious way to do things
  • Self-sufficient: Build real applications without external dependencies
  • Cohesive: Consistent naming, patterns, and idioms across the stdlib
  • Practical: Focus on what developers actually need, not academic completeness

The Rust Advantage

Seq’s runtime is implemented in Rust, which provides a massive architectural advantage for building a batteries-included stdlib. Instead of:

  • Writing crypto from scratch (dangerous, years of work)
  • Binding to C libraries like OpenSSL (complex, CVE-prone, platform headaches)
  • Building HTTP/TLS stacks from first principles
  • Maintaining fragile C FFI bindings

Seq can leverage Rust’s ecosystem directly:

CapabilityRust CrateQuality
Crypto hashingsha2RustCrypto, audited
HMAChmacRustCrypto, audited
Encryptionaes-gcmRustCrypto, audited
Signaturesed25519-dalekAudited, widely used
HTTP clienthand-rolled over may + rustlsStrand-yielding HTTP/1.1, no ureq dependency
TLSrustlsMemory-safe, modern
RegexregexFastest in class
Compressionflate2, zstdFast, well-maintained
RandomrandIndustry standard
UUIDuuidComplete implementation
DatabaserusqliteMature, stable (not yet integrated)

Pattern Already Proven

This isn’t theoretical - Seq already uses this pattern successfully:

Existing BuiltinRust Foundation
TCP networkingmay (coroutine-aware)
File I/Ostd::fs
Channelscrossbeam
Timestd::time
String opsstd::string
Base64/Hex encodingbase64, hex
Crypto (SHA-256, HMAC, AES-GCM, PBKDF2, Ed25519, Random, UUID)sha2, hmac, aes-gcm, pbkdf2, ed25519-dalek, rand, uuid
Regular expressionsregex
Compressionflate2, zstd
Arena allocatorCustom, but Rust memory safety

Each builtin is a thin FFI wrapper that exposes Rust functionality to Seq. Adding crypto, HTTP client, or regex follows the exact same pattern.

Why This Matters

  1. Security: Audited Rust crates vs. hand-rolled crypto
  2. Speed: Zero-cost abstractions, no interpreter overhead
  3. Reliability: Rust’s type system catches bugs at compile time
  4. Velocity: Days to add features, not months
  5. Maintenance: Crate updates flow through automatically
  6. Cross-platform: Rust handles platform differences

This is Seq’s unfair advantage: a concatenative language with the full power of the Rust ecosystem behind it.


Binary Contents

A seqc build binary contains exactly what the source uses. The runtime ships with every capability built in, and the link removes what the program does not reference.

There are no seqc flags for capability selection. There are no source annotations. The typechecker already knows which builtins each program touches; the linker uses the seq_main reachability boundary to drop the rest. A hello world ships no HTTP code, no TLS, no regex, no crypto, no compression — and pays nothing for their presence in the runtime archive.

This means “batteries included” is honest: adding a builtin to the runtime imposes no cost on programs that do not call it.

Note: The runtime crate (crates/runtime/Cargo.toml) still exposes Cargo features (crypto, http, regex, compression) for builds that bypass seqc and depend on the runtime directly. These are a Cargo-build internal — they are never surfaced through seqc build and have no bearing on what a Seq source program can reference.


Current State

Runtime Builtins (Rust FFI)

CategoryCapabilities
CoreStack ops, arithmetic, booleans, bitwise
TypesInt, Float, Bool, String, Symbol, List, Map, Variant
StringsConcat, split, trim, case conversion, JSON escape
I/Ostdin/stdout, file read/write, path operations
ConcurrencyChannels, strands (green threads), weave (coroutines)
NetworkingTCP (listen/accept/connect/read/write/close/local-port), UDP (bind/send-to/recv-from), TLS client upgrade, HTTP client (GET/POST/PUT/DELETE), DNS resolve
TimeUnix timestamp, high-res time, sleep
TestingAssertions, test runner, pass/fail counts
SerializationSON format (Seq Object Notation)
EncodingBase64 (standard + URL-safe), Hex
CryptoSHA-256, HMAC-SHA256, AES-256-GCM, PBKDF2, Ed25519 signatures, secure random, UUID v4
HTTP ClientGET, POST, PUT, DELETE with TLS support
Regexmatch, find, find-all, replace, captures, split, valid?
Compressiongzip, gunzip, zstd, unzstd with compression levels
OSArgs, env vars, path operations, exec, exit

Standard Library (Pure Seq)

Located in crates/compiler/stdlib/ (~2900 lines):

ModuleLinesDescription
json.seq1166Full JSON parser and encoder
yaml.seq752YAML parser
zipper.seq328Functional list zipper
http.seq190HTTP response building, request parsing
imath.seq145Integer math utilities (abs, min, max, clamp)
fmath.seq109Float math utilities
signal.seq66Signal handling
son.seq57SON serialization helpers
list.seq55List helpers
stack-utils.seq46Stack manipulation utilities
map.seq30Map helpers

HTTP Server Example

examples/http/http_server.seq (18KB) demonstrates:

  • Concurrent request handling with strands
  • Channel-based worker dispatch
  • HTTP routing with cond
  • Closure capture for connection handling
# Working HTTP server pattern
8080 net.tcp.listen
[ conn-id |
  conn-id net.tcp.read
  http-request-path
  cond
    [ "/health" string.equal? ] [ drop "OK" http-ok ]
    [ "/api" string.starts-with ] [ handle-api ]
    [ true ] [ drop "Not Found" http-not-found ]
  end
  conn-id net.tcp.write
  conn-id net.tcp.close
] accept-loop

Gaps for “Batteries Included”

CategoryStatusPriority
HTTP clientCompleteHigh
RegexCompleteMedium
TLS/HTTPSComplete (via rustls)Medium
TemplatesNot startedMedium
seq fmtNot startedMedium
seq.tomlNot startedMedium
LoggingNot startedLow
CompressionCompleteLow
DatabaseNot startedFuture
HTML parsingNot startedFuture

Already Mature

CategoryStatus
JSONComplete (1166 lines)
YAMLComplete (752 lines)
HTTP serverWorking (helpers + example)
HTTP clientComplete (builtin) - GET, POST, PUT, DELETE with TLS
RegexComplete (builtin) - match, find, replace, captures, split
CompressionComplete (builtin) - gzip, zstd with levels
LSPComplete (2200+ lines) - diagnostics, completions
REPLComplete - with LSP integration, vim keybindings
TestingComplete (builtin)
Result/OptionConvention-based (value Bool) pattern, no stdlib module
Base64/HexComplete (builtin) - standard, URL-safe, hex
Crypto Phase 1Complete (builtin) - SHA-256, HMAC, random, UUID
Crypto Phase 2Complete (builtin) - AES-256-GCM encryption, PBKDF2 key derivation
Crypto Phase 3Complete (builtin) - Ed25519 digital signatures

Priority 1: HTTP Client

The HTTP client is a hand-rolled HTTP/1.1 implementation sitting on the may-aware TCP/TLS/DNS layers — every IO step yields the cooperative carrier instead of blocking it. It keeps its own connection pool keyed on (scheme, host, port).

API

# GET request - returns response map
"https://api.example.com/users" net.http.get
# Stack: ( Map ) where Map = { "status": 200, "body": "...", "ok": true }

# POST request with body and content-type
"https://api.example.com/users" "{\"name\":\"Alice\"}" "application/json" net.http.post
# Stack: ( Map )

# PUT request (same signature as POST)
"https://api.example.com/users/1" "{\"name\":\"Bob\"}" "application/json" net.http.put

# DELETE request
"https://api.example.com/users/1" net.http.delete
# Stack: ( Map )

Response Map

All HTTP operations return a Map with these keys:

  • "status" (Int): HTTP status code (200, 404, 500, etc.) or 0 on connection error
  • "body" (String): Response body as text
  • "ok" (Bool): true if status is 2xx, false otherwise
  • "error" (String): Error message (only present on failure)

Example Usage

# Make a GET request and handle the response
"https://httpbin.org/get" net.http.get
dup "ok" map.get drop
if
  "body" map.get drop io.write-line
else
  "error" map.get drop "Error: " swap string.concat io.write-line
then

Implementation Details

  • Transport: hand-rolled HTTP/1.1 over may-aware TCP / rustls TLS
  • TLS: Built-in via rustls with ring crypto provider (no OpenSSL dependency)
  • Per-IO request/response timeout: 30s default, override with SEQ_HTTP_REQUEST_TIMEOUT_MS
  • TLS handshake timeout: 10s default, override with SEQ_TLS_HANDSHAKE_TIMEOUT_MS
  • TCP connect timeout: 10s default, override with SEQ_TCP_CONNECT_TIMEOUT_MS
  • Max body size: 10 MB
  • Connection pooling: Process-wide pool keyed on (scheme, host, port); idle entries return via Connection: keep-alive

Security: SSRF Protection

The HTTP client includes built-in SSRF protection. The following are automatically blocked:

  • Localhost: localhost, *.localhost, 127.x.x.x
  • Private networks: 10.x.x.x, 172.16-31.x.x, 192.168.x.x
  • Link-local/Cloud metadata: 169.254.x.x (blocks AWS/GCP/Azure metadata)
  • IPv6 private: loopback, link-local, unique local addresses
  • Non-HTTP schemes: file://, ftp://, gopher://, etc.

Blocked requests return an error response with ok=false.

Additional recommendations:

  • Use domain allowlists for sensitive applications
  • Apply network-level egress filtering

Priority 2: Regular Expressions

Regex support is implemented via the Rust regex crate (v1.11). Fast, safe, and no catastrophic backtracking.

# Check if pattern matches anywhere in string
"hello world" "wo.ld" regex.match?      # ( String String -- Bool )

# Find first match
"a1 b2 c3" "[a-z][0-9]" regex.find      # ( String String -- String Bool )

# Find all matches
"a1 b2 c3" "[a-z][0-9]" regex.find-all  # ( String String -- List Bool )

# Replace first occurrence
"hello world" "world" "Seq" regex.replace  # ( String String String -- String Bool )

# Replace all occurrences
"a1 b2 c3" "[0-9]" "X" regex.replace-all   # ( String String String -- String Bool )

# Extract capture groups
"2024-01-15" "(\\d+)-(\\d+)-(\\d+)" regex.captures
# ( String String -- List Bool ) returns ["2024", "01", "15"] true

# Split by pattern
"a1b2c3" "[0-9]" regex.split            # ( String String -- List Bool )

# Check if pattern is valid
"[a-z]+" regex.valid?                   # ( String -- Bool )

Examples:

  • examples/text/regex-demo.seq - Demonstrates all regex operations
  • examples/text/log-parser.seq - Practical log parsing with regex

Priority 3: Cryptography

Crypto is essential for real-world applications but often requires hunting through external packages. A batteries-included approach means shipping these out of the box.

Tier 1: Essential

APIRust CrateUse Cases
crypto.sha256sha2Checksums, content addressing, password hashing input
crypto.hmac-sha256hmac + sha2Webhook verification, JWT signing, API auth
crypto.random-bytesrandTokens, nonces, salts, session IDs
crypto.uuid4uuidUnique identifiers
crypto.constant-time-eqcustomTiming-safe comparison for signatures
# Hashing
"hello world" crypto.sha256          # ( String -- String ) hex-encoded

# HMAC for API authentication
"webhook-payload" "secret-key" crypto.hmac-sha256
# ( message key -- signature )

# Verify webhook signature
received-sig computed-sig crypto.constant-time-eq
# ( String String -- Bool ) timing-safe comparison

# Generate secure random token
32 crypto.random-bytes    # ( n -- String ) 32 random bytes as 64-char hex string

# Generate UUID v4
crypto.uuid4    # ( -- String ) "550e8400-e29b-41d4-a716-446655440000"

Tier 2: Encryption

APIRust CrateUse Cases
crypto.aes-gcm-encryptaes-gcmEncrypting data at rest, secure storage
crypto.aes-gcm-decryptaes-gcmDecrypting data
crypto.pbkdf2-sha256pbkdf2Password-based key derivation
# Symmetric encryption (AES-256-GCM)
# Key must be 64 hex chars (32 bytes = 256 bits)
plaintext hex-key crypto.aes-gcm-encrypt   # ( String String -- String Bool )
ciphertext hex-key crypto.aes-gcm-decrypt  # ( String String -- String Bool )

# Key derivation from password
"user-password" "salt" 100000 crypto.pbkdf2-sha256
# ( password salt iterations -- hex-key Bool )

# Full example: derive key and encrypt
"user-password" "unique-salt" 100000 crypto.pbkdf2-sha256
if
  "secret data" swap crypto.aes-gcm-encrypt
  if
    "Encrypted: " swap string.concat io.write-line
  else
    drop "Encryption failed" io.write-line
  then
else
  drop "Key derivation failed" io.write-line
then

Tier 3: Signatures

APIRust CrateUse Cases
crypto.ed25519-keypaired25519-dalekGenerate signing keys
crypto.ed25519-signed25519-dalekDigital signatures
crypto.ed25519-verifyed25519-dalekSignature verification
# Generate keypair
crypto.ed25519-keypair    # ( -- public-key private-key ) both as 64-char hex

# Sign a message
message private-key crypto.ed25519-sign    # ( String String -- String Bool )

# Verify signature
message signature public-key crypto.ed25519-verify  # ( String String String -- Bool )

# Full example
crypto.ed25519-keypair
"Important document" swap crypto.ed25519-sign
if
  swap "Important document" rot rot crypto.ed25519-verify
  if "Signature valid!" else "Signature invalid!" then io.write-line
else
  drop drop "Signing failed" io.write-line
then

Encoding Helpers

Available as encoding.* builtins:

# Base64 (standard with padding)
"hello" encoding.base64-encode    # ( String -- String ) "aGVsbG8="
"aGVsbG8=" encoding.base64-decode # ( String -- String Bool )

# URL-safe Base64 (no padding, for JWTs/URLs)
data encoding.base64url-encode    # ( String -- String )
encoded encoding.base64url-decode # ( String -- String Bool )

# Hex (lowercase output, case-insensitive decode)
"hello" encoding.hex-encode       # ( String -- String ) "68656c6c6f"
"68656c6c6f" encoding.hex-decode  # ( String -- String Bool )

Priority 4: Compression

Data compression via gzip and Zstandard (zstd). All operations use base64 encoding for string-safe output.

API

# Gzip compression (default level 6)
"hello world" compress.gzip              # ( String -- String Bool )

# Gzip with custom level (1-9, where 1=fastest, 9=best)
"hello world" 9 compress.gzip-level      # ( String Int -- String Bool )

# Gzip decompression
compressed compress.gunzip               # ( String -- String Bool )

# Zstandard compression (default level 3)
"hello world" compress.zstd              # ( String -- String Bool )

# Zstandard with custom level (1-22, where 1=fastest, 22=best)
"hello world" 19 compress.zstd-level     # ( String Int -- String Bool )

# Zstandard decompression
compressed compress.unzstd               # ( String -- String Bool )

Return Values

All compression operations return ( String Bool ):

  • On success: compressed-data true (data is base64-encoded)
  • On failure: error-message false

Decompression accepts base64-encoded input and returns the original string.

Example Usage

# Compress and decompress with gzip
"Hello, World!" compress.gzip
if
  dup "Compressed: " swap string.concat io.write-line
  compress.gunzip
  if
    "Decompressed: " swap string.concat io.write-line
  else
    drop "Decompression failed" io.write-line
  then
else
  drop "Compression failed" io.write-line
then

# Compare compression algorithms
"Large text data..." dup
compress.gzip if string.length else drop 0 then
swap compress.zstd if string.length else drop 0 then
# Compare sizes

When to Use Each

AlgorithmBest ForLevel Range
gzipWeb content, HTTP compression, broad compatibility1-9
zstdLarge data, better ratio, modern systems1-22
  • gzip: Universal compatibility, good for HTTP Content-Encoding
  • zstd: Better compression ratio and speed, ideal for data storage

Implementation Details

  • Crates: flate2 (gzip), zstd (Zstandard)
  • Output encoding: Base64 for string-safe transport
  • Input/Output: String → compressed base64 String

Examples:

  • examples/io/compress-demo.seq - Demonstrates all compression operations

Design Principles

1. Composition Over Configuration

# Good: Composable pieces
request
  auth-middleware
  logging-middleware
  rate-limit-middleware
  handler
http.handle

# Avoid: Giant config objects

2. Stack-Friendly APIs

# Good: Works naturally on stack
users [ is-active ] list.filter

# Avoid: Deeply nested structures that fight the stack

3. Explicit Over Magic

# Good: Clear what happens
response "body" map.get drop json-parse drop

# Avoid: Hidden transformations

4. Errors as Values

# Use (value Bool) pattern
"data.txt" file.slurp  # ( String -- String Bool )
if
  process-content
else
  drop "File not found" io.write-line
then

5. Consistent Naming

PatternExampleMeaning
noun.verbnet.http.get, json-serializeAction on type
noun?list.empty?, map.has?Predicate
->string->int, int->floatConversion

Comparison: Seq vs Go

AspectGoSeq
ParadigmImperative, structuralStack-based, functional
ConcurrencyGoroutines + channelsStrands + channels
Error handlingerror return valueResult types on stack
GenericsType parametersRow polymorphism
Buildgo buildseqc build
PackagesModule pathinclude std:module
Std library~150 packages~15 modules (focused)

References