Date: Fri, 30 Oct 2026 20:04:37 +0100
Hi,
std::optional and std::expected have monadic operations `transform` and
`and_then` which
basically short-circuit as soon as the unhappy case (nullopt/unexpected) is
reached.
My proposal is to add sender adapters with comparable semantics in the
async std::execution world (all names of course being preliminary):
* std::execution::nullopt_as_stopped turns a set_value of nullopt (either
via nullopt_t, or any optional<T> type into `set_stopped`.
* std::execution::unexepected_as_error turns set_value(std::unexpected(t))
into set_error(t)
* optional, std::execution::nullopt_as_error(specifyableErrorValue)
(Semantics should be clear from the name, analogous to the above).
* std::execution::unexpected_as_stopped is possibly a bad idea, as it
encourages to silently drop errors, but would complete this set.
Motivation: Make it easier to put chains of optional/expected function into
the async world, e.g.
int x = ...
auto s = ex:just(x)
| ex:then([](int x) {return x > 3 ? std::nullopt : std::optional{x *
2};})
| ex::nullopt_as_stopped
| ...
I anticipate a typical way of using will be to implement a shortcut for `
then(f) | nullopt_as_stopped` etc. for convience when many such operations
are chained. Whether it is beneficial to implement these as dedicated
adapters for performance reasons is yet to be explored.
Note: To my understanding, only the " inverse" operations are currently
standardized (stopped_as_optional, stopped_as_error, other custom
transformations from stopped/error channel can be implemented using
upon_stopped and upon_error), but the direction I am proposing is not yet
part of the standard nor of a paper I could find.
I am looking forward to your feedback, thanks for your time.
Best regards
Johannes
std::optional and std::expected have monadic operations `transform` and
`and_then` which
basically short-circuit as soon as the unhappy case (nullopt/unexpected) is
reached.
My proposal is to add sender adapters with comparable semantics in the
async std::execution world (all names of course being preliminary):
* std::execution::nullopt_as_stopped turns a set_value of nullopt (either
via nullopt_t, or any optional<T> type into `set_stopped`.
* std::execution::unexepected_as_error turns set_value(std::unexpected(t))
into set_error(t)
* optional, std::execution::nullopt_as_error(specifyableErrorValue)
(Semantics should be clear from the name, analogous to the above).
* std::execution::unexpected_as_stopped is possibly a bad idea, as it
encourages to silently drop errors, but would complete this set.
Motivation: Make it easier to put chains of optional/expected function into
the async world, e.g.
int x = ...
auto s = ex:just(x)
| ex:then([](int x) {return x > 3 ? std::nullopt : std::optional{x *
2};})
| ex::nullopt_as_stopped
| ...
I anticipate a typical way of using will be to implement a shortcut for `
then(f) | nullopt_as_stopped` etc. for convience when many such operations
are chained. Whether it is beneficial to implement these as dedicated
adapters for performance reasons is yet to be explored.
Note: To my understanding, only the " inverse" operations are currently
standardized (stopped_as_optional, stopped_as_error, other custom
transformations from stopped/error channel can be implemented using
upon_stopped and upon_error), but the direction I am proposing is not yet
part of the standard nor of a paper I could find.
I am looking forward to your feedback, thanks for your time.
Best regards
Johannes
Received on 2026-10-30 19:04:54
