An Example Program
Before diving into evaluation mechanics, let's look at a concrete Patchwork program. This grounds the concepts in something tangible.
The Program
fun code_review_assistant() {
var task = "Add OAuth support to the login system"
var files = $ls -1 src/auth/
var analysis = think {
The user wants to: ${task}
Relevant files in src/auth/:
${files}
Please identify:
1. Which files need modification
2. What new files should be created
3. Security considerations
}
for file in files {
if file.endsWith(".rs") {
var content = read("src/auth/${file}")
var review = think {
Review this file for OAuth readiness:
${content}
}
print("${file}: ${review}")
}
}
return analysis
}
What Happens at Runtime
When this function executes, several things interleave:
sequenceDiagram
participant E as Evaluator
participant R as Runtime
participant S as Shell
participant L as LLM
E->>R: Define 'task' = "Add OAuth..."
E->>S: Execute ls -1 src/auth/
S-->>E: ["auth.rs", "login.rs", ...]
E->>R: Define 'files' = [...]
E->>L: Think: analyze task
L-->>E: Analysis text
E->>R: Define 'analysis' = ...
loop For each file
E->>R: Check file.endsWith(".rs")
E->>R: read() file contents
E->>L: Think: review file
L-->>E: Review text
E->>R: print() review
end
E-->>E: Return analysis
Key Observations
Variables are simple - var task = "..." creates a binding in the current scope. The evaluator stores Value::String in the runtime's scope stack.
Shell commands return values - $ls -1 src/auth/ executes ls and captures stdout. With -1, the evaluator splits the output into an array of lines.
Think blocks block - When the evaluator hits think { ... }, it:
- Interpolates variables into the prompt text
- Sends a
ThinkRequestto the agent - Blocks waiting for
ThinkResponse - Returns the LLM's response as a string
Control flow is standard - for and if work like any language. The evaluator pushes a new scope for the loop body, binds the iteration variable, evaluates the body, then pops the scope.
Builtins are synchronous - read() and print() are builtin functions that the evaluator handles directly—no LLM involvement.
The Recursive Potential
This example only has one level of think blocks. But imagine the LLM's response triggered a tool call that ran more Patchwork code:
var result = think {
Analyze this task: ${task}
You can use:
- analyze_file(path) to deeply analyze a specific file
}
If analyze_file itself contains a think block, we get nested evaluation:
Evaluator → Agent → LLM → tool call → Evaluator → Agent → LLM → ...
This recursive interplay is what makes the architecture interesting, and it's why the agent needs careful channel management to avoid deadlock.
Next: The Evaluator
Now that you've seen what a program looks like at runtime, the next chapter explains how the evaluator actually walks the AST and executes each construct.