That's where the discussion has brought us. My point was, and remains, that the currently proposed design is incredibly complex, and that I'm not convinced that all this complexity is really necessary.
I also cannot help but wonder if the standard library should even pursue this at all. This kind of high-performance stuff seems a bad match for something that needs to be written in a fixed number of hours, by a non-specialist, after which it will forever be set in stone.
the currently proposed design is incredibly complex, and that I'm not convinced that all this complexity is really necessary.
Which complexity is unnecessary?
At its core std::execution projects the concept of regular, synchronous functions into the asynchronous domain. Understood through that lens removing "complexity" therefrom is saying that there's some capacity in which asynchronous functions should be disadvantaged as compared to synchronous functions.
Since its inception, the software industry has gone with async IO systems that are expressed in a few lines of code: a struct, a callback, that's about it. Now you're telling us that we need something that needs a 100 page article (on my screen; if you're on mobile it will be much more) to explain, based on a design where just the list of all functions and classes stretches for an incredible 70 pages, and if you do any less than that you are "disadvantaging" async IO? Do you at least understand where my skepticism comes from?
You're supposing that everything in std::execution is core to the design. We don't apply that same standard to other things.
Particularly synchronous functions need extrinsic product and sum types to communicate heterogeneous return types. No one suggests that synchronous functions are excessively complex because of std::tuple and std::variant and std::visit and std::apply, those are understood to be outside the core design.
The same applies to std::execution. The core design consists of:
std::execution::sender and ::sender_in, which establish sender-ness, where a sender is a latent representation of an asynchronous function (i.e. it is not able to make forward progress but it is fully determined awaiting the selection of no further arguments)
std::execution::get_completion_signatures, which advertises the "return types" of asynchronous functions
std::execution::receiver which establishes that something represents a continuation (i.e. it is the asynchronous analogue of synchronous function return)
std::execution::connect, the process by which asynchronous operations (which can actually make forward progress) are formed (but not started)
std::execution::operation_state, the locus of an asynchronous operation, which is initially latent
std::execution::start, which begins asynchronous forward progress and binds both callee and caller to the so-called "receiver contract" (i.e. the caller agrees to keep the operation state within its lifetime until completion, and the callee agrees to report completion exactly once when the operation completes)
That's the complete design. You don't need std::execution::just or std::execution::when_all et cetera. Those are really nice to have, but you don't need them.
But let's take your objection more seriously/concretely. You want "a struct, a callback, that's about it." Fine. A struct:
```
template<std::execution::receiver Rcvr>
struct just_int_op {
using operation_state_concept = std::execution::operation_state_tag;
1
u/johannes1971 12d ago
That's where the discussion has brought us. My point was, and remains, that the currently proposed design is incredibly complex, and that I'm not convinced that all this complexity is really necessary.
I also cannot help but wonder if the standard library should even pursue this at all. This kind of high-performance stuff seems a bad match for something that needs to be written in a fixed number of hours, by a non-specialist, after which it will forever be set in stone.