Skip to content

feat(pattern): add operator sugar for combinators - #50

Merged
sentomk merged 1 commit into
mainfrom
feat/pattern-operator-sugar
Aug 1, 2026
Merged

feat(pattern): add operator sugar for combinators#50
sentomk merged 1 commit into
mainfrom
feat/pattern-operator-sugar

Conversation

@sentomk

@sentomk sentomk commented Aug 1, 2026

Copy link
Copy Markdown
Owner

Summary

Add operator forms for the pattern combinators: !p for neg(p), (a || b) for any(a, b), and (a && b) for all(a, b). The pattern algebra now reads like the boolean logic it models.

match(status) | on(
  !val<200>                  >> "error",
  (lit(301) || lit(302))     >> "redirect",
  (pred(is_json) && !lit(0)) >> "json payload",
  _                          >> "ok"
);

Changes

  • negation.hpp: operator! equivalent to neg(p).
  • combinator.hpp: operator|| equivalent to any(a, b); operator&& equivalent to all(a, b).
  • The overloads are declared in ptn::pat::base: every pattern derives from pattern_base, which makes that namespace ADL-associated for all pattern types. (Concrete patterns live in ptn::pat::detail, and ADL does not ascend enclosing namespaces — declaring the operators in ptn::pat would make them unfindable.)
  • Operands are constrained to patterns that are not guard predicates, so guard-level && / || inside [...] keep their pred_and / pred_or semantics — verified by test.
  • Docs: docs/api.md combinator sections, roadmap WIP entry, README negation example.

Testing

  • Nine new cases in tests_combinator.cpp: type identity of !p vs neg(p), or/and/chained-or/double-bang behavior, sugar mixing pattern kinds (is<int> || is<std::string>), and guard-operator non-interference.
  • Full suite: 204/204 passing locally (Clang 20, C++17).
  • One caveat, documented: >> binds tighter than ||/&&, so combined patterns need parentheses in a case — (lit(1) || lit(2)) >> handler. Unary ! needs none.

Problem
- The pattern algebra had only function-call forms (neg, any,
  all), which read noisily in case lists compared to the boolean
  operators they model.

Implementation
- Add operator! for neg(p), operator|| for any(a, b), and
  operator&& for all(a, b).
- Declare the overloads in ptn::pat::base: every pattern derives
  from pattern_base, making that namespace ADL-associated for all
  pattern types (concrete patterns live in ptn::pat::detail and
  ADL does not ascend namespaces).
- Constrain operands to patterns that are not guard predicates,
  so guard-level && / || keep their pred_and / pred_or semantics.

Tests
- Nine cases in tests_combinator: type identity of !p vs neg(p),
  or/and/chained/double-bang behavior, sugar on type patterns,
  and guard-operator non-interference.
- Full suite: 204/204 (Clang 20, C++17).

Notes
- `>>` binds tighter than || and &&, so combined patterns need
  parentheses in a case: (lit(1) || lit(2)) >> handler. Unary !
  needs none. Documented in docs/api.md and README.
@codecov

codecov Bot commented Aug 1, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@sentomk
sentomk merged commit 640aa5f into main Aug 1, 2026
22 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant