Composing Patterns
Build complex patterns from reusable parts using >> (sequence), | (choice), and * (repeat).
| Prerequisites | Pattern Cookbook, DSL Reference |
| Operators | >> sequence, ` |
Recipe 1: Sequence -- Setup then payoff
Chain two patterns with >>. The sharing clause forces ?char to bind the same node across both sub-patterns.
Result: 1 match -- Alice promised then fulfilled. Bob promised but never fulfilled, so promise_kept does not fire for him. Without sharing(char), the two stages would be independent and any promise followed by any fulfillment would match.
Recipe 2: Choice -- Alternative resolutions
| creates a mutual-exclusion group. When one alternative completes, the engine kills active partial matches for the others. Each alternative becomes a separate pattern with a shared group name.
Result: 2 matches -- crisis_famine for Westeros, crisis_war for Essos. The generated pattern names are crisis_war, crisis_famine, crisis_plague (group name + underscore + original name). All three share the exclusive group "crisis", so in incremental mode, once one fires for a given partial match, the others are killed.
Recipe 3: Exact repeat -- Three strikes
* N unrolls the pattern N times in sequence. Shared variables bind across all repetitions -- the same offender must commit all three offenses.
Result: 1 match for Alice (three offenses). Bob has only one, so the pattern does not complete for him. The composed pattern has 3 stages with anchors rep0_e, rep1_e, rep2_e -- each repetition's non-shared variables get a repN_ prefix to prevent collisions.
Recipe 4: Repeat range -- Brute force (5-10 attempts)
* N..M uses repeat_range internally. The pattern completes at min repetitions and continues matching up to max. Use N.. for unbounded.
Result: 1 match after 5 failures. The pattern will keep matching up to 10 total.
Variable bindings in range repeats:
| Binding | Meaning |
|---|---|
first_e | Anchor from the first iteration |
last_e | Anchor from the most recent iteration (updated on each new match) |
account | Shared variable -- same across all iterations |
repetition_count | Number of matched repetitions (on the PartialMatch) |
The first_ / last_ prefixes come from the two-copy layout: repeat_range generates a first_ copy and a last_ copy. The last_ stages loop, overwriting their bindings on each new repetition.
Recipe 5: Chaining compositions
A compose directive produces a named pattern. Use that name in subsequent compose directives to build deeper structures.
Result: full_arc matches Alice's three-beat arc (promise, fulfill, reward). The intermediate setup_payoff also matches independently since it is registered as its own pattern.
When chaining, the second compose treats setup_payoff as a two-stage pattern. The >> appends aftermath's stage, producing a three-stage full_arc. Variable renaming applies at each level: full_arc has anchors a_a_e1, a_b_e2, b_e3 -- prefixes nest. The sharing(char) at each level keeps ?char unprefixed throughout.
How variable renaming works
Every compose renames non-shared variables to prevent collisions between sub-patterns:
| Operator | Prefix scheme |
|---|---|
A >> B | a_ for A's variables, b_ for B's |
A * 3 | rep0_, rep1_, rep2_ per repetition |
A * N..M | first_ for the initial copy, last_ for the looping copy |
A | B | C | No renaming (each alternative is a separate pattern) |
Variables listed in sharing(...) are exempt from renaming. Use sharing when the same entity must appear across sub-patterns (same character, same account, same region). Omit it when sub-patterns are independent.
Next steps
- DSL Reference -- full
composesyntax, includingsharingand range operators. - Composition concepts -- why composition works, variable scoping rules, exclusive groups.
- Pattern Cookbook -- single-pattern recipes (negation, constraints, Allen relations).
- Incremental Integration -- use composed patterns in a live simulation loop.