represents a computation that always succeeds by selecting one of several alternatives Structurally it serves as the single layer carrier for coproduct states avoiding the invalid nested representation The computation carriers Promotion via Pipeline Functors Pipeline on a that returns a promotes automatically to Similarly pipeline on a can return a or directly Inside its domain behaves differently from bare is self flattening data a returned from a branch dissolves into the result is an atomic never failing computation a returned from a branch remains an alternative unless joins it away Consider a scenario where different branches of a switch return different types A callback returning a bare value requires rather than s rejects bare value returns with a compile time diagnostic TIP Mathematical note why copack is not a monad but choice is Categorically is an object level coproduct disjoint sum whereas is a monad representing a coproduct bearing computation context is self flattening not a monad Naked sums are naturally self flattening e g and enforces it syntactically a nested is ill formed by design But the multiplication presupposes that is expressible as a type so join has no domain to act on The constraint is intentional it allows on to collapse every branch s results into a flat deduplicated set instead of an ever nesting type is the monad the structural suspend button To restore monad laws the monadic carrier wraps the sum in an identity layer to preserve structural depth That layer holds eager flattening in check Thus acts as a lawful monad under over coproduct objects the nominal identity wrapper which Haskell spells as the newtype Because yet is a distinct C type the unit and the multiplication are exactly the wrapping and unwrapping that nominal typing makes observable Unit return wraps the coproduct in the layer Building a from a single alternative composes this with the coproduct s own injection Join flatten strips one layer letting the underlying sum deduplicate its alternatives the codiagonal fold executed statically via Bind maps then flattens explicitly via join Making that step explicit is what grants control over when flattening occurs choice just<copack<Ts >> fn transform just copack choice auto test_identity_transformation fn just<UserId> j > void Transforming a just with a callable returning a copack produces a choice auto mapped j | fn transform UserId return fn copack_for<Missing FilePath> Missing static_assert std same_as<decltype mapped fn choice_for<FilePath Missing>> fn and_then just choice expected<T copack<>> choice copack copack copack choice choice and_then choice auto test_choice_mapping fn choice<User UserId> ch > void constexpr auto mapper fn overload UserId return fn choice<Missing> Missing User return fn choice<FilePath> FilePath transform nests the returned choice as a mapped value auto mapped ch | fn transform mapper static_assert std same_as<decltype mapped fn choice_for<fn choice<FilePath> fn choice<Missing>>> and_then joins and flattens them into a normalized superset choice auto bound ch | fn and_then mapper static_assert std same_as<decltype bound fn choice_for<FilePath Missing>> transform and_then choice and_then copack<Ts > choice<Ts > copack libfn copack transform copack choice choice<Ts > choice<choice<T>> choice<T> choice Identity choice choice choice choice_for