Grading an provides exactly bounded error sets When an outer computation holds a coproduct of successful values and each value requires a different operation to proceed derives a single normalized shape Consider a configuration reader that parses a loosely typed file into specific valid structural alternatives or Two independent joins occur during The successful branch values formed the normalized value copack The existing outer errors and the new branch errors formed the normalized error copack Unioning allows different grades of to share the same carrier family Although ungraded requires the identical error type or its singular lift to participate in monadic bindmeaning and are not any two graded types are regardless of how their error sets differ as the compiler can always derive their union Value joining and error grading are independent branch values can join while the error side stays plain as in During sequential composition derives the promoted type from the you supply In success binding a plain error type is promoted to if the returning error type of the callback is In recovery error binding a plain success type is promoted to if the returning success type of the callback is An un graded computation thus enters a graded pipeline without manual lifting If you need to perform this promotion explicitly on the carrier itself before entering a composition provides direct member helpers on explicitly lifts the error transforming to on explicitly lifts the success value transforming to on symmetrically lifts the value transforming to These helper methods provide a compact explicit alternative to the pipeline promotions Recovery via behaves symmetrically It handles input error alternatives and joins any new errors produced by the recovery branches while preserving the successful value path Heterogeneous recovery values require a suitable copack valued input Any original error handled by a branch is removed from the resulting grade unless a branch explicitly re returns it When there are no error alternatives to handlesuch as in there is nothing to recover from the callback is neither invoked nor instantiated Widening is subeffecting In accordance with the subeffecting principles of graded monads Why compose types as well as values a narrow error set can be widened during composition but narrowing requires explicit mitigation Implicit narrowing without handling the removed errors is unsafe and rejected by the compiler However you can safely narrow or collapse an error grade by explicitly handling and mapping the errors using Because on a graded forces exhaustive matching you can map diverse error types into one common error type collapsing the grade to a singular or into a narrower safely reducing the static error grade The bottom error grade is This computation cannot fail but it is algebraically prepared to widen if later composition introduces possible errors A concrete example of this is aliased as in the library Because represents the unit and represents the zero this type maps algebraically to Having a cardinality of exactly one it has no possible errors can never fail and can only succeed with a single empty trigger This makes it structurally isomorphic to the unit type In practice acts as the entry point for graded pipelines Initiating a chain with this unit trigger opts subsequent bindings into graded error set unioning without requiring mock starting errors or manual wrapping Because its starting error set is empty unioning it with subsequent steps errors such as yields exactly those errors An alternative unit type without an error channel is The identity cluster TIP Mathematical note the graded functor and its two units Having established the error pomonoid in Why compose types as well as values s graded is the lax monoidal functor from the pomonoid category to the endofunctor category on C types with following Orchard Wadler and Eades Unifying graded and parameterised monads Its unit is eta_A A to G_I A qquad I emptyset Two different units meet in the gateway type the neutral grade of the error pomonoid and the unit object of the value category spelled It is precisely expected libfn expected MaximumSize FilePath BlockSize auto read_config > fn expected<fn copack_for<MaximumSize FilePath BlockSize> fn copack_for<BadSyntax UnknownKey>> auto config_pipeline > void auto validated read_config | fn and_then fn overload MaximumSize v return fn expected<MaximumSize fn copack<OutOfRange>> v FilePath v return fn expected<FilePath fn copack<Missing>> v BlockSize v return fn expected<BlockSize fn copack<OutOfRange>> v The result exactly bounds both the successful paths and the error paths static_assert std same_as<decltype validated fn expected<fn copack_for<BlockSize FilePath MaximumSize> fn copack_for<BadSyntax Missing OutOfRange UnknownKey>>> and_then BadSyntax UnknownKey OutOfRange Missing expected expected<T E> E copack<E> expected<int IoError> expected<User Missing> same_kind expected same_kind static_assert fn same_kind<fn expected<int fn copack<IoError>> fn expected<User fn copack<Missing>>> expected<copack_for<A B> E> libfn copack and_then E copack<E> copack<E> or_else T copack<T> copack<T> libfn copack_error expected expected<T E> expected<T copack<E>> copack_value expected expected<T E> expected<copack<T> E> copack_value optional optional<T> optional<copack<T>> auto test_explicit_lifting fn expected<User IoError> result fn optional<User> opt > void Explicitly lift the error side of expected auto graded_err std move result copack_error static_assert std same_as<decltype graded_err fn expected<User fn copack<IoError>>> Explicitly lift the value side of expected auto graded_val std move result copack_value static_assert std same_as<decltype graded_val fn expected<fn copack<User> IoError>> Explicitly lift the value side of optional auto graded_opt std move opt copack_value static_assert std same_as<decltype graded_opt fn optional<fn copack<User>>> or_else expected<T copack<>> transform_error transform_error expected copack copack copack<> template <typename T> using cannot_fail_t fn expected<T fn copack<>> expected<void copack<>> fn expected_unit void 1 copack<> 0 void expected<void copack<>> and_then copack<> IoError just<void> libfn expected expected<void copack<>> void