From cb4a64e710541481ceeb3c27a3011cf9d47413e4 Mon Sep 17 00:00:00 2001 From: Bronek Kozicki Date: Mon, 28 Sep 2026 20:35:29 +0100 Subject: [PATCH] Move `pack` and `copack` operations to `` Separate the data types from operator&, conjoin, disjoin and the carrier join helpers. Keep the data operator& overload set together in algebra.hpp. Make copack.hpp include pack.hpp so void transforms need no extra include. Carrier headers include algebra.hpp; direct users of the moved operations must include it. Move the corresponding tests to algebra.cpp. Assisted-by: Claude:claude-opus-5-5 Assisted-by: Codex:gpt-6-astra --- CHANGELOG.md | 6 + docs/reference/conjoin.md | 40 ++-- docs/reference/disjoin.md | 2 +- docs/reference/pack.md | 2 + examples/calculator/calculator.hpp | 1 + examples/type_algebra/main.cpp | 1 + include/CMakeLists.txt | 1 + include/fn/algebra.hpp | 240 +++++++++++++++++++ include/fn/copack.hpp | 6 +- include/fn/detail/traits.hpp | 6 + include/fn/detail/variadic_union.hpp | 7 +- include/fn/expected.hpp | 1 + include/fn/just.hpp | 1 + include/fn/optional.hpp | 1 + include/fn/pack.hpp | 242 +------------------ tests/CMakeLists.txt | 1 + tests/fn/algebra.cpp | 344 +++++++++++++++++++++++++++ tests/fn/copack.cpp | 1 - tests/fn/detail/functional.cpp | 1 + tests/fn/optional.cpp | 39 --- tests/fn/pack.cpp | 306 +----------------------- tests/util/helper_types.hpp | 7 + 22 files changed, 646 insertions(+), 610 deletions(-) create mode 100644 include/fn/algebra.hpp create mode 100644 tests/fn/algebra.cpp diff --git a/CHANGELOG.md b/CHANGELOG.md index f64d5cc1..bf88c009 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,12 @@ Design history of libfn, newest first. The living documents — [README.md](README.md), [CONTRIBUTING.md](CONTRIBUTING.md), [docs/](docs/) — describe only the present state of the design; when a decision makes an earlier idea obsolete, this file is where the transition is recorded and explained. +## `pack` and `copack` operations move to `` — 27 September 2026 + +`operator&` over `pack` and `copack`, `conjoin`, and `disjoin` move to ``. Include this header when using these operations without a carrier header. The `expected`, `optional`, and `just` headers include it. + +`` includes ``; the reverse dependency is removed. A copack transform returning `void` therefore needs only ``. Code using `copack` through `` must include `` explicitly. + ## `&` takes a `void` side as the unit factor `pack<>` — 27 September 2026 `&` treats a `void` value as the unit factor `pack<>`. For example, `expected & just` yields `expected, E>`; previously it yielded `expected`. This breaking change applies across carrier pairings in either order. Copack values distribute into packs, and `optional & just` yields an owning `optional>`. Two `void` values still yield `void`. diff --git a/docs/reference/conjoin.md b/docs/reference/conjoin.md index 665a855e..a7013f78 100644 --- a/docs/reference/conjoin.md +++ b/docs/reference/conjoin.md @@ -2,7 +2,7 @@ title: "fold fn::conjoin" --- -##### Defined in {style: "api", badge: "#include "} +##### Defined in {style: "api", badge: "#include "} --- @@ -29,54 +29,54 @@ conjoin_t conjoin; // (1) ## The operator {style: "api"} -The binary conjunction each carrier declares; `conjoin` is its n-ary fold. +Binary conjunction of data or carriers; `conjoin` is its n-ary fold. ```cpp {title: "fn::operator&"} +constexpr auto operator&(auto &&lh, auto &&rh); // (1) + template -constexpr auto operator&(Lh &&lh, Rh &&rh); // (1) +constexpr auto operator&(Lh &&lh, Rh &&rh); // (2) template -constexpr auto operator&(Lh &&lh, Rh &&rh); // (2) +constexpr auto operator&(Lh &&lh, Rh &&rh); // (3) template -constexpr auto operator&(Lh &&lh, Rh &&rh); // (3) +constexpr auto operator&(Lh &&lh, Rh &&rh); // (4) template -constexpr auto operator&(Lh &&, Rh &&rh); // (4) +constexpr auto operator&(Lh &&, Rh &&rh); // (5) template -constexpr auto operator&(Lh &&lh, Rh &&); // (5) +constexpr auto operator&(Lh &&lh, Rh &&); // (6) template -constexpr auto operator&(Lh &&lh, Rh &&rh); // (6) -constexpr auto operator&(Lh &&, Rh &&) -> just; // (7) +constexpr auto operator&(Lh &&lh, Rh &&rh); // (7) +constexpr auto operator&(Lh &&, Rh &&) -> just; // (8) template -constexpr auto operator&(Lh &&lh, Rh &&rh); // (8) - -template constexpr auto operator&(Lh &&lh, Rh &&rh); // (9) -template +template constexpr auto operator&(Lh &&lh, Rh &&rh); // (10) -template +template constexpr auto operator&(Lh &&lh, Rh &&rh); // (11) -template +template constexpr auto operator&(Lh &&lh, Rh &&rh); // (12) -constexpr auto operator&(auto &&lh, auto &&rh); // (13) +template +constexpr auto operator&(Lh &&lh, Rh &&rh); // (13) ``` -:include-doxygen-doc: fn::operator& { args: "Lh &&, Rh &&" } - -:include-doxygen-doc-params: fn::operator& { args: "Lh &&, Rh &&", title: "parameters" } - :include-doxygen-doc: fn::operator& { args: "auto &&, auto &&" } :include-doxygen-doc-params: fn::operator& { args: "auto &&, auto &&", title: "parameters" } +:include-doxygen-doc: fn::operator& { args: "Lh &&, Rh &&" } + +:include-doxygen-doc-params: fn::operator& { args: "Lh &&, Rh &&", title: "parameters" } + --- ## Call signatures {style: "api"} diff --git a/docs/reference/disjoin.md b/docs/reference/disjoin.md index 2dcba1c1..f57b868d 100644 --- a/docs/reference/disjoin.md +++ b/docs/reference/disjoin.md @@ -2,7 +2,7 @@ title: "fold fn::disjoin" --- -##### Defined in {style: "api", badge: "#include "} +##### Defined in {style: "api", badge: "#include "} --- diff --git a/docs/reference/pack.md b/docs/reference/pack.md index f898672d..b06ada56 100644 --- a/docs/reference/pack.md +++ b/docs/reference/pack.md @@ -133,6 +133,8 @@ template constexpr auto get(P &&p) -> decltype(auto); // (3) ``` +Overloads (1) and (2) take a `copack` and are defined in ``. + :include-doxygen-doc: fn::get { args: "Cp &&" } :include-doxygen-doc-params: fn::get { args: "Cp &&", title: "parameters" } diff --git a/examples/calculator/calculator.hpp b/examples/calculator/calculator.hpp index 3ad129ea..a2b56da2 100644 --- a/examples/calculator/calculator.hpp +++ b/examples/calculator/calculator.hpp @@ -6,6 +6,7 @@ #ifndef EXAMPLES_CALCULATOR_CALCULATOR #define EXAMPLES_CALCULATOR_CALCULATOR +#include #include #include #include diff --git a/examples/type_algebra/main.cpp b/examples/type_algebra/main.cpp index bc533620..f3a60229 100644 --- a/examples/type_algebra/main.cpp +++ b/examples/type_algebra/main.cpp @@ -1,3 +1,4 @@ +#include #include #include #include diff --git a/include/CMakeLists.txt b/include/CMakeLists.txt index cfd60e5f..13278643 100644 --- a/include/CMakeLists.txt +++ b/include/CMakeLists.txt @@ -112,6 +112,7 @@ set(INCLUDE_FN_HEADERS fn/detail/pack_impl.hpp fn/detail/traits.hpp fn/detail/variadic_union.hpp + fn/algebra.hpp fn/and_then.hpp fn/concepts.hpp fn/copack.hpp diff --git a/include/fn/algebra.hpp b/include/fn/algebra.hpp new file mode 100644 index 00000000..4002db75 --- /dev/null +++ b/include/fn/algebra.hpp @@ -0,0 +1,240 @@ +// Copyright (c) 2026 Bronek Kozicki +// +// Distributed under the ISC License. See accompanying file LICENSE.md +// or copy at https://opensource.org/licenses/ISC + +#ifndef INCLUDE_FN_ALGEBRA +#define INCLUDE_FN_ALGEBRA + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +namespace fn { +inline namespace LIBFN_VERSION { + +namespace detail { + +// The join checks has_value() before value(); exclude the accessor +// from noexcept calculations for folding and result construction. +template using _value_of_t = decltype(::std::declval().value()); + +template +using _factor_t = ::std::conditional_t<::std::is_void_v::value_type>, + ::fn::pack<>, typename ::std::remove_cvref_t::value_type>; +template +using _factor_of_t = ::std::conditional_t<::std::is_void_v::value_type>, + ::fn::pack<>, _value_of_t>; + +template [[nodiscard]] constexpr auto _factor(Monad &&m) -> _factor_of_t +{ + if constexpr (::std::is_void_v::value_type>) + return {}; + else + return FWD(m).value(); +} + +// An uninhabited factor has no value fold. Avoid instantiating it in types, +// noexcept specifications and bodies. +template +constexpr inline bool _uninhabited_join = empty_copack::value_type> + || empty_copack::value_type>; + +template struct _joined { + using type = copack<>; +}; +template struct _joined { + using type = decltype(::fn::detail::_fold_detail::fold<_factor_t, _factor_t>( + ::std::declval<_factor_of_t>(), ::std::declval<_factor_of_t>())); +}; +template using _joined_t = typename _joined<_uninhabited_join, Lh, Rh>::type; + +// Match the named efn parameter, which _join invokes as an lvalue. +template typename Tpl, typename Lh, typename Rh, typename Efn> +struct _nothrow_join_arm { + static constexpr bool value = ::std::is_nothrow_invocable_v && ::std::is_nothrow_invocable_v + && _nothrow_initializable>, ::std::invoke_result_t> + && _nothrow_initializable>, ::std::invoke_result_t>; +}; +template