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);
}
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.
FWIW, I'd be more interested in a proposal to help std::optional take advantage of the monadic properties of the coroutines language feature.
FWIW, I'd be more interested in a proposal to help std::optional take advantage of the monadic properties of the coroutines language feature.
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.
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.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/f4f59c35-27de-46f0-8393-e2bd8d84d2ff%40isocpp.org.
P.S. I'll contact Nial to see if he can share his paper already.
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-proposal...@isocpp.org.
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.
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.std::optional<int> foo() {
return
e
=> d
=> c
=> a()
& b;
}
std::optional<int> foo() {
using namespace monad;
return
e.bind(d)
.bind(c)
.bind(a())
.catch_error(b);
}
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.
return f(a) try g try h;
auto b = try f(a);
auto c = try g(b);
return try h(c);
return a try f try g try h;return try_(a, f, g, h);I think it would make more sense and be more functional / useful for `try` tobe an infix than a prefix operator, so you could doreturn f(a) try g try h;
instead ofauto b = try f(a);
auto c = try g(b);
return try h(c);
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);
}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
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; }(); }
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; }(); }
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
auto test() { auto x = f1(); auto y = f2(x + 7); auto z = f3(x * 9, y / 2); return z; }
| From: to...@mi6.gen.nz Sent: Thursday, October 5, 2017 7:35 PM To: ISO C++ Standard - Future Proposals Reply To: std-pr...@isocpp.org Subject: Re: [std-proposals] Proposal: Monadic operations for std::optional |
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.
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.
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
--
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/CAOfiQqm%2BiQtYVT30dyv%3DjpHpVeraWLk%3DkuF6FA%3Dy1W4e6zQSVw%40mail.gmail.com.
struct foo {
int get_i();
};
std::optional<foo> a;
a.get_i();
Have you thought about using the dot operator instead a 'map' function ?