Core Examples

Core Examples

Note: These examples hoist a host extension with use<ext:app> => $app and call methods via dotted access ($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.

Extraction Operators

Demonstrates destructuring, slicing, and enumeration.

Destructuring Function Results

# Destructure dict results into named variables
[output: "test output", code: 0] -> destruct<output: $out, code: $code>

$code -> .gt(0) ? {
  "Tests failed:\n{$out}" -> log
}

"All tests passed" -> log

Processing Structured Data

# Process list of file-mode pairs
[
  ["src/auth.ts", "security"],
  ["src/api.ts", "performance"],
  ["src/db.ts", "security"]
] -> seq({
  $ -> destruct<$f, $mode>
  "Review {$f} for {$mode} issues" -> log
})

Slicing Results

# Get first 3 items
["a", "b", "c", "d", "e"] -> slice<:3>
# ["a", "b", "c"]
# Process in reverse order
["a", "b", "c"] -> slice<::-1>
# ["c", "b", "a"]

Dict Iteration

# Use .entries to iterate over dict key-value pairs
[host: "localhost", port: 8080] -> .entries -> seq({
  "{$[0]}={$[1]}"
}) -> .join("\n")

Collection Operations

Pipeline operators for fan, fold, seq, filter, and aggregate patterns.

fan: Parallel Transform

# Define closure first, then use it
|x| { $x * 2 } => $double
[1, 2, 3, 4, 5] -> fan($double)
# [2, 4, 6, 8, 10]
# Map with inline block
["alice", "bob", "carol"] -> fan({ "Hello, {$}!" })
# ["Hello, alice!", "Hello, bob!", "Hello, carol!"]

filter: Parallel Predicate

# 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 fan
|x| { $x * 2 } => $dbl
[1, 2, 3, 4, 5] -> filter({ .gt(2) }) -> fan($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], dict[name: "carol", age: 25]]

fold/chain: Reduction and Sequential Chaining

# Chain transformations
|s|"{$s} -> validated" => $validate
|s|"{$s} -> processed" => $process
|s|"{$s} -> complete" => $complete

"input" -> chain([$validate, $process, $complete])
# "input -> validated -> processed -> complete"

# Numeric reduction
|x|($x + 10) => $add10
|x|($x * 2) => $double

5 -> chain([$add10, $double, $add10])
# ((5 + 10) * 2) + 10 = 40

seq with break: Early Termination

# Break stops iteration and returns collected results up to that point
[1, 2, 3, 4, 5] -> seq({
  ($ == 3) ? break
  $ * 2
}) => $result
# Result: [2, 4]

seq catches the break signal and returns the partial results list. Elements processed before break are included; the element that triggered break is not.

# Use filter to find matching elements
[1, 2, 3, 4, 5] -> filter({ .gt(3) })
# Result: [4, 5]

Aggregate/Sum

# Sum numbers using fold
[10, 20, 30, 40] -> fold(0, { $@ + $ })
# 100

# Count matching elements using filter (parallel predicate)
$items -> filter({ .contains("error") }) -> .len => $count
"Found {$count} errors" -> log

Transform and Collect

# Process items, collect results using fan
["file1.txt", "file2.txt", "file3.txt"] -> fan({ "analyzed: {$}" }) -> .join("\n")
# "analyzed: file1.txt\nanalyzed: file2.txt\nanalyzed: file3.txt"

Args Type and Strict Invocation

Explicit argument unpacking with validation.

Named Args (Strict Invocation)

# Define a function
|a, b, c| { "{$a}-{$b}-{$c}" } => $fmt

# Create named args and invoke
ordered[a: 1, b: 2, c: 3] -> $fmt(...)    # "1-2-3"

# Store args for later use
ordered[a: 1, b: 2, c: 3] => $myArgs
$myArgs -> $fmt(...)       # "1-2-3"

Named Args

# Named args spread positionally into parameters
|width, height|($width * $height) => $area

ordered[width: 10, height: 20] -> $area(...)  # 200

Parameter Defaults

# Defaults fill missing trailing arguments
|x, y = 10, z = 20|($x + $y + $z) => $fn

ordered[x: 5] -> $fn(...)              # 35 (5 + 10 + 20)
ordered[x: 5, y: 10, z: 30] -> $fn(...)  # 45 (5 + 10 + 30)

Type Checking with .^type

# Use .^type to inspect values
42 => $x
$x.^type == number      # true
"hello" => $s
$s.^type == string      # true
[1, 2] => $l
$l.^type == list        # true
ordered[a: 1, b: 2] => $t
$t.^type == ordered     # true
[a: 1] => $d
$d.^type == dict        # true

# Parameterized type comparison
[1, 2, 3] => $nums
$nums.^type == list(number)    # true — exact structural match
["a", "b"] => $strs
$strs.^type == list(string)    # true
$strs.^type == list(number)    # false

# 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 10

Workflow Examples (require host functions)

Feature Implementation Workflow

Validates requirements, creates spec, iterates on review, then implements.

timeout: 00:10:00
args: requirements: string

use<ext:app> => $app

"""
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}"
}

"""
Create a technical specification from {$requirements}.
Include API design, data models, and component structure.
""" -> $app.prompt() => $spec

"Specification created" -> log

$spec -> while (.contains("REVISION")) do {
  """
Review this specification for issues:
{$}

Output APPROVED if ready, or REVISION REQUIRED with feedback.
  """ -> $app.prompt() => $review

  $review -> ?(.contains("APPROVED")) { break }

  """
Update the specification based on this feedback:
{$review}

Original spec:
{$}
  """ -> $app.prompt()
} => $approved_spec

"""
Implement the approved specification:
{$approved_spec}

Create the necessary files and tests.
""" -> $app.prompt() => $implementation

$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

use<ext:app> => $app

# Initial check
$app.prompt("Read {$plan} and find the first unchecked item (- [ ])") => $status

# Work loop
$status -> while (!.contains("ALL COMPLETE")) do {
  """
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}" -> log

Test-Fix Loop

Runs tests, fixes failures, repeats until passing.

args: target: string

use<ext:app> => $app

# Run tests
$app.prompt("Run tests for {$target} and report results") => $result

# Fix loop
$result -> while (.contains("FAIL")) do {
  "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
---

use<ext:app> => $app

$app.prompt("Read and summarize {$file}") => $summary

"""
Evaluate for SECURITY issues:
{$summary}

Output PASS, WARN, or FAIL with explanation.
""" -> $app.prompt() => $security

"""
Evaluate for PERFORMANCE issues:
{$summary}

Output PASS, WARN, or FAIL with explanation.
""" -> $app.prompt() => $performance

$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

use<ext:app> => $app

# 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
---

use<ext:app> => $app

# Do-while: body runs first, then condition checked
do<limit: 5> {
  """
Perform: {$operation}

Output SUCCESS, RETRY, or FAILED.
  """ -> $app.prompt()
} while (.contains("RETRY")) => $result

# Loop exits when result doesn't contain RETRY
$result -> .contains("SUCCESS") ? [code: 0, msg: "Succeeded"] ! dict[code: 1, msg: "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
---

use<ext:app> => $app

# 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

use<ext:app> => $app

# 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"]
# Parameterized type annotation on closure parameter
|items: list(string)| {
  $items -> seq({ $ -> .upper })
} => $upper_all:closure

# Runtime validates element types
$upper_all(list["hello", "world"])
# Result: list["HELLO", "WORLD"]

Pattern Extraction

Extracts specific information from responses.

---
args: logfile: string
---

use<ext:app> => $app

$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

use<ext:app> => $app

# 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] -> number => $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) -> seq({
  $ => $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:::"

use<ext:app> => $app

"""
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 -> while (!.contains(":::DONE:::")) do {
  """
Continue working on: {$task}

Previous progress:
{$}

Remember the signal rules.
  """ -> $app.prompt()
} => $final

"Task complete: {$final}" -> log

Vector Database

Vector database operations for semantic search and RAG workflows. These examples hoist a qdrant extension with use<ext:qdrant> => $qdrant and call $qdrant.method(...). The same script runs against pinecone or chroma by changing the hoisted extension to use<ext:pinecone> or use<ext:chroma> — method signatures match across providers.

RAG Pipeline

Embed query, search similar vectors, format context for LLM.

args: question: string

use<ext:openai> => $openai
use<ext:qdrant> => $qdrant
use<ext:anthropic> => $anthropic

# 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 -> fan({ $.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.prompt

Batch Upsert with Error Handling

Store multiple documents with partial failure recovery.

args: documents: list

use<ext:openai> => $openai
use<ext:qdrant> => $qdrant

# Embed all documents
$documents -> fan({
  [
    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.

use<ext:openai> => $openai
use<ext:qdrant> => $qdrant

# 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 -> fan({
  [
    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 -> seq({ $ -> 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
---

use<ext:openai> => $openai
use<ext:qdrant> => $qdrant
use<ext:anthropic> => $anthropic

# Define search tool with closure annotation
^("Search the knowledge base for relevant information")
|^("Search query text") query: string| {
  $query -> $openai.embed -> $qdrant.search($, [k: 5]) -> fan({
    "ID: {$.id}\nScore: {$.score}\nContent: {$.metadata.text}"
  }) -> .join("\n\n---\n\n")
} => $search_knowledge_base

# Define store tool with closure annotation
^("Store a new document in the knowledge base")
|^("Document ID") id: string, ^("Document text") text: string, ^("Document title") title: string| {
  [
    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_document

# Run tool loop with dict-form 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_knowledge_base: $search_knowledge_base, store_document: $store_document],
    max_turns: 10,
    user_message: $user_query
  ]
) => $loop_result

$loop_result.content

See Also

DocumentDescription
Stream and Time-Domain ExamplesStream consumption, error recovery, and time-domain operators
GuideGetting started tutorial
CookbookReusable design patterns (state machines, dispatch, accumulators)
TroubleshootingCommon mistakes and fixes
ReferenceLanguage specification