Proposal: Monadic operations for std::optional

342 views
Skip to first unread message

Simon Brand

unread,
Oct 3, 2017, 11:09:48 AM10/3/17
to std-pr...@isocpp.org
Hi everyone,

I've written a proposal to add map and bind member functions to
std::optional. The idea is to allow the writing of clear and concise
code in the presence of functions which may not produce a result.For
example, instead of writing this:

std::optional<int> foo() {
auto ra = a();
    if (!a) return std::nullopt;

    auto rb = b(*a);
    if (!b) return std::nullopt;

    auto rc = c(*b);
    if (!c) return std::nullopt;

    auto rd = d(*c);
    if (!d) return std::nullopt;

    auto re = e(*d);
    return re;
}

You could write this:

std::optional<int> foo() {
    return
      a().bind(b)
         .bind(c)
         .bind(d)
         .bind(e);
}

I'd appreciate any feedback on the proposal as written and the utility
of this idea. You can find it here:
https://github.com/TartanLlama/monadic-optional-proposal

Thanks!
Simon


Nicol Bolas

unread,
Oct 3, 2017, 11:27:39 AM10/3/17
to ISO C++ Standard - Future Proposals, tarta...@gmail.com
On Tuesday, October 3, 2017 at 11:09:48 AM UTC-4, Simon Brand wrote:
Hi everyone,

I've written a proposal to add map and bind member functions to
std::optional. The idea is to allow the writing of clear and concise
code in the presence of functions which may not produce a result.For
example, instead of writing this:

std::optional<int> foo() {
auto ra = a();
     if (!a) return std::nullopt;

     auto rb = b(*a);
     if (!b) return std::nullopt;

     auto rc = c(*b);
     if (!c) return std::nullopt;

     auto rd = d(*c);
     if (!d) return std::nullopt;

     auto re = e(*d);
     return re;
}

You could write this:

std::optional<int> foo() {
     return
       a().bind(b)
          .bind(c)
          .bind(d)
          .bind(e);
}

Reading the latter requires some understanding of what this "monadic bind" thing is doing. Reading the former requires only understanding of C++. Without a functional background, the name "bind" makes absolutely no sense. What does it mean to "bind" a function to an `optional`? What kind of association are you making?

Note that the above code also requires the identifiers `b`, `c`, `d`, and `e` to all be non-member function names that takes only one parameter and have no overloads. If you need to use "bind" with a member function, you have to use a lambda (so that it can have a `this`). If you need to use "bind" with a specific function in an overload set, you need a lambda wrapper.

Ville Voutilainen

unread,
Oct 3, 2017, 11:40:55 AM10/3/17
to ISO C++ Standard - Future Proposals
The code merely requires that the function object passed can be
std::invoked with
a monad yielding another monad.

Ray Hamel

unread,
Oct 3, 2017, 12:13:06 PM10/3/17
to ISO C++ Standard - Future Proposals, tarta...@gmail.com
This would be useful, but I'd agree that "bind," although it is consistent with
`std::bind()`, is not very clear to people who aren't familiar with FP. I think
"chain" or "try" would be clearest. (It could have multiple names, of course).

If it were added to `std::optional` it should probably also be added to all the
standard types with an `operator bool()`.

-Ray


On Tuesday, October 3, 2017 at 11:09:48 AM UTC-4, Simon Brand wrote:

Jeffrey Yasskin

unread,
Oct 3, 2017, 12:24:17 PM10/3/17
to std-pr...@isocpp.org
FWIW, I'd be more interested in a proposal to help std::optional take advantage of the monadic properties of the coroutines language feature.


Simon


--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/f82efc4a-eabd-1580-c0ce-7ada7f21c164%40gmail.com.

Nicol Bolas

unread,
Oct 3, 2017, 4:28:21 PM10/3/17
to ISO C++ Standard - Future Proposals
On Tuesday, October 3, 2017 at 12:24:17 PM UTC-4, Jeffrey Yasskin wrote:
FWIW, I'd be more interested in a proposal to help std::optional take advantage of the monadic properties of the coroutines language feature.

Is that really a thing we want to encourage? Ignoring that "awaiting" on something that isn't even potentially asynchronous is semantic nonsense, putting `co_await` into function causes a number of effects. Non-trivial effects.

You can no longer use `return`, for example; you must use `co_return`. So you can't just stick `co_await` anywhere and expect it to work.

Also, they inhibit guaranteed elision of return prvalues. Coroutines that return prvalues are really returning a copy/move from an object stored in the promise. And that requires that it be initialized, and therefore must be distinct from the caller's object.

If we really want a feature like this, then we should have one explicitly designed for this purpose. Not one that accidentally serves it, which has a bunch of unpleasant side-effects.

Nicol Bolas

unread,
Oct 3, 2017, 4:29:19 PM10/3/17
to ISO C++ Standard - Future Proposals
Sure, but an overloaded function cannot be passed without explicitly specifying the specific signature you want at the site where the name is used. So instead of `d`, you have to do `(some_signature*)&d`.

Similarly, a member function name cannot be passed as an argument, even if the function takes a compatible member pointer. Furthermore, being "invokable" is irrelevant, because std::invoke on a member pointer would still need a proper `this`. Which you need a functor/lambda to provide.

My overall point is that the `bind` example will only occasionally be as compact as is suggested, because passing functions in C++ is never as simple as functional programmers like to pretend that it is. Whereas the regular code example will be just as "concise" regardless of whether those names are overloads or member functions.

Richard Smith

unread,
Oct 3, 2017, 4:50:34 PM10/3/17
to std-pr...@isocpp.org
You're right. But in my view, the problem isn't the choice to use coroutines, it's the choice *in the Coroutines TS* to use terminology and mechanisms that only make sense for a subset of the use cases. That's something we can fix by generalizing and improving the mechanisms of the coroutines proposal.

Niall Douglas

unread,
Oct 3, 2017, 5:03:29 PM10/3/17
to ISO C++ Standard - Future Proposals, tarta...@gmail.com
Doesn't http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0650r0.pdf already propose this, and indeed a wider monadic framework for all C++?

Niall

Vicente J. Botet Escriba

unread,
Oct 3, 2017, 5:32:41 PM10/3/17
to std-pr...@isocpp.org
Le 03/10/2017 à 18:23, 'Jeffrey Yasskin' via ISO C++ Standard - Future Proposals a écrit :
FWIW, I'd be more interested in a proposal to help std::optional take advantage of the monadic properties of the coroutines language feature.

I believe this is orthogonal. We can have a monadic interface and a more user oriented interface close to co_await.

While we can define operator co_await for optional, expected and result<T>, these types share something in common. They are some kind of SuccessOrError and have the possibility to know if there is one of them and to extract the Success value or the Failure Value.

    auto v = try opt_expr;
   
to be translated in a much simpler and more efficient way.

In addition using co_await on a type that doesn't suspend is not natural to some of us.

Niall has started a draft paper that we are polishing.  This operator try would be customized in a similar but simpler way to co_await.

I've also a draft of SuccessOrError that I would like to finish for Albuquerque
    https://github.com/viboes/std-make/blob/master/doc/proposal/nullable/ValuedOrError.md

Here it is an example:

U h(T);

X<T,E> f();

X<U,E> g() {
    auto t =  try f();
    return h(t); 


The meaning of a try-expression is given by the following transformation

X<U,E> g() {
    auto __trier = operator try(f());
    if ( __trier.failed() )
        return __trier.failure_value();
    auto t = __trier.success_value();   
    return h(t); 



Any feedback on this papers is welcome.

Vicente

P.S. I'll contact Nial to see if he can share his paper already.



Nicol Bolas

unread,
Oct 3, 2017, 5:40:14 PM10/3/17
to ISO C++ Standard - Future Proposals
Good luck getting anyone to agree to what would amount to a page-1 rewrite of the feature.

Also, I contest the idea that the Coroutines TS "only make sense for a subset of the use cases". It works for the use cases that it's designed for. Coroutines TS is about halting the execution of a function and rescheduling it for later. What is being discussed here has nothing to do with stopping the execution with the expectation of continuing later; it's about doing a conditional return of a value.

Those aren't related to one another. While you can coerce the coroutine machinery to serve that purpose (effectively "rescheduling" the execution for "never"), this is not a use case that the system was ever intended to serve. So it's hardly a failure of the system that it does so inelegantly.
 

Vicente J. Botet Escriba

unread,
Oct 3, 2017, 5:44:10 PM10/3/17
to std-pr...@isocpp.org, Nicol Bolas
This problem is not specific to this proposal, it is a common and know problem with any algorithm expecting a function object. We have the overload set proposal [p0119r2] that would allow to pass an overloaded set if not transparently at least with some minor syntactic disambiguation.
I don't remember where we are with this proposal. There was also [p0382r0.]
We need to have this overload set in C++20 if we don't want to require some boilerplate code at the call site

Vicente

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0119r2.pdf
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0382r0.html

Richard Smith

unread,
Oct 3, 2017, 5:51:19 PM10/3/17
to std-pr...@isocpp.org
On 3 October 2017 at 14:40, Nicol Bolas <jmck...@gmail.com> wrote:
On Tuesday, October 3, 2017 at 4:50:34 PM UTC-4, Richard Smith wrote:
On 3 October 2017 at 13:28, Nicol Bolas <jmck...@gmail.com> wrote:
On Tuesday, October 3, 2017 at 12:24:17 PM UTC-4, Jeffrey Yasskin wrote:
FWIW, I'd be more interested in a proposal to help std::optional take advantage of the monadic properties of the coroutines language feature.

Is that really a thing we want to encourage? Ignoring that "awaiting" on something that isn't even potentially asynchronous is semantic nonsense, putting `co_await` into function causes a number of effects. Non-trivial effects.

You can no longer use `return`, for example; you must use `co_return`. So you can't just stick `co_await` anywhere and expect it to work.

Also, they inhibit guaranteed elision of return prvalues. Coroutines that return prvalues are really returning a copy/move from an object stored in the promise. And that requires that it be initialized, and therefore must be distinct from the caller's object.

If we really want a feature like this, then we should have one explicitly designed for this purpose. Not one that accidentally serves it, which has a bunch of unpleasant side-effects.

You're right. But in my view, the problem isn't the choice to use coroutines, it's the choice *in the Coroutines TS* to use terminology and mechanisms that only make sense for a subset of the use cases. That's something we can fix by generalizing and improving the mechanisms of the coroutines proposal.

Good luck getting anyone to agree to what would amount to a page-1 rewrite of the feature.

Also, I contest the idea that the Coroutines TS "only make sense for a subset of the use cases". It works for the use cases that it's designed for. Coroutines TS is about halting the execution of a function and rescheduling it for later. What is being discussed here has nothing to do with stopping the execution with the expectation of continuing later; it's about doing a conditional return of a value.

The Coroutines TS was not designed for the full set of coroutine use cases, which include cases where we only wish to suspend and destroy the coroutine, not suspend and later resume it. This is a degenerate, but important, special case of the general feature.

Those aren't related to one another. While you can coerce the coroutine machinery to serve that purpose (effectively "rescheduling" the execution for "never"), this is not a use case that the system was ever intended to serve. So it's hardly a failure of the system that it does so inelegantly.
 

--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.

Niall Douglas

unread,
Oct 3, 2017, 5:58:48 PM10/3/17
to ISO C++ Standard - Future Proposals

P.S. I'll contact Nial to see if he can share his paper already.


I am highly hesitant as I haven't even got round to merging your list of feedback into the proposed text. It's still literally as you saw it before your feedback.

But I will do as you request anyway on a separate thread.

Niall 

Nicol Bolas

unread,
Oct 3, 2017, 6:18:12 PM10/3/17
to ISO C++ Standard - Future Proposals
On Tuesday, October 3, 2017 at 5:51:19 PM UTC-4, Richard Smith wrote:
On 3 October 2017 at 14:40, Nicol Bolas <jmck...@gmail.com> wrote:
On Tuesday, October 3, 2017 at 4:50:34 PM UTC-4, Richard Smith wrote:
On 3 October 2017 at 13:28, Nicol Bolas <jmck...@gmail.com> wrote:
On Tuesday, October 3, 2017 at 12:24:17 PM UTC-4, Jeffrey Yasskin wrote:
FWIW, I'd be more interested in a proposal to help std::optional take advantage of the monadic properties of the coroutines language feature.

Is that really a thing we want to encourage? Ignoring that "awaiting" on something that isn't even potentially asynchronous is semantic nonsense, putting `co_await` into function causes a number of effects. Non-trivial effects.

You can no longer use `return`, for example; you must use `co_return`. So you can't just stick `co_await` anywhere and expect it to work.

Also, they inhibit guaranteed elision of return prvalues. Coroutines that return prvalues are really returning a copy/move from an object stored in the promise. And that requires that it be initialized, and therefore must be distinct from the caller's object.

If we really want a feature like this, then we should have one explicitly designed for this purpose. Not one that accidentally serves it, which has a bunch of unpleasant side-effects.

You're right. But in my view, the problem isn't the choice to use coroutines, it's the choice *in the Coroutines TS* to use terminology and mechanisms that only make sense for a subset of the use cases. That's something we can fix by generalizing and improving the mechanisms of the coroutines proposal.

Good luck getting anyone to agree to what would amount to a page-1 rewrite of the feature.

Also, I contest the idea that the Coroutines TS "only make sense for a subset of the use cases". It works for the use cases that it's designed for. Coroutines TS is about halting the execution of a function and rescheduling it for later. What is being discussed here has nothing to do with stopping the execution with the expectation of continuing later; it's about doing a conditional return of a value.

The Coroutines TS was not designed for the full set of coroutine use cases, which include cases where we only wish to suspend and destroy the coroutine, not suspend and later resume it. This is a degenerate, but important, special case of the general feature.

"Suspend and destroy the coroutine" is something we can already do. It's called "returning from the function".

We need the coroutines feature to be able to suspend and resume it later. "Suspend and destroy" is not a use case for the Coroutines TS feature. It's simply something you can do, and something that some people wish to pervert into a way to do a conditional return.

Conditional returns has nothing to do with any of the existing coroutine machinery. A conditional return doesn't require a coroutine promise or future return value. It ignores most of the `co_await` machinery. It doesn't use the `coroutine_handle` itself, or the ability to suspend the function.

This is not a "use case" of a "coroutine". It's not a "coroutine" at all. One could imagine a way to design lightweight continuations that don't allow you to implement conditional returns with them.

The only reason people want to slap conditional return into coroutines (rather than make a feature dedicated to it) is because it's the path of least resistance. Coroutines TS is something we're pretty sure is "gonna happen" in C++ (if not 2020, then 2023), so if you can make your feature look like a "degenerate case" of it, then it'll be easier to get it into the language than to get the committee to agree to your new/repurposed keyword for that purpose.

It is a political move, not one based on the technical merits of the idea.



Those aren't related to one another. While you can coerce the coroutine machinery to serve that purpose (effectively "rescheduling" the execution for "never"), this is not a use case that the system was ever intended to serve. So it's hardly a failure of the system that it does so inelegantly.
 

--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposal...@isocpp.org.

Nicol Bolas

unread,
Oct 3, 2017, 6:21:52 PM10/3/17
to ISO C++ Standard - Future Proposals
On Tuesday, October 3, 2017 at 6:18:12 PM UTC-4, Nicol Bolas wrote:
On Tuesday, October 3, 2017 at 5:51:19 PM UTC-4, Richard Smith wrote:
On 3 October 2017 at 14:40, Nicol Bolas <jmck...@gmail.com> wrote:
On Tuesday, October 3, 2017 at 4:50:34 PM UTC-4, Richard Smith wrote:
On 3 October 2017 at 13:28, Nicol Bolas <jmck...@gmail.com> wrote:
On Tuesday, October 3, 2017 at 12:24:17 PM UTC-4, Jeffrey Yasskin wrote:
FWIW, I'd be more interested in a proposal to help std::optional take advantage of the monadic properties of the coroutines language feature.

Is that really a thing we want to encourage? Ignoring that "awaiting" on something that isn't even potentially asynchronous is semantic nonsense, putting `co_await` into function causes a number of effects. Non-trivial effects.

You can no longer use `return`, for example; you must use `co_return`. So you can't just stick `co_await` anywhere and expect it to work.

Also, they inhibit guaranteed elision of return prvalues. Coroutines that return prvalues are really returning a copy/move from an object stored in the promise. And that requires that it be initialized, and therefore must be distinct from the caller's object.

If we really want a feature like this, then we should have one explicitly designed for this purpose. Not one that accidentally serves it, which has a bunch of unpleasant side-effects.

You're right. But in my view, the problem isn't the choice to use coroutines, it's the choice *in the Coroutines TS* to use terminology and mechanisms that only make sense for a subset of the use cases. That's something we can fix by generalizing and improving the mechanisms of the coroutines proposal.

Good luck getting anyone to agree to what would amount to a page-1 rewrite of the feature.

Also, I contest the idea that the Coroutines TS "only make sense for a subset of the use cases". It works for the use cases that it's designed for. Coroutines TS is about halting the execution of a function and rescheduling it for later. What is being discussed here has nothing to do with stopping the execution with the expectation of continuing later; it's about doing a conditional return of a value.

The Coroutines TS was not designed for the full set of coroutine use cases, which include cases where we only wish to suspend and destroy the coroutine, not suspend and later resume it. This is a degenerate, but important, special case of the general feature.

"Suspend and destroy the coroutine" is something we can already do. It's called "returning from the function".

We need the coroutines feature to be able to suspend and resume it later. "Suspend and destroy" is not a use case for the Coroutines TS feature. It's simply something you can do, and something that some people wish to pervert into a way to do a conditional return.

Conditional returns has nothing to do with any of the existing coroutine machinery. A conditional return doesn't require a coroutine promise or future return value. It ignores most of the `co_await` machinery. It doesn't use the `coroutine_handle` itself, or the ability to suspend the function.

This is not a "use case" of a "coroutine". It's not a "coroutine" at all. One could imagine a way to design lightweight continuations that don't allow you to implement conditional returns with them.

The only reason people want to slap conditional return into coroutines (rather than make a feature dedicated to it) is because it's the path of least resistance. Coroutines TS is something we're pretty sure is "gonna happen" in C++ (if not 2020, then 2023), so if you can make your feature look like a "degenerate case" of it, then it'll be easier to get it into the language than to get the committee to agree to your new/repurposed keyword for that purpose.

It is a political move, not one based on the technical merits of the idea.

Indeed, perverting `co_await` into a conditional return leads to a big problem when you try to combine the intended use of the feature with actual conditional returns. That is, a function whose return type is a `future` and is awaitable, but the `T` in the future an `optional` or an `expected<T, E>`, and you want to `co_await` on one of those.

With a real conditional return system, the two could be combined. With `co_await`, it leads to a crazy interaction between disparate systems that aren't designed to work that way.

Conditional returns are not coroutines, and they should not be treated as such.

Vicente J. Botet Escriba

unread,
Oct 3, 2017, 8:32:24 PM10/3/17
to std-pr...@isocpp.org, Simon Brand
Le 03/10/2017 à 17:09, Simon Brand a écrit :
Hi everyone,

I've written a proposal to add map and bind member functions to std::optional. The idea is to allow the writing of clear and concise code in the presence of functions which may not produce a result.For example, instead of writing this:



You could write this:

std::optional<int> foo() {
    return
      a().bind(b)
         .bind(c)
         .bind(d)
         .bind(e);
}

I'd appreciate any feedback on the proposal as written and the utility of this idea. You can find it here: https://github.com/TartanLlama/monadic-optional-proposal




Hi,

as you surely know expected had map and bind functions that were striped from the proposal in favor of a generic monadic interface. This was the reason d'être of the monadic proposal you reference. BTW, this proposal has not yet been reviewed by the committee.

I agree that my monadic interface is too heavy,

std::optional<int> foo() {
    return
      monad::bind(e,    
        monad::bind(d,
          monad::bind(c,
            monad::bind(a(), b));
}

but we expect to have an operator try (that is equivalent to the haskell do-notation for types as optional and expected) and we will have co_await for the classes that cannot define the operator try (are mMonads, but not SuccessOrFailure types).

My proposal includes a catch_error function that makes explicit the possible error recovery. No need to overload bind.

We could as well, as you overload existing operators for map (|) and bind (>=) and  catch_error (&)
std::optional<int> foo() {
    return
      	e    
         => d
         => c
         => a() 
	 & b;
}

With UFCS we could as well have
std::optional<int> foo() {
 using namespace monad;
 return
      e.bind(d) 
       .bind(c)
	.bind(a())
	.catch_error(b);
}

As you can see there is a lot way to add syntactic sugar.

What is more important to me is to have a common interface for all those types. Syntactic sugar will come later as needed.

Vicente


Barry Revzin

unread,
Oct 3, 2017, 10:37:25 PM10/3/17
to ISO C++ Standard - Future Proposals
If we really want a feature like this, then we should have one explicitly designed for this purpose. Not one that accidentally serves it, which has a bunch of unpleasant side-effects.

You're right. But in my view, the problem isn't the choice to use coroutines, it's the choice *in the Coroutines TS* to use terminology and mechanisms that only make sense for a subset of the use cases. That's something we can fix by generalizing and improving the mechanisms of the coroutines proposal.


Toby Allsopp implemented this for optional and expected: https://github.com/toby-allsopp/coroutine_monad
Just thought it was worth sharing. 

Ray Hamel

unread,
Oct 4, 2017, 12:04:35 AM10/4/17
to ISO C++ Standard - Future Proposals
I think it would make more sense and be more functional / useful for `try` to
be an infix than a prefix operator, so you could do

return f(a) try g try h;

instead of

auto b = try f(a);
auto c = try g(b);
return try h(c);

Infix `try` would also be harder to confuse with its present use in
`try`-`catch` blocks. (And, for the same reason, simpler to parse as well).

Ray

Ray Hamel

unread,
Oct 4, 2017, 12:24:33 AM10/4/17
to ISO C++ Standard - Future Proposals
To clarify a bit, I'm suggesting infix `try` take an expression on the left and a
callable on the right, so

return a try f try g try h;

would be the semantic equivalent of the second snippet (with prefix `try`).

Ray

Todd Fleming

unread,
Oct 4, 2017, 9:11:49 AM10/4/17
to ISO C++ Standard - Future Proposals
Maybe a library function could do this:

return try_(a, f, g, h);

Todd

Barry Revzin

unread,
Oct 4, 2017, 10:01:02 AM10/4/17
to ISO C++ Standard - Future Proposals


On Tuesday, October 3, 2017 at 11:04:35 PM UTC-5, Ray Hamel wrote:
I think it would make more sense and be more functional / useful for `try` to
be an infix than a prefix operator, so you could do

return f(a) try g try h;

instead of

auto b = try f(a);
auto c = try g(b);
return try h(c);


The point of try is to alleviate the pain that is having to check a bunch of SuccessOrErrors and have nice syntax to just properly return in those cases. It doesn't make sense to me to think about try as being associated with some callable - it's just a hook for returning early. 

optional<A> maybe_a();
optional
<B> maybe_b();
optional
<C> maybe_c();
Z foo
(A, B, C);

optional
<Z> maybe_foo() {
   
// today
   
auto oa = maybe_a();
   
if (!oa) return nullopt;
   
auto ob = maybe_b();
   
if (!ob) return nullopt;
   
auto oc = maybe_c();
   
if (!oc) return nullopt;

   
return foo(*oa, *ob, *oc);

   
// with Simon's paper, we can use bind, but it does quite badly in this particular case
   
// which shouldn't be interpreted as a negative against the paper - this is just the worst case
   
// in my view, the map/bind interface and the try interface are orthogonal and both desirable
   
return maybe_a().bind([=](A a){
     
return maybe_b().bind([=](B b){
       
return maybe_c().map([=](C c){
         
return foo(a, b, c);
       
});
     
});
   
});

   
// ... which is why it'd be nice to additionally have something like the Rust/Swift try
   
auto a = try maybe_a();
   
auto b = try maybe_b();
   
auto c = try maybe_c();
   
return foo(a, b, c);
}

That last one is pretty nice to read, it's syntactically clear that these things can fail but without all the noise from the first section. 

Alberto Barbati

unread,
Oct 5, 2017, 3:40:30 AM10/5/17
to ISO C++ Standard - Future Proposals, tarta...@gmail.com
Il giorno mercoledì 4 ottobre 2017 02:32:24 UTC+2, Vicente J. Botet Escriba ha scritto:

std::optional<int> foo() {
    return
      a().bind(b)
         .bind(c)
         .bind(d)
         .bind(e);
}

I agree that my monadic interface is too heavy,

std::optional<int> foo() {
    return
      monad::bind(e,    
        monad::bind(d,
          monad::bind(c,
            monad::bind(a(), b));
}

[...]


We could as well, as you overload existing operators for map (|) and bind (>=) and  catch_error (&)
std::optional<int> foo() {
    return
      	e    
         => d
         => c
         => a() 
	 & b;
}

With UFCS we could as well have
std::optional<int> foo() {
 using namespace monad;
 return
      e.bind(d) 
       .bind(c)
	.bind(a())
	.catch_error(b);
}


I am curious to know why no one has proposed the following approach, that would be the more obvious to me:

     return first_nonnullopt(a, b, c, d, e);

Are we so enamored of the monadic approach that the function approach is now being frowned upon?

Alberto

PS: possible alternative names include but are not limited to: first_value(), first_with_value(), etc.

Todd Fleming

unread,
Oct 5, 2017, 8:57:51 AM10/5/17
to ISO C++ Standard - Future Proposals, tarta...@gmail.com
On Thursday, October 5, 2017 at 3:40:30 AM UTC-4, Alberto Barbati wrote:
I am curious to know why no one has proposed the following approach, that would be the more obvious to me:

     return first_nonnullopt(a, b, c, d, e);

Are we so enamored of the monadic approach that the function approach is now being frowned upon?

Alberto
 
That's like what I replied with earlier. Unfortunately it's hard to use that for the following example from Toby Allsopp's repo at https://github.com/toby-allsopp/coroutine_monad:

auto test_expected_coroutine() {
  return []() -> expected<int, error> {
    auto x = co_await f1();
    auto y = co_await f2(x);
    auto z = co_await f3(x, y);
    co_return z;
  }();
}


It'd really be nice to be able to do this:

auto test_expected_coroutine() {
  return []() -> expected<int, error> {
    auto x = co_await f1();
    auto y = co_await f2(x + 7);
    auto z = co_await f3(x * 9, y / 2);
    co_return z;
  }();
}

Todd

Alberto Barbati

unread,
Oct 5, 2017, 10:04:40 AM10/5/17
to ISO C++ Standard - Future Proposals, tarta...@gmail.com
I'm sorry, Todd, but I fail to see how this is connected with what I wrote. I was just talking about using a plain old non-coroutine algorithm with variadic arguments instead of a chain of member/binary functions.

Alberto

Todd Fleming

unread,
Oct 5, 2017, 10:08:13 AM10/5/17
to ISO C++ Standard - Future Proposals, tarta...@gmail.com
I'm sorry, Todd, but I fail to see how this is connected with what I wrote. I was just talking about using a plain old non-coroutine algorithm with variadic arguments instead of a chain of member/binary functions.

Alberto

My point is that a plain non-coroutine algorithm (you called it first_nonnullopt, I called it try_) isn't as flexible.

Todd

Tony V E

unread,
Oct 5, 2017, 2:36:44 PM10/5/17
to Standard Proposals
How about

auto test() {
    auto x = f1();
    auto y = f2(x + 7);
    auto z = f3(x * 9, y / 2);
    return z;
}


--
Be seeing you,
Tony

to...@mi6.gen.nz

unread,
Oct 5, 2017, 7:35:48 PM10/5/17
to ISO C++ Standard - Future Proposals
Hi Tony.

The point of the example using co_await is that the functions f1, f2, and f3 return expected<int, error> and we want to immediately return the error if one of them returns an error.

I'm not sure if you're proposing that your normal-looking syntax should have that behaviour or if you're suggesting the use of exceptions or if this is a koan of some type :P.

Can you please clarify?

Cheers,
Toby.

Tony V E

unread,
Oct 5, 2017, 8:21:39 PM10/5/17
to ISO C++ Standard - Future Proposals
Let's say is a koan about exceptions.

The point is that none of this discussion makes sense without comparing it to code that uses exceptions.

Sent from my BlackBerry portable Babbage Device
Sent: Thursday, October 5, 2017 7:35 PM
To: ISO C++ Standard - Future Proposals
Subject: Re: [std-proposals] Proposal: Monadic operations for std::optional

--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposal...@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.

Simon Brand

unread,
Oct 6, 2017, 6:38:38 PM10/6/17
to std-pr...@isocpp.org

Thanks for the feedback!


Niall:

Yes, I've read that paper and there's a section which talks about it in my proposal: https://github.com/TartanLlama/monadic-optional-proposal#other-solutions


Nicol:

Regarding naming: there's a section which addresses that in the proposal: https://github.com/TartanLlama/monadic-optional-proposal#alternative-names

For the overload set/lambda issue, there's a note in the proposal saying that abbreviated lambdas or lift operator would help, but I'll expand on this point a bit.

Regarding member functions pointers: you can pass a member function pointer to both bind and map if this is supplied by the optional. For example:


struct foo {
    int get_i();
};

std::optional<foo> a;
a.map(&foo::get_i);

--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposal...@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.

Simon Brand

unread,
Oct 6, 2017, 6:38:38 PM10/6/17
to std-pr...@isocpp.org

Thanks for the feedback!


Niall:

Yes, I've read that proposal and there's a section which talks about it in my proposal: https://github.com/TartanLlama/monadic-optional-proposal#other-solutions


Nicol:

Regarding naming: there's a section which addresses that in the proposal: https://github.com/TartanLlama/monadic-optional-proposal#alternative-names

For the overload set/lambda issue, there's a note in the proposal saying that abbreviated lambdas or lift operator would help, but I'll expand on this point a bit.

Regarding member functions pointers: you can pass a member function pointer to both bind and map if this is supplied by the optional. For example:


struct foo {
    int get_i();
};

std::optional<foo> a;
a.map(&foo::get_i);




--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposal...@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/b72edfb3-82bf-423f-b140-aac66193ede3%40isocpp.org.

-- 
Simon Brand
Senior Software Engineer, GPGPU Toolchains
Codeplay Software Ltd
Level C, Argyle House, 3 Lady Lawson St, Edinburgh, EH3 9DR
Tel: 0131 466 0503
Fax: 0131 557 6600
Website: http://www.codeplay.com
Twitter: https://twitter.com/codeplaysoft

This email and any attachments may contain confidential and /or privileged information and is for use by the addressee only. If you are not the intended recipient, please notify Codeplay Software Ltd immediately and delete the message from your computer. You may not copy or forward it, or use or disclose its contents to any other person. Any views or other information in this message which do not relate to our business are not authorized by Codeplay software Ltd, nor does this message form part of any contract unless so stated.
As internet communications are capable of data corruption Codeplay Software Ltd does not accept any responsibility for any changes made to this message after it was sent. Please note that Codeplay Software Ltd does not accept any liability or responsibility for viruses and it is your responsibility to scan any attachments.
Company registered in England and Wales, number: 04567874
Registered office: Regent House, 316 Beulah Hill, London, United Kingdom, SE19 3HF

Patrice Roy

unread,
Oct 13, 2017, 9:25:08 PM10/13/17
to std-pr...@isocpp.org
Gor would find this lovely, I'm sure. This co_return thing looked like a strange compromise, which still escapes me today to be honest (return looks fine in my eyes...) :/

--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+unsubscribe@isocpp.org.

To post to this group, send email to std-pr...@isocpp.org.

Cleiton Santoia

unread,
Nov 4, 2017, 7:15:46 PM11/4/17
to ISO C++ Standard - Future Proposals
Have you thought about using the dot operator instead a 'map' function ?

struct foo {
    int get_i();
};

std::optional<foo> a;
a.get_i();

Actually I´m not sure if it´s possible...

Nicol Bolas

unread,
Nov 4, 2017, 8:37:36 PM11/4/17
to ISO C++ Standard - Future Proposals
On Saturday, November 4, 2017 at 7:15:46 PM UTC-4, Cleiton Santoia wrote:
Have you thought about using the dot operator instead a 'map' function ?

Well, there is no "dot operator" at present. That's still a proposal, and hasn't really moved forward.

Reply all
Reply to author
Forward
0 new messages