From 1957b7e39b5d64836477f14d99f8a947871be6e4 Mon Sep 17 00:00:00 2001 From: Yingfan Guo <115908952+sentomk@users.noreply.github.com> Date: Mon, 26 Oct 2026 22:59:49 +0800 Subject: [PATCH] docs(tutorials): migrate guards to structured-binding names Problem - PTN_BIND is deprecated, but three tutorials still used it as the primary way to name guard placeholders. - The policy tutorial's guest rule did not compile: its predicate took one int, but the guard passes both members listed in has<...>. - The roadmap did not mention the unreleased has<...> naming work and pointed at an empty WIP section. Implementation - Rewrite other-languages, policy-constraint and sphere-constraint to declare names with auto [a, b] = has<...>. - Make is_public_resource take (Role, int) and explain that a callable guard receives every member listed in has<...>. - Add an unreleased block to the roadmap FINISHED list and fix the stale "see WIP above" reference. Tests - Compiled and ran the updated snippets with g++ and clang++ in C++17 and C++20. --- docs/roadmap.md | 11 ++++++++++- docs/tutorials/other-languages.md | 6 +++--- docs/tutorials/policy-constraint.md | 16 +++++++++++++--- docs/tutorials/sphere-constraint.md | 15 ++++++++------- 4 files changed, 34 insertions(+), 14 deletions(-) diff --git a/docs/roadmap.md b/docs/roadmap.md index 5e0477a..d913869 100644 --- a/docs/roadmap.md +++ b/docs/roadmap.md @@ -11,6 +11,15 @@ Status labels: ## FINISHED +Completed on `main` (unreleased): + +- `has<...>` is tuple-like: `auto [x, y] = has<&Point::x, &Point::y>;` + declares member-anchored guard placeholders without a macro. +- `PTN_BIND` is deprecated in favor of structured-binding member names. + It remains supported, and is still needed for function-local names + inside `PTN_ON` in C++17. +- Bare `is` and `alt` reach the static variant dispatch fast path. + Completed in [v0.9.4](changelog/v0.9.4.md): - `PTN_BIND(Type, names...)` member-anchored named guard @@ -111,4 +120,4 @@ here so they are not re-proposed without new motivation. with explicit range modes. - **`PTN_BIND` `Type` validation**: resolved without reflection — names now expand to `member_t<&Type::name>`, so member checking - happens at declaration time (see WIP above). + happens at declaration time (shipped in v0.9.4). diff --git a/docs/tutorials/other-languages.md b/docs/tutorials/other-languages.md index fbf7059..40d2b89 100644 --- a/docs/tutorials/other-languages.md +++ b/docs/tutorials/other-languages.md @@ -58,11 +58,11 @@ auto label = match(x) | on( Without native language destructuring, Patternia uses explicit member pointers: ```cpp -PTN_BIND(User, age, active); +constexpr auto u = has<&User::age, &User::active>; +auto [age, active] = u; auto label = match(user) | on( - $(has<&User::age, &User::active>)[age < 18 && active == true] - >> "minor", + $(u)[age < 18 && active == true] >> "minor", _ >> "adult" ); ``` diff --git a/docs/tutorials/policy-constraint.md b/docs/tutorials/policy-constraint.md index bd5661b..e431d54 100644 --- a/docs/tutorials/policy-constraint.md +++ b/docs/tutorials/policy-constraint.md @@ -20,21 +20,27 @@ struct Request { ## Naming Domain Predicates ```cpp -auto is_public_resource = [](int id) { +auto is_public_resource = [](Role, int id) { return id >= 0 && id < 1000; }; ``` +A callable guard receives every member listed in `has<...>`, in order. The +rule below lists `role` and `resource_id`, so the predicate takes both, even +though it only inspects the id. + ## Declaring the Rules ```cpp bool allowed(const Request &req) { using namespace ptn; - PTN_BIND(Request, role, resource_id, read_only); + constexpr auto request = + has<&Request::role, &Request::resource_id, &Request::read_only>; + auto [role, resource_id, read_only] = request; return match(req) | on( $(has<&Request::role>)[_ == Role::Admin] >> true, - $(has<&Request::role, &Request::resource_id, &Request::read_only>) + $(request) [role == Role::User && resource_id >= 0 && read_only == true] >> true, $(has<&Request::role, &Request::resource_id>) [role == Role::Guest && is_public_resource] >> true, @@ -52,3 +58,7 @@ Each rule is self-contained: - the handler states the outcome That keeps policy code readable even when ordering matters. + +The names declared from `request` follow members, not positions, so the +third rule can use `role` with a pattern that lists only `role` and +`resource_id`. diff --git a/docs/tutorials/sphere-constraint.md b/docs/tutorials/sphere-constraint.md index 8174dc9..be740dd 100644 --- a/docs/tutorials/sphere-constraint.md +++ b/docs/tutorials/sphere-constraint.md @@ -34,21 +34,22 @@ const char *classify(const Vec3 &p) { ## Refining the Constraint -You can combine a named predicate with a `PTN_BIND` placeholder for simple -extra checks: +You can combine a named predicate with a structured-binding member name for +simple extra checks: ```cpp const char *classify_upper(const Vec3 &p) { using namespace ptn; - PTN_BIND(Vec3, x, y, z); + constexpr auto xyz = has<&Vec3::x, &Vec3::y, &Vec3::z>; + auto [x, y, z] = xyz; return match(p) | on( - $(has<&Vec3::x, &Vec3::y, &Vec3::z>)[z >= 0 && inside_unit_sphere] - >> "inside", + $(xyz)[z >= 0 && inside_unit_sphere] >> "inside", _ >> "outside" ); } ``` -For multi-value guards, prefer `PTN_BIND` names or a callable that accepts all -bound values. +For multi-value guards, prefer structured-binding member names or a callable +that accepts all bound values. Only `z` is used here; unused names such as `x` +and `y` are fine.