Cookbook
Practical recipes for common programming tasks in Suji.
Overview
The Cookbook provides ready-to-use solutions for everyday programming challenges. Each recipe includes:
- Complete working code
- Step-by-step explanations
- Real-world use cases
- Common variations
- Best practices
Every code block on these pages is a complete program: it creates whatever input it needs — a literal, or a temp file it cleans up afterwards — so you can copy one into recipe.si and run suji recipe.si unchanged. The only exception is HTTP with curl, whose recipes make real network requests.
Recipe Categories
File Processing
Work with files efficiently:
- Reading Files Line by Line - Stream a file with
read_line()instead of loading it - Processing CSV Files - Parse and transform CSV data
- Log File Analysis - Extract insights from log files
- Batch File Operations - Rename, copy or back up many files
- Directory Traversal - Walk directory trees with
find - Checking a File Exists - Test paths without aborting the script
Data Transformation
Transform data between formats:
- JSON to YAML - Convert between data formats
- CSV to JSON - Transform tabular data
- Data Filtering - Filter datasets with predicates
- Nested Data Manipulation - Work with complex structures
- Aggregation and Grouping - Summarize data
- Data Validation - Validate data structures
Configuration Management
Manage application configuration:
- Loading Config - Read from multiple sources
- Environment Settings - Handle different environments
- Config Validation - Ensure valid configuration
- Config Merging - Combine configuration objects
- Type-Safe Access - Access config safely
Working with APIs
HTTP and API integration:
- Making HTTP Requests - GET, POST, PUT, DELETE with
curl - JSON API Consumption - Parse API responses
- Authentication - Handle API keys and tokens
- Error Handling - Graceful failure handling
- Rate Limiting - Respect API limits
- Pagination - Handle paginated responses
Text Processing
String manipulation and regex:
- Email Validation - Validate email formats
- URL Extraction - Extract URLs from text
- Log Parsing - Parse structured logs
- Template Generation - Generate text from templates
- Text Search and Replace - Normalize and rewrite text
Scripting Tasks
Automation and workflows:
- Script Arguments - Read
env:argssafely - Standard Input - Consume piped input
- Shell Commands - Backticks, quoting and failure handling
- Pipelines - Pipe closures into commands and back
- Retry and Poll Loops - Wait for something to become ready
- Backup Script - A complete worked script
Quick Examples
Read and Process CSV
import std:csv
import std:io
import std:os
import std:println
path = `mktemp`
f = io:open(path, true, true)
f::write("name,age\nAlice,34\nBob,12\nCarol,29\n")
f::close()
file = io:open(path)
rows = csv:parse(file::read_all())
file::close()
adults = rows[1;]
::filter(|row| row[1]::to_number() >= 18)
::map(|row| row[0])
println("Adults: ${adults::join(", ")}") # Adults: Alice, Carol
os:rm(path)
Parse API Data
import std:json
import std:println
text = '[{"name": "Alice"}, {"name": "Bob"}]'
users = json:parse(text)
println("Loaded ${users::length()} users") # Loaded 2 users
Swap the literal for `curl -fsSL <url>` to read the same shape off the network — see HTTP with curl.
Process Log Files
import std:io
import std:os
import std:println
path = `mktemp`
f = io:open(path, true, true)
f::write("INFO started\nERROR disk full\nERROR disk full\n")
f::close()
file = io:open(path)
lines = file::read_lines()
file::close()
errors = lines::filter(|line| line ~ /ERROR/)
println("Found ${errors::length()} errors") # Found 2 errors
os:rm(path)
Generate Report
import std:io
import std:os
import std:println
data = { total: 12500, average: 4166.67 }
report = """<html>
<head><title>Sales Report</title></head>
<body>
<h1>Sales Report</h1>
<p>Total Sales: ${data:total}</p>
<p>Average: ${data:average}</p>
</body>
</html>"""
path = `mktemp`
out_file = io:open(path, true, true) # create=true, truncate=true
out_file::write(report)
out_file::close()
println(`grep -c '<p>' ${path}`) # 2
os:rm(path)
How to Use This Cookbook
- Browse by Category - Find recipes related to your task
- Copy and Adapt - Start with working code, modify for your needs
- Understand the Pattern - Learn the underlying approach
- Experiment - Try variations and extensions
Common Patterns
Defensive Checking Pattern
Suji has no exceptions: a runtime error prints a diagnostic and ends the process. The only strategy is to check before you act, and to return a (value, error) tuple instead of throwing.
import std:io
import std:println
exists = |p| `test -f "${p}" && echo yes || echo no` == "yes"
process_file = |filename| {
!exists(filename) && return (nil, "File not found")
file = io:open(filename)
content = file::read_all()
file::close()
return (content::length(), nil)
}
size, error = process_file("/no/such/file")
match {
error != nil => { println("Error: ${error}") },
_ => { println("Read ${size} bytes") },
}
Output:
Error: File not found
Pipeline Pattern
Chain transformations with method calls, or compose named steps with >>:
import std:println
validate = |xs| xs::filter(|x| x::is_number())
transform = |xs| xs::map(|x| x * 2)
summarize = |xs| xs::sum()
pipeline = validate >> transform >> summarize
println(pipeline([1, "two", 3, 4])) # 16
Configuration Pattern
Layer defaults, file values and environment variables, then validate the result:
import std:env
import std:println
defaults = { port: 8080, log_level: "info" }
file_config = { log_level: "debug" }
# merge mutates the receiver, so copy the defaults first
config = defaults
config::merge(file_config)
override = env:var::get("APP_PORT", nil)
match { override != nil => { config["port"] = override::to_number() } }
println("${config:port} / ${config:log_level}") # 8080 / debug
Tips for Success
DO:
- Start with working examples
- Check for missing keys, missing files and empty lists before using them
- Use
map::get(key, default)for anything optional - Break complex tasks into small named functions
- Guard shell commands that may fail with
|| true
DON’T:
- Expect
try/catch,if/else,fororwhile— Suji has none of them - Rely on truthiness;
&&,||and!require real booleans - Read a huge file with
read_all()whenread_line()will do - Hardcode configuration or absolute paths
- Assume a non-zero exit status from a command is recoverable
Next Steps
Start with the recipe category most relevant to your task:
- File Processing - Working with files and directories
- Data Transformation - Converting and filtering data
- Configuration - Managing app configuration
- APIs - HTTP requests and API integration
- Text Processing - String manipulation and regex
- Scripting - Automation and workflows
See Also
- Standard Library - Built-in modules reference
- Functions - Function programming guide
- Examples - Complete example programs