Conjoin two ungraded error types into their sum - #385
Merged
Conversation
The conjunction's error channel is the sum, so producing the grade is the operator's work, not a precondition on its operands - and nowhere else does a sum ask for one: `|` forms its value sum out of two ungraded value types. The four different-error arms drop the clause requiring one operand's error to be a copack already; each already widens to copack_for<E1, E2>. Assisted-by: Claude:claude-opus-5
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
🤖 Augment PR SummarySummary: This PR fixes a constraint bug in Changes:
Technical Notes: The change is intentionally additive (previously ill-formed expressions become well-formed) while preserving the matching-error fast path (still collapses to the plain error type). 🤖 Was this summary useful? React with 👍 or 👎 |
|
Bronek
added a commit
that referenced
this pull request
Jul 26, 2026
Sections 1, 2, 6, 7 and 9 no longer state the grading precondition #385 removed; section 6's example now conjoins plain error types, so the fence asserts the operator forming the grade rather than requiring it. Sections 6 and 7 share one skeleton, each stating its channel rules once. The rest is prose: announcing openers, passages restating their own section, and three admonitions. Assisted-by: Claude:claude-opus-5
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



Fixes #384.
expected<int, Error> & expected<bool, ErrorOther>was ill-formed unless one of the two error types was already acopack, while the same pair disjoined without complaint. The conjunction's error channel is the sum, so producing the grade is the operator's work, not a precondition on its operands — and nowhere else does a sum ask for one:|forms its value sum out of two ungraded value types, in every carrier. Of the four channels —&'s value product and error sum,|'s value sum and error product — this was the only one gated.The fix deletes the clause
from the four different-error
operator&arms (void×value, value×void, void×void, value×value). Each already computescopack_for<El, Er>and lifts every operand's error through thecopack<>-guarded_nothrow_arm, so nothing downstream moved: the widened result, the injection by type and the computednoexceptare those the graded pair has always had.fn::conjoinfollows without a change of its own, being constrained on the fold being well-formed.Purely additive — every expression newly accepted was ill-formed before, and a matching pair of error types still collapses to the plain error it always had. In the n-ary fold only the first pair was ever blocked:
conjoinfolds left, so after one&the accumulated error is acopackand every later step already took the graded path.The bind is deliberately untouched.
and_thenandor_elseare the foundational operations, and a callback whose error type merely differs from self's still refuses: switching a pipeline to the graded monad is the caller's decision, not something a bind makes for them.Tests
tests/fn/expected.cppcarried the defect as a pin —static_assert(not can_amp(fn::expected<int, other_error>{1})); // mismatched non-graded error— now positive, with a converse keeping the kinds apart (not can_amp(fn::optional<int>{1})). A newSECTION("different plain error types")covers all four value shapes: the produced types, the leftmost-failure injection by type through a single two-arm probe (so both arms run), the constant-evaluated twins, and thenoexceptaxis where both operands' error relocations weigh.tests/fn/pack.cppgains the matchingconjoinrows beside the existing carrier fold.Verification
VALIDATE_CXX23— 6/6 passLIBFN_CXX26) — pass, 86/86clang-format --dry-run -Werrorclean