Patternia API Reference¶
Overview¶
Patternia exposes one public matching shape:
match(subject) | on(
case_1,
case_2,
_ >> fallback
)
The pipeline is immediate. There is no deferred builder stage.
match(subject)¶
match(subject) creates the evaluation context for one subject value.
int x = 7;
auto r = match(x) | on(
lit(7) >> 1,
_ >> 0
);
Rules:
subjectmust be an lvalue.- Cases are evaluated in source order.
- Matching uses first-match-wins semantics.
on(case1, case2, ...)¶
on(...) groups the case list consumed by match(subject).
match(x) | on(
lit(1) >> "one",
lit(2) >> "two",
_ >> "other"
);
Rules:
- The last case must be a wildcard fallback.
- Each entry must be a
pattern >> handlercase expression. - Unreachable wildcard/alt ordering errors are diagnosed at compile time.
pattern >> handler¶
This is the core case-expression form.
lit(1) >> 42
$(is<int>) >> [](int v) { return v * 2; }
$(has<&Point::x, &Point::y>) >> [](int x, int y) { return x + y; }
Handler forms:
pattern >> valuepattern >> callable
Bindings are determined entirely by the pattern.
Wildcard _¶
_ is the public wildcard and fallback pattern.
match(x) | on(
lit(1) >> "one",
_ >> "other"
);
The wildcard does not bind values by itself.
Literal Patterns¶
lit(value)¶
Runtime literal match.
match(x) | on(
lit(5) >> 42,
_ >> -1
);
val<value>¶
Compile-time literal match. Useful when the literal is known at compile time and the lowering engine can consider static dispatch.
match(x) | on(
val<1> >> 1,
val<2> >> 2,
_ >> 0
);
lit_ci(value)¶
Runtime ASCII case-insensitive string match.
match(s) | on(
lit_ci("hello") >> 1,
_ >> 0
);
Binding Patterns¶
$ and $(...)¶
Patternia keeps binding explicit.
$ // bind the whole subject
$(subpattern) // bind under a subpattern
Examples:
match(x) | on(
$ >> [](int v) { return v; },
_ >> 0
);
match(v) | on(
$(is<std::string>) >> [](const std::string &s) {
return s.size();
},
_ >> 0
);
Binding rules:
- No pattern binds implicitly.
- Handler parameters follow binding order.
- Patterns that do not bind produce zero-argument handlers.
Guard Attachment []¶
Attach a guard to a binding pattern with pattern[guard].
match(x) | on(
$[_ > 0 && _ < 10] >> "small",
_ >> "other"
);
Guard evaluation order:
- Match the pattern.
- Bind values.
- Evaluate the guard.
- Invoke the handler if the guard passes.
A guard failure only rejects the current case.
Guard Helpers¶
_¶
Placeholder alias for a single bound value.
$[_ > 5]
Use _ when one binding is enough and the predicate reads clearly without an
explicit name.
Structured-binding member names¶
A has<...> pattern is tuple-like. Prefer a C++17 structured binding to name
its member placeholders without a macro:
constexpr auto pt = has<&Point::x, &Point::y>;
auto [x, y] = pt;
$(pt)[x * x + y * y == 25]
There is one name per non-ignored member, in the order listed in has<...>;
_ign slots do not get names. The names are placeholders, not values extracted
from a subject. $(pt) still controls which values reach the handler.
Inside a guard, names resolve to the position of their member in the has<>
member list at compile time, so they follow members, not positions — the order
of member pointers in a subsequent pattern does not matter:
$(has<&Point::y, &Point::x>)[x == 3 && y == 4] // x is still .x
A name whose member is not listed in has<> fails a static_assert. Member
names are only valid in guards attached to has<...>; using them on a
non-structural pattern is a compile-time error.
Scope and cached matchers¶
Function-local auto [x, y] = pt; works with on(...), but not with
PTN_ON(...): the macro uses a captureless lambda, which cannot access these
automatic bindings. For cached matchers, choose one of these options:
- Declare the pattern and its structured-binding names at namespace scope.
-
In C++20 or later, give both the pattern and the bindings static storage inside the function:
static constexpr auto pt = has<&Point::x, &Point::y>; static auto [x, y] = pt; -
In C++17, keep
PTN_BINDfor function-local names used insidePTN_ON, or useon(...)instead. Any local pattern passed toPTN_ONmust also be static; alternatively, writehas<...>directly inside the macro.
PTN_BIND(Type, names...) (deprecated)¶
PTN_BIND remains supported for compatibility, but prefer structured-binding
member names where the scope rules above permit them. It declares
static constexpr member_t<&Type::name> placeholders for one to ten
non-static data members:
PTN_BIND(Point, x, y);
$(has<&Point::x, &Point::y>)[x * x + y * y == 25]
Declarations are valid at namespace or block scope. Their static storage
duration allows block-scope names inside PTN_ON even in C++17. A misspelled
member name fails at the PTN_BIND line.
rng(lo, hi, mode)¶
Range helper for single-bound-value guards.
$[rng(0, 10)]
$[rng(0, 10, pat::mod::open)]
Use callables for domain logic that does not read naturally as _ or a short
named-placeholder expression.
Migration from positional guard APIs¶
The guard surface now uses _ for one bound value and structured-binding
member names for structural guards. The former __, _0, arg<N>,
PTN_LET, and PTN_WHERE spellings have been removed rather than retained as aliases.
// Single value
$[_ > 0]
// Multiple structural members
constexpr auto pt = has<&Point::x, &Point::y>;
auto [x, y] = pt;
$(pt)[x < y]
Structural Matching has<&T::member...>¶
has<> describes structure.
Wrap it with $(...) to extract values.
struct Point {
int x;
int y;
};
match(p) | on(
$(has<&Point::x, &Point::y>) >> [](int x, int y) {
return x + y;
},
_ >> 0
);
Properties:
- Member order is explicit and stable.
- Unlisted members are ignored.
- Validation happens at compile time.
Variant Matching is<T> and alt<I>¶
is<T>¶
Type-based std::variant match.
match(v) | on(
is<int> >> "int",
$(is<std::string>) >> [](const std::string &s) {
return "str:" + s;
},
_ >> [] { return std::string("other"); }
);
alt<I>¶
Index-based std::variant match.
match(v) | on(
alt<0> >> "first",
alt<1> >> "second",
_ >> "other"
);
Rules:
is<T>requiresTto appear exactly once.alt<I>requiresIto be in range.- Use
$(...)when you want the alternative value bound into the handler.
Pattern Combinators¶
any(ps...)¶
Matches when at least one sub-pattern matches (OR semantics). Short-circuits on the first matching sub-pattern.
match(x) | on(
any(val<1>, val<2>, val<3>) >> "1, 2, or 3",
_ >> "other"
);
Properties:
- Non-binding: handlers receive zero arguments.
- Sub-patterns are evaluated left-to-right; evaluation stops at the first match.
- Requires at least one sub-pattern; every argument must be a pattern object.
- Operator sugar:
(a || b)is equivalent toany(a, b). Note that>>binds tighter than||, so parenthesize:(lit(1) || lit(2)) >> handler.
all(ps...)¶
Matches only when every sub-pattern matches (AND semantics). Short-circuits on the first mismatch.
match(x) | on(
all(any(val<1>, val<2>), val<2>) >> "2",
_ >> "other"
);
Properties:
- Non-binding: handlers receive zero arguments.
- Sub-patterns are evaluated left-to-right; evaluation stops at the first mismatch.
- Requires at least one sub-pattern; every argument must be a pattern object.
- Operator sugar:
(a && b)is equivalent toall(a, b). Note that>>binds tighter than&&, so parenthesize:(p && q) >> handler.
neg(p)¶
Negates the match result of a sub-pattern. neg(p) matches when p does
not match, and vice versa.
match(x) | on(
neg(val<0>) >> "non-zero",
_ >> "zero"
);
Properties:
- Non-binding: handlers receive zero arguments.
- Accepts exactly one sub-pattern (no zero- or multi-argument form).
neg(neg(p))restores the original match behavior (double negation cancels).- Operator sugar:
!pis equivalent toneg(p)and needs no parentheses:!val<200> >> "error".
The pattern-level operators only accept pattern operands, so they never
collide with the guard-level && / || (which keep their pred_and /
pred_or meaning inside [...] guards).
Cached Case Packs¶
static_on(...)¶
Cache a stateless on(...) factory.
match(x) | static_on([] {
return on(
val<1> >> 1,
val<2> >> 2,
_ >> 0
);
});
PTN_ON(...)¶
Convenience macro over static_on(...).
match(x) | PTN_ON(
val<1> >> 1,
val<2> >> 2,
_ >> 0
);
The factory must be stateless.
Namespace Summary¶
The public surface is available through namespace ptn, plus the macros listed
below:
matchonlit,val,lit_ci$_,rnghasis,altany,all,negPTN_ON(macro)PTN_BIND(deprecated compatibility macro)
Minimal Example¶
#include <ptn/patternia.hpp>
int main() {
using namespace ptn;
int x = 2;
int r = match(x) | on(
lit(1) >> 10,
lit(2) >> 20,
_ >> 0
);
return r == 20 ? 0 : 1;
}