Skip to content

Add TYPE_ALGEBRA - #359

Merged
Bronek merged 59 commits into
mainfrom
bronek/draft_TYPE_ALGEBRA
Aug 1, 2026
Merged

Add TYPE_ALGEBRA#359
Bronek merged 59 commits into
mainfrom
bronek/draft_TYPE_ALGEBRA

Conversation

@Bronek

@Bronek Bronek commented Jul 20, 2026

Copy link
Copy Markdown
Member

Add a comprehensive, mathematically rigorous technical specification of the type-level algebra and functional-composition model of libfn/functional.

  • Define core algebraic vocabulary (pack, copack, expected, optional, choice, and just) and map them to their corresponding ADT cardinalities (0, 1, A+B, AxB).
  • Detail graded error-set unioning (widening/subeffecting) and outline implicit-rejection safety boundaries alongside explicit narrowing via transform_error.
  • Document identity cluster transitions, explaining the decoupling design (member functions strict to carriers vs. namespace fn:: pipeline operators).
  • Explain type-tagged elimination (apply_type) using C++ standard state and constructor tags (std::in_place, fn::unexpect, std::nullopt, std::in_place_type).
  • Consolidate prose, remove redundant descriptions, and include 100% compile-verified C++20 code blocks demonstrating same-kind concept boundaries, pack::append splicing, and Cartesian product distribution.

Documentation site

TYPE_ALGEBRA now renders on libfn.org alongside README, CONTRIBUTING and LICENSE, each generated from the document itself rather than copied into a second place that can drift.

Sections. The site reads README · TYPE ALGEBRA · CONTRIBUTING · REFERENCE · CONTINUOUS INTEGRATION · LICENSE. A section is named for the file it carries and its pages keep the document's own headings. TYPE_ALGEBRA splits at its ## boundaries into eighteen pages, its Section N cross-references becoming links to the pages they name; the other documents stay whole. Section numbering never reaches the site — a page carries the title alone. docs/index.md is gone: it restated README's introduction in a hand-maintained copy, and the two had already drifted, so README is the front page and znai generates the redirect to it. Badges are stripped on the way in; they belong on the repository page.

Reference. Ten per-carrier chapters became one flat section with a page per feature, the category in the page title — type fn::pack, monad fn::expected, functor fn::and_then, fold fn::conjoin, multidispatch fn::apply, other fn concepts, polyfills pfn. A verb had been documented once per carrier it applies to, byte-identical in seven cases of eleven. Twenty-four pages replace thirty-eight, verified lossless: no distinct doxygen member lost, every example region preserved. The concepts, the utilities, and fn::functor — the extension point behind the pipeline — reach the site for the first time.

Continuous integration. The section named seven of the fifteen workflows and pointed at image paths that no longer exist. It now describes the pipeline as it is, including why coverage and analysis each come in two workflows: a pull request from a fork is given no credentials, so the build uploads an artifact and a second workflow reports from the base repository.

Generation

scripts/stage_docs_source.py stages the znai source into the build tree, so the documents stay single files in git and docs/ stays hand-written. scripts/fix_site_urls.py repairs the deployed output, which znai leaves in two states it cannot be talked out of:

  • with an empty doc id — which is what deploying at a domain root means — every internal link is emitted as //chapter/page, a network-path reference that sends the browser to a host named after the chapter. None of the four link spellings znai accepts avoids it.
  • View on GitHub was one link for the whole site, and a broken one (/docs, without GitHub's blob/main). Each page now carries the path it was rendered from.

Both repairs fail the build if they find nothing to do, so a znai release that fixes either will say so rather than leave the workaround standing quietly.

Doxygen stays at 1.12.0. 1.17.0 was tried and rejected: it exits zero while silently rendering the requires(not some_copack<value_type>) overload of copack_value once where 1.12.0 renders it twice, on both the expected and optional pages.

Verification

docs runs on every pull request, so a dangling link, an unresolvable directive or a missing page fails review rather than the deployment — znai is strict about all three, which is how each of the problems above surfaced. The generation scripts carry no test of their own: the docs build is their gate, and there is no python test harness in the repository to host one.

Document principles

All important concepts appear early in a short introduction, and are expanded further in the following sections. Less important concepts are introduced in the sections where they are most useful. No concept is used before it is introduced. The document should be easy to any C++ software engineer to read and digest, and it should be also informative for these versed in category theory.

1. The Opening Map (Preamble & Chapter 3)

  • Payload Types (pack/copack) and Computation Carriers (optional/expected/just/choice) are first introduced as a sparse, high-level list in the main preamble.
  • The term identity cluster is introduced in Chapter 3 under the infallible carriers as a simple grouping definition: "Together, just, choice and expected<T, copack<>> form the identity cluster." It does not expand on its operations or mathematical properties here, keeping the introduction minimal.

2. Core Algebraic Foundations (Chapters 2, 3, & 4)

  • Chapter 2 establishes the core algebraic identities of types (0, 1, A + B, A × B), separates Zero (copack<>) from Unit (pack<>), and defines copack set semantics (deduplication, flattening, sorting) and why the algebra is strictly opt-in.
  • Chapter 3 establishes the carriers, the fact that raw data lacks control flow while carriers have it, and the basics of cross-carrier recovery-path bridging.
  • Chapter 4 expands on the concrete C++ payload behaviors of pack and copack (ADL get, structured bindings, .append(), singular copacks).

3. Progressive Functional Composition (Chapters 5–9)

  • Once the data layers (payloads) and computational contexts (carriers) are defined, the document advances into composition:
    • Chapter 5 (Mapping): Covers functorial mapping (transform/transform_error).
    • Chapter 6 (Conjunction - operator&): Multiplies values into packs and unions errors into copacks.
    • Chapter 7 (Disjunction - operator|): Unions values into copacks and multiplies errors into packs.
    • Chapter 8 (Monadic Binding - and_then): Introduces Kleisli arrows and monadic sequencing of success paths.
    • Chapter 9 (Graded Expected): Explains how the type system derives exact error union grades during sequential binding.

4. Advanced Algebraic Crossings (Chapters 10 & 11)

  • Only after all core compositions have been mastered does the document expand the advanced infallible states:
    • Chapter 10 (Identity Cluster): Curates the actual success-path licensing, cross-carrier transitions, and specialized monadic rules for the cluster members.
    • Chapter 11 (choice Monad): Explains the specific newtype monadic properties of choice, how it behaves atomically compared to flat copack data, and its categorical laws.

5. Elimination & Reference (Chapters 12–15)

  • Chapter 12 (Multidispatch): Introduces the exit point from the algebra back to ordinary C++ values via apply and apply_type.
  • Chapters 13–15 (Map, Laws, Mechanics): Synthesizes everything into a unified operational map, compiler-checked monad/functor equations, and the underlying C++ standard rules preserving the model.

Assisted-by: Claude:claude-opus-5
Assisted-by: Gemini:gemini-3.6-flash

@augmentcode

augmentcode Bot commented Jul 20, 2026

Copy link
Copy Markdown
🤖 Augment PR Summary

Summary: Adds a new design/spec document (TYPE_ALGEBRA.md) and updates the documentation pipeline so the site is generated directly from the repo’s canonical markdown sources.

Changes:

  • Introduces TYPE_ALGEBRA.md with compile-verified C++20 examples explaining libfn’s type-algebra model, graded errors, composition operators, and multidispatch.
  • Updates the docs site structure (new docs/reference/ pages, refreshed TOC, and a rewritten continuous-integration chapter).
  • Adds scripts/stage_docs_source.py to stage/reshape the Znai source tree so root documents become first-class chapters (with optional splitting at ##).
  • Adds scripts/fix_site_urls.py to post-process Znai output (fixing doubled internal URLs and populating per-page “View on GitHub” paths).
  • Replaces the README-only example sync hook with a generalized scripts/sync_md_examples.py hook and adds a corresponding hook for TYPE_ALGEBRA examples.
  • Extends the examples build with a new examples/type_algebra target used as the source-of-truth for fenced code in the spec.

Technical Notes: The docs CMake now requires a Python interpreter for staging and post-processing; docs generation becomes stricter by failing the build if the workaround scripts find nothing to patch.

🤖 Was this summary useful? React with 👍 or 👎

@augmentcode augmentcode Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review completed. 2 suggestions posted.

Fix All in Augment

Comment augment review to trigger a new review at any time.

Comment thread TYPE_ALGEBRA.md Outdated
Comment thread TYPE_ALGEBRA.md Outdated
Add a comprehensive, mathematically rigorous technical specification of the type-level algebra and functional-composition model of libfn/functional.

- Define core algebraic vocabulary (pack, copack, expected, optional, choice, and just) and map them to their corresponding ADT cardinalities (0, 1, A+B, AxB).
- Detail graded error-set unioning (widening/subeffecting) and outline implicit-rejection safety boundaries alongside explicit narrowing via transform_error.
- Document identity cluster transitions, explaining the decoupling design (member functions strict to carriers vs. namespace fn:: pipeline operators).
- Explain type-tagged elimination (apply_type) using C++ standard state and constructor tags (std::in_place, fn::unexpect, std::nullopt, std::in_place_type).
- Consolidate prose, remove redundant descriptions, and include 100% compile-verified C++20 code blocks demonstrating same-kind concept boundaries, pack::append splicing, and Cartesian product distribution.

Assisted-by: Gemini:gemini-3.5-flash
@Bronek
Bronek force-pushed the bronek/draft_TYPE_ALGEBRA branch from e49ba49 to d3ed77f Compare July 20, 2026 19:20
Bronek added 16 commits July 23, 2026 10:41
* Document simultaneous disjunction and conjunction's identity cluster support
* Fix pack get projection ADL syntax and Section 9 cross-carrier constraints
* Add singular lift and get extraction documentation to Section 3
* Elevate all mathematical notes to category-theoretically rigorous style
* Eliminate duplicate graded monad definitions and fix apply_type carrier list

Assisted-by: Gemini:gemini-3.6-flash
* Fold fn::as_pack directly into existing test_pack() example
* Fold fn::as_copack and ADL get() into existing test_copack() example
* Clean up and streamline prose for primary narrative
* Ensure category-theoretic mathematical notes are consistent and rigorous

Assisted-by: Gemini:gemini-3.6-flash
* Re-format copack vs copack_for under-the-hood section as standard [!NOTE] block
* Re-format nested coproduct just/choice section as standard [!NOTE] block

Assisted-by: Gemini:gemini-3.6-flash
* Detail how success mapping (transform) promotions work with pipeline operators
* Detail how recovery (or_else/recover) and fallbacks (value_or) are compile-rejected or vacuously dead
* Connect identity cluster behaviors systematically to the Section 12 operations map

Assisted-by: Gemini:gemini-3.6-flash
* Move fail to the Success Channel group as a success-intercepting transition operation
* Move recover to the Error/Empty Channel group as a failure-intercepting transition operation
* Add explicit dual symmetry rules for fail and recover under Key Architectural Rules

Assisted-by: Gemini:gemini-3.6-flash
…ary folds

* Add operator| (disjunction / simultaneous sum composition) to the core operations list
* Clarify that operator& represents conjunction
* Explicitly introduce the fn::conjoin and fn::disjoin n-ary fold utilities

Assisted-by: Gemini:gemini-3.6-flash
… TYPE_ALGEBRA.md

* Correct copack apply description to note recursive unpacking of nested tuple-likes
* Document the terminal data guarantee of normalized sum-of-products multidispatch
* Refine carrier terminology to gently introduce 'monadic' context as functional jargon
* Polished layout into two clean, readable paragraphs in Section 3

Assisted-by: Gemini:gemini-3.6-flash
* Fix spelling and grammatical structure of the constrained parameters warning
* Fully integrate examples/type_algebra/main.cpp into the project CMake build
* Enable pre-commit enforcement of the synchronized code regions
* Verify compile-clean success across all 44 unit and example tests

Assisted-by: Gemini:gemini-3.6-flash
…ions

* Fix C++26 compilation errors with LIBFN_CXX26
* Polish copack, copack_for, choice, and choice_for terminology for strict ordering
* Document and illustrate value-category preservation in deduction-only as_pack
* Document and verify by-value decay and multi-argument coercion in explicit as_pack
* Synchronize all code regions in TYPE_ALGEBRA.md with examples/type_algebra/main.cpp

Assisted-by: Gemini:gemini-3.6-flash
…ions

* Relax type algebra checks in examples to use stable choice instead of choice_for
* Polish copack, copack_for, choice, and choice_for terminology for strict ordering
* Document and illustrate value-category preservation in deduction-only as_pack
* Document and verify by-value decay and multi-argument coercion in explicit as_pack
* Synchronize all code regions in TYPE_ALGEBRA.md with examples/type_algebra/main.cpp
* Document and verify the compile-time behavior of vacuous or_else recovery

Assisted-by: Gemini:gemini-3.6-flash
* Reposition code block outside of the markdown blockquote for clean synchronization
* Rewrite introductory transition sentence to flow naturally into test_vacuous_or_else()
* Keep math notes and closure principle explanations fully intact
* Upgrade sync_type_algebra_examples.py to support quoted code examples

Assisted-by: Gemini:gemini-3.6-flash
* Group all four computation carriers in Section 3 and sum/product payloads in Section 4
* Unified and document Success-Path and Failure-Path bridging in Section 3
* Illustrate multi-alternative choice to optional bridging with heterogeneous success join
* Fix stale Section references throughout TYPE_ALGEBRA.md
* Synchronize all code fences with examples/type_algebra/main.cpp

Assisted-by: Gemini:gemini-3.6-flash
@Bronek
Bronek force-pushed the bronek/draft_TYPE_ALGEBRA branch from 71a2a91 to b7c187c Compare July 24, 2026 17:40
Bronek added 9 commits July 24, 2026 19:24
Split carrier bridging by source: section 3 keeps fallible-to-fallible,
section 10 takes every bridge from an identity carrier. Drop section 6's
duplicate operator& example in favour of section 1's, and widen the shared
example-types region to the types the later fences use.

Each prose change carries a `<!-- why: ... -->` annotation recording the
mechanism and its evidence, for the author's review.

Assisted-by: Claude:claude-opus-5
Reformat examples/type_algebra at 100 columns (new .clang-format), turn
example fixtures into parameters so the quoted fences show their types,
and strengthen the assertions - including new witnesses for
as_pack<T const &> forcing a reference element and for a
reference-bearing pack stored in a copack. The disjoin example now shows
the binary error-side product rather than unary forwarding.

Prose: copack grades the value side as well as the error side; prefer
copack_for/choice_for to naming copack/choice directly; member verbs
cannot bridge carriers; sort-key collisions are a limitation of builds
without std::type_order. Correct .value() on a fallible carrier - it is
partial and throws, it does not discard the error channel.

Assisted-by: Claude:claude-opus-5
Unify the separate sync scripts into scripts/sync_md_examples.py with
individual pre-commit hook stanzas for README and TYPE_ALGEBRA. Apply
prose shorthand rule to omit fn:: prefixes on core vocabulary types in
README.md, and remove the temporal 'Now' from CONTRIBUTING.md. Use
C++20 trailing return type styles in README.md and its compiled example.

Assisted-by: Claude:gemini-1.5-pro
Remove all 61 developer-review 'why' annotations. Systematically polish
and refine the adjacent prose to adhere to standard WG21-level C++
terminology, including clarifications on compiler-specific type sorting,
empty copack constraint-selected overloads, raw reference payload
prohibitions on expected/choice/just, and singular monadic lift.

Assisted-by: Claude:gemini-1.5-pro
…EBRA.md

Highlight the symmetric dual nature of sum composition (disjunction) and
product composition (conjunction) with reversed value/error behaviors.
Clearly document that conjunction (operator&) works on both carriers and data
whereas conjoin is data-only, and disjunction (operator| & disjoin) applies
strictly to computation carriers.

Assisted-by: Claude:gemini-1.5-pro
The operator constrains itself on the other operand, which is only safe where
its own operand is deduced: as a hidden friend it could be spelled only as
Policy::type<T, E>, a non-deduced context, so deduction rejected nothing and the
constraint ran for every left operand there is. Where that operand reached this
same operator by ADL, satisfaction depended on itself - a hard error, where the
question should simply answer false. Declared at namespace scope instead, once
per carrier; its three siblings constrain on the operands' channels rather than
on the other operand, and keep their form.

Closes #381

Assisted-by: Claude:claude-opus-5
Bronek added 7 commits July 29, 2026 15:57
znai renders a directory as a chapter and a file within it as a page, so each root document is
split at its `##` boundaries into a chapter staged beside the API reference. Staging keeps the
split in the build tree, leaving the documents whole in git and `docs/` hand-written throughout.

Section numbering reaches the site as neither a title nor a dangling reference: a page carries
the name alone, and the prose's `Section N` becomes a link to the page it names. Repo-relative
links, which znai resolves as page references and rejects, become links to the sibling chapter
or to the file on GitHub.

The terminology table loses the backticks around its header cell because znai reads such a cell
as plain text and dereferences null otherwise, in 1.73 and 1.91 alike.

Assisted-by: Claude:claude-opus-5
docs/index.md restated README's introduction in a second, hand-maintained copy, and the two had
already drifted: its How was a condensed form of README's, and its What a different framing of
the same facts. README is now the front page itself, whole — splitting it would scatter an
introduction written to be read in one go.

The only claim the old page held alone, that the functor system is extensible, moves to README,
where the section on what lies beyond the example already gathers capabilities of that kind.

Assisted-by: Claude:claude-opus-5
Each root document becomes a section named after the file it came from, so a reader can tell what
they are reading and where to find it: README, TYPE ALGEBRA, CONTRIBUTING, the hand-written
chapters, CONTINUOUS INTEGRATION, and LICENSE last. Only TYPE_ALGEBRA is long enough to navigate
better as pages; the rest stay whole. README titles its page by joining its heading to the tagline
below it, so the site says "libfn - Functional programming in C++" without the script knowing it.
The site keeps no index.md of its own — znai generates one redirecting the root to the first page.

Badges are dropped on the way in. They belong on the repository page, and one of them stood as the
whole of the License section, which now names the licence in words.

Two repairs are made to the deployed site, neither having anywhere earlier to go. znai builds
internal URLs as `/<doc-id>/<chapter>/<page>`, and this site is deployed at the root of its domain
— so the doc id is empty and every link arrives as `//chapter/page`, which a browser resolves as a
host. The navigation escapes this by never following its own hrefs; a link written in a page does
not. `View on GitHub` was one link for the whole site, and a broken one: `/docs` without GitHub's
`blob/main`. Each page now carries the path it was rendered from.

Both repairs fail the build if they find nothing to do, so a znai release that fixes either will
say so rather than leave the workaround standing.

Assisted-by: Claude:claude-opus-5
The reference was organised by carrier, so a verb was documented once per carrier it applies to -
byte-identical in seven cases of eleven, and differing only in which example it quoted in three
more. A verb is one entity: it now has one page, carrying every carrier's examples, and the page
names its category so the flat list reads as the grouping the chapters used to give.

Only `transform_error` differed in substance: the operation is rejected on `optional`, which the
optional page said and the expected page did not. The merged page says it.

The concepts and the utilities reach the site for the first time, as does `fn::functor`, the
extension point behind the pipeline. A concept renders as its description alone, doxygen giving
znai no signature to show, so each carries a heading naming it - without which the page is forty
unattributed paragraphs.

Assisted-by: Claude:claude-opus-5
The section listed seven of the fifteen workflows, pointed at image definitions under paths that
no longer exist, and named a registry the images left. It now groups the workflows by what they
prove — that the library works, that it can be consumed, and that the containers the rest run in
are current — and covers the packaging routes and the image tagging that were missing entirely.

It also records why coverage and analysis each come in two workflows, which the YAML shows but
does not explain: a pull request from a fork is given no credentials, so the build uploads an
artifact and a second workflow reports from the base repository. Setting a fork up to report
against your own accounts stays in CONTRIBUTING, which this links to rather than restates.

Assisted-by: Claude:claude-opus-5
@Bronek

Bronek commented Jul 29, 2026

Copy link
Copy Markdown
Member Author

augment review

@augmentcode augmentcode Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review completed. 2 suggestions posted.

Fix All in Augment

Comment augment review to trigger a new review at any time.

Comment thread scripts/sync_md_examples.py
Comment thread scripts/stage_docs_source.py Outdated
Bronek added 15 commits July 30, 2026 16:28
znai renders a member's signature per overload but resolves the description
once, by name, so every documentation stanza after the first on a name was
dropped - pack::append's second stanza reached no page at all. Selecting an
overload with args: gives each its own description, and the three plugins
that need it all honour the option.

The signature listing is written out in the page because znai cannot draw
one: its doxygen node carries no ref-qualifier, so a member's four value
categories render as four identical entries. doxygen_signatures.py holds
every listing to the headers, and the docs build fails when they drift.

conjoin and make each had overloads separated only by a requires-clause,
which znai cannot select between; their stanzas are merged.

Assisted-by: Claude:claude-opus-5
The carriers' own members were documented in the headers and rendered
nowhere: expected::and_then, optional::or_else, copack::emplace and 45
others. Each now has a section on its carrier's page, so a reader looking
up a carrier finds its whole surface. Where a specialisation declares a
member the primary also declares, its listing joins that section rather
than opening a second one of the same name.

Three renderer corrections came with them. A constructor has no return
type, so it is no longer drawn with one; a SFINAE-friendly trailing
decltype over the library's internals names what a reader cannot, so it
is left to the prose, as cppreference does with the same declarations;
and a parameter type doxygen links is spelled by znai with a space after
the link, which a selector must match.

choice, copack and just each documented the implicit and explicit arms of
a constructor pair separately, which znai cannot select between; their
stanzas are merged.

Assisted-by: Claude:claude-opus-5
expected<void, Err> and optional<T &> carried no documentation at all,
while their primary templates were documented throughout - so a reader
reaching either specialization found bare signatures. The words are not
the primary's: over a void value side the callable is invoked with no
arguments and apply_type's value arm receives std::in_place alone, and a
reference optional has one overload of each member rather than a cv/ref
set, hands the referent over as a plain T&, and has no value side to
grade, so or_else must return that very type.

disjoin_t::operator() was undocumented in both arities, its meaning
carried only by the type's own stanza; it now mirrors conjoin's.

Assisted-by: Claude:claude-opus-5
Member types, constructors, assignment, swap and the comparisons carried
no documentation at all, so the reference showed each carrier's verbs
without ever showing how one is made. Each now has a stanza - one line,
naming the member rather than explaining it, because a copy constructor
copies - and the pages carry them grouped above the verbs: what the type
is and how it is built, before what it does.

Most of these were not merely undocumented but invisible. doxygen parses
a C++20 conditional-explicit constructor as a member named `explicit`,
so every such constructor was absent from the XML entirely; expanding
`explicit(x)` to `explicit` recovers all of them.

One znai limit is now reported rather than worked around: it normalizes
a query with `,\s+` -> `,` but leaves the member's own spelling alone, so
a parameter type carrying a comma inside its template arguments -
expected<U, G> const & - can be listed but never selected for. Four
converting constructors lose their description to this; the checker
names them, and says so as a note rather than failing a build no edit
here could make pass.

Assisted-by: Claude:claude-opus-5
The vocabulary landed in two passes and so landed in two places: just
listed its constructors both under Construction and under a section of
their own, and copack did the same for its constructors, has_type and
operator=. Every listing of a kind now sits in one section, whichever
of the page's compounds declares it.

The sections run member types, construction, destruction, emplace, the
remaining special functions, then the verbs - what the type is and how
it is built before what it does. choice's deduction guides are named as
such rather than sharing a heading with its constructors, and the prose
that described the constructors moves with them.

Assisted-by: Claude:claude-opus-5
A reader writes fn::transform, not fn::transform_t, and the object
carried nothing while its type carried everything. Each of the thirteen
now says what a pipeline does with it - the object is the trailing
declarator of its own type's definition, so the description is doxygen's
trailing form - and each verb's page lists it.

Variables were being rendered as calls: `static auto size -> std::size_t`
is not a declaration anyone could write. A variable is now spelled as
its type and name, with the initializer that is the whole point of a
constant like `size`, and without the elaborated `struct fn::` doxygen
puts in front of a verb object's own type.

Assisted-by: Claude:claude-opus-5
optional's twenty-three free operators and swap, and expected's
comparison against a value, carried nothing. Each now says what it
answers, and a page gathers them: the whole set for a carrier in one
place, which is how a reader asks whether an empty operand compares at
all. It carries no "Defined in" badge on purpose - these are declared
beside the carrier they serve, across five headers, and one badge would
be wrong for four of them.

Placement went by matching each declaration's own text, not by doxygen's
line numbers: doxygen reports the same source line for operator>= and
operator<=>, which an earlier line-keyed attempt turned into malformed
comments attached to the wrong overloads.

fn::operator== against a value on an expected is listed without its
description - `expected<T, Err> const &` carries a comma inside its
template arguments, which znai's selector normalization cannot name.

Assisted-by: Claude:claude-opus-5
The conjunction and disjunction are the algebra; conjoin and disjoin are
only their n-ary folds, and those had pages while the operators had
none. Each fold page now carries its operator's whole dispatch surface -
nineteen arms for &, eight for | - above the fold that builds on it.

The description had to move to reach the site. All the carrier arms
share the parameter signature (Lh &&, Rh &&), and the reference renders
one description per signature, taking the first arm doxygen reports:
choice.hpp's, which carried none, so the expected and optional stanzas
described overloads nobody could reach. One merged description now sits
on that first arm and covers every carrier; the two it replaces stay in
their own headers as ordinary comments. pack's data conjunction keeps
its own stanza - taking (auto &&, auto &&), it is separately selectable.

Assisted-by: Claude:claude-opus-5
pfn is a polyfill: sixty of its members were undocumented because they
are std::expected's and std::optional's members, and a second copy of
that specification would only be a second thing to keep true. Each type
now says so and the page links where they are specified, so the silence
reads as a decision rather than an omission.

The links live in the page, not the stanzas. doxygen turns a bare URL
into a <ulink>, which znai drops - the address survived as text a reader
would have to copy out by hand - while an ordinary markdown link in the
page renders as a link.

Assisted-by: Claude:claude-opus-5
The concepts page claimed one header for all forty of its concepts.
Twenty-eight are declared elsewhere - with the feature they constrain,
across nineteen headers - so a reader including what the page said got
neither some_monadic_type nor most of the rest. Each concept now names
its own header, and the page says why they are not gathered.

fn::choice had no description at all: the page asked for the compound
`fn::choice`, which is the incomplete declaration and carries nothing,
where the documented type is the constrained partial specialization.
znai renders a compound that does not exist as silence, so the page had
been arriving without its opening paragraph unnoticed.

Assisted-by: Claude:claude-opus-5
znai renders a description it cannot find as silence, so documentation
has gone missing here three separate ways and each time the build stayed
green: forty-eight carrier members documented and named by no page,
fn::choice asking for a compound that carries nothing, and operator&
rendering the first overload's description when the documented overload
was not first. check_docs_coverage.py fails on all three.

Seventeen entities that were documented and unreachable are published
with it - the free lifts and as_pack, fn::get, the applicability traits
of both namespaces, and value_or's call operator, the one verb whose
operator() no page carried. The ten that remain are the verbs' dispatch
objects, listed in coverage-exemptions.txt with the reason; an entry
that stops being needed is reported too, so the file cannot rot.

Assisted-by: Claude:claude-opus-5
Assisted-by: Claude:claude-fable-5
* Remove conversational filler, "AI slop" tells, and announcing phrases
* Simplify explanations and lists across all sections to make them active and direct
* Replace grand mechanism names (such as "the graded gateway") with clear, straightforward terms ("the entry point")
* Explicitly classify libfn's own pack and copack as raw passive data layouts alongside std::tuple and std::variant
* Improve opening paragraph wording to "as operations are chained into expressions"
* Rigorously preserve all factual C++ mechanics, mathematical notes, and the ordering rule

Assisted-by: Gemini:gemini-3.6-flash
An example region that no fence quotes now fails the sync instead of
warning, and staging keeps a bare image line - only a linked image is
a badge. Both flagged by review on #359.

Assisted-by: Claude:claude-fable-5
@Bronek

Bronek commented Aug 1, 2026

Copy link
Copy Markdown
Member Author

augment review

@augmentcode

augmentcode Bot commented Aug 1, 2026

Copy link
Copy Markdown

This pull request is abnormally large and would use a significant amount of tokens to review. If you still wish to review it, comment "augment review" and we will review it.

@augmentcode

augmentcode Bot commented Aug 1, 2026

Copy link
Copy Markdown

augment review

…nsistency

* Sync stale fn::operator& in conjoin.md and fn::operator| in disjoin.md with C++ headers
* Add Doxygen specialization/inheritance artifacts for void and reference types to docs/coverage-exemptions.txt to satisfy docs_check_coverage

Assisted-by: Gemini:gemini-3.6-flash
@Bronek
Bronek merged commit 50bbd39 into main Aug 1, 2026
62 of 63 checks passed
@sonarqubecloud

sonarqubecloud Bot commented Aug 1, 2026

Copy link
Copy Markdown

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