Skip to content

Give the singular copack the tuple protocol #416

Description

@Bronek

Give a copack with exactly one alternative the full tuple protocol, complementing the existing index-less get(cp):

  • std::tuple_size (= 1)
  • std::tuple_element
  • ADL get<0> in all cv/ref-qualified forms
  • Structured bindings

The protocol remains restricted to singular copacks:

  • A multi-alternative copack is unchanged. Its active alternative is a runtime property, so tuple_size and tuple_element have no static answer—the same reason the index-less get is already singular-only.
  • copack<> is explicitly refused. Setting tuple_size == 0 would make the uninhabited zero resemble the unit pack<>, creating exactly the zero/unit conflation that the algebra separates.

Motivation: Generic Interoperability

A singular copack becomes usable wherever protocol-generic code accepts a one-element tuple. The state-shape isomorphism singular copack ≅ one-element pack ≅ payload becomes exercisable in generic code, while every nominal distinction remains intact.

The protocol adds no type identity: copack<X>, pack<X>, and X remain distinct types; normalization and deduplication are untouched; and copack_for<A, pack<A>, std::tuple<A>, std::array<A,1>>::size remains 4. The algebra is not weakened.

Doctrine: Storage and Call Views May Differ in Arity

This difference is intentional. For copack<pack<A,B>>, tuple_size is 1 and get<0> yields the whole pack, while apply eliminates at arity 2. The two protocols answer different questions:

  • get<0> asks: Which alternative is stored?
  • apply asks: What argument list does it make?

This is the converse of the existing "Storage shape vs call shape" principle: different types, same call shape. It should be added to that section.

This is not the rejected nested-pack incoherence. In that case, a canonical flat duplicate of the same product existed, making the disagreement gratuitous. Here, no flat alternative spelling exists: pack<A,B> alone denotes a product, not a sum of one. Moreover, the storage view is genuinely inexpressible through apply, because the arity-1 whole-pack call does not compile.

No Enrolment in apply's Tuple-Like Arm

By declared doctrine, the unpacked domain of pfn::apply and fn::apply is the enumerative standard set: array, complex, pair, tuple, and subrange. Acquiring the tuple protocol therefore does not divert fn::apply; the sum arm continues to win. pfn::apply continues to refuse copacks, preserving fnpfn.

Implementation Rider

Verify whether C++26 std::apply is constrained by the tuple-like concept or specified through tuple_size and get. If the latter, std::apply would accept a singular copack through its storage view while pfn::apply refuses it. That raises a pfn fidelity question to settle, separately if necessary.

Documentation

Update TYPE_ALGEBRA.md §4 on singular extraction, the "Storage shape vs call shape" section, and add a dated CHANGELOG.md entry.

Assisted-by: Claude:claude-fable-5

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestrelease-0.2Planned for release 0.2

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions