Skip to content

Patternia v0.9.0 Release Note

Release Date: March 13, 2026 Version: 0.9.0


Overview

Patternia v0.9.0 is an API modernization and compiler compatibility release.

It introduces PTN_WHERE and PTN_LET as the canonical named-guard syntax, removes the legacy bind() entry point and positional guard aliases _1/_2/_3, and ships a series of fixes that make callable guards work correctly across GCC, Clang, and MSVC. vcpkg packaging support is also added in this version.


Highlights

Named Guard Macros: PTN_WHERE and PTN_LET

PTN_WHERE((names...), expr) gives guard arguments explicit names without writing a lambda. It supports 1 to 5 bound values:

using namespace ptn;

struct Point { int x; int y; };

bool on_diagonal(const Point &p) {
  return match(p) | on(
    $(has<&Point::x, &Point::y>())[PTN_WHERE((x, y), x == y)] >> true,
    _ >> false
  );
}

PTN_LET(name, expr) is the single-value shorthand, equivalent to PTN_WHERE((name), expr):

using namespace ptn;

const char *bucket(int x) {
  return match(x) | on(
    $[PTN_LET(value, value < 0)] >> "negative",
    $[PTN_LET(value, value < 10)] >> "small",
    _ >> "large"
  );
}

Both macros expand to captureless guard callables and compose with && / || like other guard predicates.

API Cleanup: bind() and _1/_2/_3 Removed (Breaking)

The legacy bind() entry point and the positional guard aliases _1, _2, _3 have been removed from the public surface.

Migrate to the current equivalents:

Removed Replacement
bind() $
bind(subpattern) $(subpattern)
_1 arg<0> or PTN_LET
_2 arg<1>
_3 arg<2>

A compile-fail test covers both removed names to prevent silent regressions.

Callable Guard Fixes for MSVC and Standard C++

Four guard fixes were shipped in this cycle:

  • Callable guards evaluate as tuple predicates. The guard trait system now classifies callable_guard correctly so PTN_WHERE/PTN_LET participate in composed guard evaluation (&&, ||) on all compilers.
  • Replaced std::apply in tuple guard calls with an explicit index sequence expansion to avoid MSVC ICEs on conformance-mode builds.
  • Replaced std::invoke in callable guard dispatch with direct call syntax for the same reason.
  • MSVC traditional preprocessor workaround for PTN_WHERE argument counting via __VA_ARGS__ indirection.

These fixes have no effect on GCC and Clang builds. MSVC users with /Zc:preprocessor enabled are not affected either.

vcpkg Support

Patternia is now available through the vcpkg package manager:

vcpkg install patternia

Or via vcpkg.json manifest:

{
  "dependencies": ["patternia"]
}

Consume through the existing CMake target:

find_package(patternia CONFIG REQUIRED)
target_link_libraries(your_target PRIVATE patternia::patternia)

Migration Notes

bind() removed

// Before
match(x) | on(
  bind()[_0 > 0] >> [](int v) { return v; },
  _ >> 0
);

// After
match(x) | on(
  $[_0 > 0] >> [](int v) { return v; },
  _ >> 0
);

_1/_2/_3 removed

// Before
$(has<&Point::x, &Point::y>())[_1 == _2] >> "diagonal"

// After
$(has<&Point::x, &Point::y>())[arg<0> == arg<1>] >> "diagonal"
// or
$(has<&Point::x, &Point::y>())[PTN_WHERE((x, y), x == y)] >> "diagonal"

Bug Fixes

  • Fixed PTN_WHERE argument counting failing under MSVC traditional preprocessor mode. Added __VA_ARGS__ indirection as a standard workaround. No effect on conforming preprocessors.
  • Fixed callable guards failing to dispatch bound values correctly in composed guard expressions by routing them through the tuple predicate path instead of std::invoke.
  • Fixed std::apply-based tuple guard calls producing ICEs on MSVC conformance builds.

Internal

  • include/ptn/pattern/modifiers/guard.hpp: added callable_guard type and make_callable_guard, rewrote tuple expansion without std::apply or std::invoke.
  • include/ptn/pattern/base/pattern_traits.hpp, modifiers/fwd.h: added callable_guard to the guard trait classifier.
  • include/ptn/patternia.hpp: added PTN_WHERE and PTN_LET macros, removed bind and _1/_2/_3 exports.
  • tests/compile_fail/: added bind_function_removed.cpp and guard_placeholder_alias_removed.cpp.