This proposal in isolation is purely about a function being able to return multiple times and with multiple different types, and the call site offering behaviours for each. In precisely the same way that a function can be generated by a call to a template function, the return path could be generated by a yield from a function such as I propose. The idea is simply to provide what callback structs provide with a more intuitive interface.

The sender/receiver would simply be an application of this. A sender has a list of defined types to transmit, and a receiver has a list of defined types to receive (this can be figured out by the compiler). The current library implementations of this concept are inelegant compared with a language approach, and come with more bootstrap than you can shake a stick at. They also tend to focus on the async side of things, which isn't my intention.



On Wed, 5 Feb 2020, 13:19 Gašper Ažman via Std-Proposals, <std-proposals@lists.isocpp.org> wrote:
Jake,

Seems to me like doing what you describe well is exactly what the executors' sender/receiver model is built for. Look at libunifex for an example.

On Wed, Feb 5, 2020 at 12:27 AM Garrett May via Std-Proposals <std-proposals@lists.isocpp.org> wrote:
On Tue, 4 Feb 2020, 11:27 am Jake Arkinstall via Std-Proposals, <std-proposals@lists.isocpp.org> wrote:
This proposal will likely also include a mechanism for switching on types. I'm also thinking of using template/concept syntax in place of var.

I don't believe that template/concept syntax would be appropriate here unless changes were made to what templates/concepts can do. That's because what we have here is return types that are not dependent on templates, nor on concepts, but instead on branches.

Because this would be designed to be "switching on types", perhaps the following would be appropriate syntax:

switch(auto) halve(unsigned int i){
    if(i % 2 == 0){
        // branch for unsigned int
        return i / 2;
    } else{
        // branch for double
        return i * 0.5;
    }
}

And probably for consistency, a switch(decltype(auto)) would be available.

As for the yielding concept, this seems like a second proposal, because its basis is mainly on the ability to yield (a lazy idea) rather than "switching on types".

This is just my two pence. It seems along the same lines as more recent proposals, such as expansion statements (template for).

On Tue, 4 Feb 2020, 4:18 pm Jake Arkinstall via Std-Proposals, <std-proposals@lists.isocpp.org> wrote:
The '14 version you have provided is exactly what my proposal generates, but without the indirection. Much of my code involves exactly this pattern, stitched together with a pipeline builder struct, cringy use of the >> operator and helper macros for yield, etc. It suffices, but it's crying out for either a new language, a preprocessor, or a new language feature.

You'll also note that debugging through the callback approach is nothing less than horrific, especially if any pipeline building code is involved (just like with variants). I can't imagine this changing while the code being written is in the form of callbacks.

The reason is that given N components of the pipeline (each yielding a new value to be passed to the next), an unhandled type from component N-1 to component N bubbles an error generating from the first call to component 0. Deciphering the message that comes out is fun, to say the least, and while static_assert is unable to add typenames etc there's no real way around it.

As a language feature, it's easy. The compiler is aware of what you're trying to do, and can provide useful information when you get it wrong.

On Tue, 4 Feb 2020, 15:16 Arthur O'Dwyer via Std-Proposals, <std-proposals@lists.isocpp.org> wrote:
On Tue, Feb 4, 2020 at 8:16 AM Михаил Найденов via Std-Proposals <std-proposals@lists.isocpp.org> wrote:
This definitely steps into both "language variant" and Pattern Matching territory. 
I am not sure what optimization we can expect from "language variant", but PM should give us close to your ideal code

void main(int argc, char const* const* argv){
    inspect(halve(args))
      Numeric x => std::cout << "You passed 2*(" << x << ") arguments\n";
    return 0;
}

The current Pattern Matching proposal doesn't give the programmer any way to write `halve` so that it can actually return either of two alternative types (`unsigned` or `double`) depending on a runtime condition (the evenness of the runtime argument).

However, I don't see anything wrong with the existing C++14 language solution to OP's problem. It's not worth pursuing any crazy core-language gymnastics unless you can provide a use-case that isn't already solvable idiomatically in C++14 (17, 20). Here's the '14 solution:

template<class F>
void halve(unsigned i, const F& f) {
    if (i % 2 == 0) {
        f(i / 2);
    } else {
        f(i * 0.5);
    }
}

int main(int argc, char**) {
    halve(argc, [](auto x) {
        std::cout << "You passed 2*(" << x << ") arguments\n";
    });
    return 0;
}

Short, readable, relatively easy to understand. Certainly easier to understand than anything proposed in the OP.

–Arthur
--
Std-Proposals mailing list
Std-Proposals@lists.isocpp.org
https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals
--
Std-Proposals mailing list
Std-Proposals@lists.isocpp.org
https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals
--
Std-Proposals mailing list
Std-Proposals@lists.isocpp.org
https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals
--
Std-Proposals mailing list
Std-Proposals@lists.isocpp.org
https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals