Core Examples
Note: These examples use
app::prefix for host-provided functions (app::prompt(),app::fetch(), etc.). Built-in functions (log,range,json) need no prefix. Frontmatter is opaque to rill; the host parses it and provides named variables to the script context.
Pure Language Examples
Extraction Operators
Demonstrates destructuring, slicing, and enumeration.
Destructuring Function Results
# Destructure list results into named variables
["test output", 0] -> *<$out, $code>
$code -> .gt(0) ? {
"Tests failed:\n{$out}" -> log
}
"All tests passed" -> logProcessing Structured Data
# Process list of [file, mode] pairs
[
["src/auth.ts", "security"],
["src/api.ts", "performance"],
["src/db.ts", "security"]
] -> each {
$ -> *<$f, $mode>
"Review {$f} for {$mode} issues" -> log
}Slicing Results
# Get first 3 items
["a", "b", "c", "d", "e"] -> /<:3>
# ["a", "b", "c"]# Process in reverse order
["a", "b", "c"] -> /<::-1>
# ["c", "b", "a"]Dict Iteration
# Use .entries to iterate over dict key-value pairs
[host: "localhost", port: 8080] -> .entries -> each {
"{$[0]}={$[1]}"
} -> .join("\n")Collection Operations
Pipeline operators for map, reduce, find, and aggregate patterns.
Map with Parallel Spread
# Define closure first, then use it
|x| { $x * 2 } => $double
[1, 2, 3, 4, 5] -> map $double
# [2, 4, 6, 8, 10]
# Map with inline block
["alice", "bob", "carol"] -> map { "Hello, {$}!" }
# ["Hello, alice!", "Hello, bob!", "Hello, carol!"]Filter with Parallel Filter
# Keep elements matching condition (block form)
[1, 2, 3, 4, 5] -> filter { .gt(2) }
# [3, 4, 5]
# Filter with closure predicate
|x| { $x % 2 == 0 } => $even
[1, 2, 3, 4, 5, 6] -> filter $even
# [2, 4, 6]
# Filter non-empty strings
["hello", "", "world", ""] -> filter { !.empty }
# ["hello", "world"]
# Chain filter and map
|x| { $x * 2 } => $dbl
[1, 2, 3, 4, 5] -> filter { .gt(2) } -> map $dbl
# [6, 8, 10]
# Filter structured data
[
[name: "alice", age: 30],
[name: "bob", age: 17],
[name: "carol", age: 25]
] -> filter { $.age -> .ge(18) }
# [[name: "alice", age: 30], [name: "carol", age: 25]]Reduce with Sequential Spread
# Chain transformations
|s|"{$s} -> validated" => $validate
|s|"{$s} -> processed" => $process
|s|"{$s} -> complete" => $complete
"input" -> @[$validate, $process, $complete]
# "input -> validated -> processed -> complete"
# Numeric reduction
|x|($x + 10) => $add10
|x|($x * 2) => $double
5 -> @[$add10, $double, $add10]
# ((5 + 10) * 2) + 10 = 40Find First Match
# Find first element matching condition
[1, 2, 3, 4, 5] -> each {
.gt(3) ? { $ -> break }
} => $found
# 4
# Find with default
[1, 2, 3] -> each {
.gt(10) ? { $ -> break }
} => $result
$result -> .empty ? { "not found" } ! { "found: {$result}" }Aggregate/Sum
# Sum numbers using fold
[10, 20, 30, 40] -> fold(0) { $@ + $ }
# 100
# Count matching elements using filter
$items -> filter { .contains("error") } -> .len => $count
"Found {$count} errors" -> logTransform and Collect
# Process items, collect results using map
["file1.txt", "file2.txt", "file3.txt"] -> map { "analyzed: {$}" } -> .join("\n")
# "analyzed: file1.txt\nanalyzed: file2.txt\nanalyzed: file3.txt"Args Type and Strict Invocation
Explicit argument unpacking with validation.
Positional Args
# Define a function
|a, b, c| { "{$a}-{$b}-{$c}" } => $fmt
# Create args from tuple and invoke
*[1, 2, 3] -> $fmt() # "1-2-3"
# Store args for later use
*[1, 2, 3] => $myArgs
$myArgs -> $fmt() # "1-2-3"Named Args
# Named args match by parameter name, order doesn't matter
|width, height|($width * $height) => $area
*[height: 20, width: 10] -> $area() # 200Parameter Defaults
# Defaults provide opt-in leniency
|x, y = 10, z = 20|($x + $y + $z) => $fn
*[5] -> $fn() # 35 (5 + 10 + 20)
*[x: 5, z: 30] -> $fn() # 45 (5 + 10 + 30)Type Checking with Global Functions
# Use type() to inspect values
42 -> type # "number"
"hello" -> type # "string"
[1, 2] -> type # "list"
*[1, 2] -> type # "tuple"
[a: 1] -> type # "dict"
# Use json() to serialize
[name: "test", count: 42] -> json
# '{"name":"test","count":42}'
# Use log() to debug while continuing pipe
"processing" -> log -> .len # logs "processing", returns 10Workflow Examples (require host functions)
Feature Implementation Workflow
Validates requirements, creates spec, iterates on review, then implements.
timeout: 00:10:00
args: requirements: string
# Phase 1: Validate requirements
"""
Review the requirements document at {$requirements}.
Check for completeness and clarity.
Output READY if complete, or list missing elements.
""" -> app::prompt() => $validation
$validation -> .contains("READY") -> !$ ? {
error "Requirements incomplete: {$validation}"
}
# Phase 2: Create specification
"""
Create a technical specification from {$requirements}.
Include API design, data models, and component structure.
""" -> app::prompt() => $spec
"Specification created" -> log
# Phase 3: Review loop - iterate until approved
($spec -> .contains("REVISION")) @ {
"""
Review this specification for issues:
{$}
Output APPROVED if ready, or REVISION REQUIRED with feedback.
""" -> app::prompt() => $review
$review -> ?(.contains("APPROVED")) { break }
# Apply feedback and continue
"""
Update the specification based on this feedback:
{$review}
Original spec:
{$}
""" -> app::prompt()} => $approved_spec
# Phase 4: Implementation
"""
Implement the approved specification:
{$approved_spec}
Create the necessary files and tests.
""" -> app::prompt() => $implementation
# Phase 5: Verify
app::prompt("Run tests and verify implementation") => $verification
$verification -> ?(.contains("PASS")) {
[0, "Workflow complete"]
} ! {
[1, "Verification failed: {$verification}"]
}Document-Driven Task Loop
Works through a checklist until complete.
args: plan: string
# Initial check
app::prompt("Read {$plan} and find the first unchecked item (- [ ])") => $status
# Work loop
$status -> (!.contains("ALL COMPLETE")) @ {
"""
Based on this status:
{$}
1. Implement the identified unchecked item
2. Mark it complete in {$plan}
3. Check if any unchecked items remain
4. Output ALL COMPLETE if done, or describe next item
""" -> app::prompt()
} => $final
"Plan complete: {$final}" -> logTest-Fix Loop
Runs tests, fixes failures, repeats until passing.
args: target: string
# Run tests
app::prompt("Run tests for {$target} and report results") => $result
# Fix loop
$result -> @(.contains("FAIL")) {
"Fixing failures..." -> log
"""
Fix these test failures:
{$}
Make minimal changes. Then run tests again and report results.
""" -> app::prompt()} => $final
$final -> ?(.contains("PASS")) {
"All tests passing"
} ! {
error "Could not fix all tests"
}Code Review
Reviews code against multiple criteria.
---
args: file: string
---
# Get file summary
app::prompt("Read and summarize {$file}") => $summary
# Security check
"""
Evaluate for SECURITY issues:
{$summary}
Output PASS, WARN, or FAIL with explanation.
""" -> app::prompt() => $security
# Performance check
"""
Evaluate for PERFORMANCE issues:
{$summary}
Output PASS, WARN, or FAIL with explanation.
""" -> app::prompt() => $performance
# Check results
$security -> .contains("FAIL") -> ? {
error "Security review failed: {$security}"
}
$performance -> .contains("FAIL") -> ? {
error "Performance review failed: {$performance}"
}
"Code review passed"Environment-Aware Deployment
Deploys based on environment configuration.
args: service: string
# Validate environment
$ENV.DEPLOY_ENV -> ?(.empty()) {
error "DEPLOY_ENV not set"
}
# Environment-specific deployment
($ENV.DEPLOY_ENV == "production") ? {
"""
Deploy {$service} to production.
- Run full test suite first
- Enable monitoring
- Use blue-green deployment
""" -> app::prompt()} ! ($ENV.DEPLOY_ENV == "staging") ? {
"""
Deploy {$service} to staging.
- Run smoke tests
- Enable debug logging
""" -> app::prompt()} ! {
app::prompt("Deploy {$service} to development environment")
} => $result
"Deployment complete" -> log
[0, "Deployed {$service} to {$ENV.DEPLOY_ENV}"]Retry Pattern
Retries an operation until success or max attempts. Use do-while since you always want at least one attempt:
---
args: operation: string
---
# Do-while: body runs first, then condition checked
^(limit: 5) @ {
"""
Perform: {$operation}
Output SUCCESS, RETRY, or FAILED.
""" -> app::prompt()
} ? (.contains("RETRY")) => $result
# Loop exits when result doesn't contain RETRY
$result -> .contains("SUCCESS") ? [0, "Succeeded"] ! [1, "Failed: {$result}"]The do-while form eliminates the separate first-attempt code since the body always executes at least once.
Inline Capture Pattern
Captures mid-chain for debugging or later reference while data continues flowing.
---
args: file: string
---
# Inline capture: value flows through $raw to log to conditional
app::prompt("Read {$file}") => $raw -> log -> .contains("ERROR") -> ? {
error "Failed to read: {$raw}"
}
# Continue with $raw available for later use
app::prompt("Analyze this content:\n{$raw}") => $analysis -> log -> .empty -> ? {
error "Analysis produced no output"
}
# Both $raw and $analysis available
"""
Compare the original:
{$raw}
With the analysis:
{$analysis}
""" -> app::prompt()Semantically, => $var -> is => $var.set($) -> — the capture acts like log, storing the value while passing it through unchanged.
Type-Safe Variables
Uses type annotations to prevent accidental type changes during script execution.
args: file: string
# Define a typed helper closure
|input: string| {
app::prompt("Validate: {$input}") -> ?(.contains("VALID")) { true } ! { false }
} => $validate:closure
# Capture with explicit type locks the variable
"processing" => $status:string
"checking {$file}" => $status # OK: same type
# 42 => $status # ERROR: cannot assign number to string
# Closures are type-locked too
# "oops" => $validate # ERROR: cannot assign string to closure
# Inline type annotation in pipe chain
app::prompt("Check {$file}") => $result:string -> log -> ?(.contains("ERROR")) {
error $result
}
# Type annotations catch mistakes early
app::prompt("Analyze {$file}") => $analysis:string
?(.contains("FAIL")) {
error "Analysis failed: {$analysis}"
}
[0, "Processing complete"]Pattern Extraction
Extracts specific information from responses.
---
args: logfile: string
---
app::prompt("Read {$logfile} and find all ERROR lines") => $errors
$errors -> .empty -> ? {
"No errors found"
} ! {
"""
Analyze these errors and categorize them:
{$errors}
For each unique error type, suggest a fix.
""" -> app::prompt() => $analysis
"Error analysis complete" -> log
$analysis
}Multi-Phase Pipeline with Bailout
Each phase can halt the pipeline on failure.
---
args: file: string
---
# Multi-phase pipeline with early exit on errors
"content of {$file}" => $content
$content -> .contains("ERROR") ? {
"Read failed" -> return
}
"analyzed: {$content}" => $analysis
$analysis -> .contains("FAIL") ? {
"Analysis failed" -> return
}
"Pipeline complete: {$analysis}"Arithmetic in Loops
Uses bar-delimited arithmetic for calculations within workflow logic.
args: items: string
# Count items and calculate batch sizes
app::prompt("Count items in {$items}") -> .match("(\\d+) items") => $m
$m -> .empty -> ? {
error "Could not parse item count"
}
$m.groups[0] -> .num => $count
# Calculate batches: ceil(count / 10)
(($count + 9) / 10) => $batches
"Processing {$count} items in {$batches} batches" -> log
# Process each batch using range
range(1, $batches + 1) -> each {
$ => $batch_num
(($batch_num - 1) * 10) => $start
($start + 10) => $end
"""
Process batch {$batch_num} of {$batches}
Items {$start} through {$end}
""" -> app::prompt()}
[0, "Processed all batches"]Signal-Based Workflow
Uses explicit signals for workflow control.
args: task: string
exceptions:
- ":::BLOCKED:::"
- ":::NEEDS_HUMAN:::"
"""
Work on this task: {$task}
Rules:
- Output :::BLOCKED::: if you need information you don't have
- Output :::NEEDS_HUMAN::: if human judgment is required
- Output :::DONE::: when complete
""" -> app::prompt() => $result
$result -> (!.contains(":::DONE:::")) @ {
"""
Continue working on: {$task}
Previous progress:
{$}
Remember the signal rules.
""" -> app::prompt()
} => $final
"Task complete: {$final}" -> logVector Database
Vector database operations for semantic search and RAG workflows. These examples use qdrant:: prefix, but all functions work identically across qdrant::, pinecone::, and chroma:: namespaces — change the prefix to switch providers.
RAG Pipeline
Embed query, search similar vectors, format context for LLM.
args: question: string
# Generate embedding for the query
$question -> openai::embed => $query_vector
# Search for similar documents
$query_vector -> qdrant::search($, [k: 3, score_threshold: 0.7]) => $results
# Extract metadata for context
$results -> map { $.metadata.text } -> .join("\n\n---\n\n") => $context
# Generate answer with retrieved context
"""
Answer this question using the provided context:
Question: {$question}
Context:
{$context}
""" -> anthropic::promptBatch Upsert with Error Handling
Store multiple documents with partial failure recovery.
args: documents: list
# Embed all documents
$documents -> map {
[
id: $.id,
vector: $.text -> openai::embed,
metadata: [title: $.title, source: $.source]
]
} => $items
# Batch insert with error handling
$items -> qdrant::upsert_batch => $result
# Check for partial failure
$result.failed -> .empty -> !$ ? {
# Partial failure occurred
"Batch failed at {$result.failed}: {$result.error}" -> log
"Successfully stored {$result.succeeded} vectors before failure" -> log
error "Batch upsert incomplete"
} ! {
# Full success
"Successfully stored {$result.succeeded} vectors" -> log
}Collection Lifecycle Management
Create, populate, and manage vector collections.
# Create a new collection
qdrant::create_collection("knowledge_base", [
dimensions: 1536,
distance: "cosine"
]) => $create_result
"Created collection: {$create_result.name}" -> log
# Store vectors (assumes $docs defined)
$docs -> map {
[
id: $.id,
vector: $.text -> openai::embed,
metadata: [title: $.title]
]
} -> qdrant::upsert_batch => $upsert_result
# Verify collection state
qdrant::describe() => $info
"Collection has {$info.count} vectors with {$info.dimensions} dimensions" -> log
# List all collections
qdrant::list_collections() => $collections
$collections -> each { $ -> log }
# Clean up when done
# qdrant::delete_collection("knowledge_base")Tool Loop Integration
Vector search as an LLM tool within anthropic::tool_loop.
args: user_query: string
# Define search tool
tool(
"search_knowledge_base",
"Search the knowledge base for relevant information",
[query: "string"],
{
# Embed the query and search
.query -> openai::embed -> qdrant::search($, [k: 5]) -> map {
"ID: {$.id}\nScore: {$.score}\nContent: {$.metadata.text}"
} -> .join("\n\n---\n\n")
}
) => $search_tool
# Define store tool
tool(
"store_document",
"Store a new document in the knowledge base",
[id: "string", text: "string", title: "string"],
{
# Create dict, embed text, upsert
[
id: .id,
vector: .text -> openai::embed,
metadata: [title: .title, text: .text]
] => $item
$item.vector -> qdrant::upsert($item.id, $, [title: $item.metadata.title])
"Stored document {$item.id}"
}
) => $store_tool
# Run tool loop with both tools
anthropic::tool_loop(
"Answer the user's question. Use search_knowledge_base to find relevant information. If the user provides new information to remember, use store_document.",
[
tools: [$search_tool, $store_tool],
max_turns: 10,
user_message: $user_query
]
) => $response
$response.content