← Docs index · API : Module · API : Events · API : Patterns
The Switch instance is what EasySwitch.new(...) and EasySwitch("name", ...) return. All builder methods return self for fluent chaining ; :execute(value) is the only terminal call.
:execute(value) runs this pipeline in order :
- emit
beforeExecute(skipped when no listener bound) - memoize cache lookup (only if
:memoize()was called) — cache HIT short-circuits the rest, firesafterExecuteand returns :before(...)gate — if it returnsfalse, firesbeforeCheckFailedand returnsnil- middleware chain — transforms the value
- dispatcher — Map literal lookup → complex pattern walk → default
- memoize cache store (only if
:memoize()and result ≠ nil) - emit
noMatch(only when nothing handled the value) - emit
afterExecute(skipped when no listener bound)
Subscribe a listener to one of the seven events. Throws on unknown event names — typos are caught at registration time.
| Argument | Type | Description |
|---|---|---|
event |
string |
One of the seven event names (see Events). |
callback |
function |
Receives the event-specific payload. |
| Returns | Type | Description |
|---|---|---|
self |
Switch |
For chaining. |
sw:on("noMatch", function(v) print("[warn] unmatched:", v) end)
:on("error", function(stage, err) log_error(stage, err) end)See Events API for the complete list with payloads.
Add a dispatch rule. Three call shapes are accepted depending on the second argument :
The simplest form. cases is matched against the input ; if it matches, action(input) runs and its return value is the dispatch result.
| Argument | Type | Description |
|---|---|---|
cases |
any | Literal, array of literals, table pattern, P descriptor, or DSL string. |
action |
function |
Called with the matched input value. |
Adds a runtime predicate that must return truthy for the action to fire. Useful when the pattern needs to depend on the value's content beyond what P.* can express directly.
| Argument | Type | Description |
|---|---|---|
cases |
any | Same as shape 1. |
guard |
function |
Called with the input. The action only fires when this returns truthy. |
action |
function |
The action to run when both pattern and guard match. |
sw:when(P.number, function(n) return n > 0 end, function() return "positive" end)When the second argument is a table, it's interpreted as a matchigo scope table — cases is force-parsed as DSL, and the scope's PascalCase entries resolve refs inside it.
| Argument | Type | Description |
|---|---|---|
cases |
string |
DSL source. |
scope |
table |
PascalCase refs (e.g. { Num = P.number, Str = P.string }). |
action |
function |
Called with the matched value. |
sw:when("{| kind: 'click', x: Num, y: Num |}", { Num = P.number },
function(c) return ("at %d,%d"):format(c.x, c.y) end)| Type | Behaviour |
|---|---|
string starting with {, [, (, ', " |
Auto-parsed as DSL pattern |
| any other string | Literal value (Map fast path) |
| number, boolean | Literal value (Map fast path) |
array {"a", "b"} (#cases > 0) |
Each element registered as a Map literal (one action shared) |
dict-like table { kind = "x", n = P.number } |
Partial shape pattern (extra keys allowed) |
matchigo P descriptor (P.string, P.union(...)) |
Pattern-typed rule (walked on dispatch) |
Important
Guards are not supported with literal arrays. sw:when({"a", "b"}, guard, action) throws. Workaround : write two rules.
| Returns | Type | Description |
|---|---|---|
self |
Switch |
For chaining. |
Note
Calling :when() invalidates the memoize cache (when enabled). Stale entries that would conflict with the new rule are wiped.
Set the fallback action that fires when no rule matched.
| Argument | Type | Description |
|---|---|---|
action |
function |
Receives the input value. |
| Returns | Type | Description |
|---|---|---|
self |
Switch |
For chaining. |
sw:default(function(v) return "unhandled: " .. tostring(v) end)Note
Calling :default() invalidates the memoize cache.
Append a middleware function. Middlewares run in declaration order between the gate check and the dispatcher.
| Argument | Type | Description |
|---|---|---|
middleware |
function(value): any |
Receives the current value, returns the transformed one (or nil to keep the prior value). |
| Returns | Type | Description |
|---|---|---|
self |
Switch |
For chaining. |
sw:use(function(v) return string.lower(v) end) -- normalise input
sw:use(function(v) return string.gsub(v, "%s+", "") end) -- strip whitespaceNote
Errors inside a middleware are isolated : the prior value is preserved, the error event fires (when listened), and the chain continues. Calling :use() invalidates the memoize cache.
See Middleware guide.
Set the gate function. Returning false from it short-circuits dispatch — beforeCheckFailed fires (when listened) and :execute() returns nil. Pass nil to clear an existing gate.
| Argument | Type | Description |
|---|---|---|
checkFunction |
function(value): boolean |
Or nil to clear. |
| Returns | Type | Description |
|---|---|---|
self |
Switch |
For chaining. |
sw:before(function(v)
return type(v) == "string" -- only string inputs proceed
end)Note
Calling :before() invalidates the memoize cache.
Run the dispatch pipeline against value and return the matched action's result.
| Argument | Type | Description |
|---|---|---|
value |
any | The input to dispatch on. |
| Returns | Type | Description |
|---|---|---|
result |
any | The matching action's return value, the default's return value, or nil if neither matched. |
- If a matching action returns
EasySwitch.FALLTHROUGH, the dispatcher continues to the next rule (see Fallthrough). - If
safe = truewas passed toEasySwitch.new, action errors are caught —errorfires and:execute()returnsnil. Otherwise errors propagate. - The
noMatchevent fires only when literally no rule and no default produced a value.
Enable result caching by input value. Idempotent — calling it more than once just updates the verify flag.
| Argument | Type | Description |
|---|---|---|
opts |
table? |
Optional config. See below. |
| Returns | Type | Description |
|---|---|---|
self |
Switch |
For chaining. |
| Field | Type | Default | Description |
|---|---|---|---|
verify |
boolean |
false |
When true, every cache HIT re-runs the full pipeline and errors if the result differs from the cached one. Dev-only safety net for non-deterministic actions. |
- Cache uses weak references (
__mode = "kv") — table values get GC-reclaimed when otherwise unreferenced. nilandNaNkeys are never cached (Lua disallows them as table keys).nilresults are not cached, so adding a rule afterwards isn't shadowed by a stale miss.- Mutations (
:when,:default,:use,:before) automatically invalidate the cache.
local sw = EasySwitch.new():memoize()
:when("expensive", function() return slow_compute() end)
sw:execute("expensive") -- runs slow_compute()
sw:execute("expensive") -- returns cached value, action does NOT re-runSee Memoize guide.
Empty the memoize cache without disabling memoize. No-op when memoize is disabled.
| Returns | Type | Description |
|---|---|---|
self |
Switch |
For chaining. |
sw:memoize()
sw:execute("x") -- caches "x"
sw:clearCache() -- cache empty
sw:execute("x") -- runs again, caches againRemove all listeners for a given event, or for every event when no name is passed.
| Argument | Type | Description |
|---|---|---|
event |
string? |
Event name. When nil, clears every event. |
| Returns | Type | Description |
|---|---|---|
self |
Switch |
For chaining. |
sw:clearEvents("noMatch") -- only noMatch listeners
sw:clearEvents() -- all listeners across all eventsThe sentinel value, also reachable from EasySwitch.FALLTHROUGH. Returning it from an action keeps dispatch going. See Fallthrough guide.
- Module API —
EasySwitch.new, registry helpers, re-exports - Events API — payloads for every event
- Pattern matching guide — how patterns get evaluated