std::optional with lambdas, I run into some rather annoying issue. I need to always use trailing return type syntax to get it deduced correctly in situations like this (e.g. when return std::nullopt comes first):
[](auto args)
{
if (!check(args))
return std::nullopt;
return foo(args); //returns some std::optional<T>
}
The above will not compile, as std::nullopt_t cannot be converted to std::optional<T>.
Context:
I'm trying to implement try_in_sequence, which basically executes functions passed as arguments one by one, until one succeeds (e.g. returns non-empty optional). Writing return type on every one of the 4-5 lambdas per function call is daunting. I understand that lambda's return type is consistent with auto, but it doesn't seem to be practical in this case.
Proposal:
Use std::common_type<return_stm_types...> as return type. E.g. use auto on each return statement individually, then deduce common type using `std::common_type`. std::common_type<std::nullopt_t, std::optional<int>> indeed yields std::optional<int>, as shown in shown in this example:
#include <type_traits>
#include <optional>
template <typename>
struct undefined;
int main()
{
using common_t = std::common_type_t<std::nullopt_t, std::optional<int>>;
static_assert(std::is_same_v<std::optional<int>, common_t>);
//undefined<std::common_type_t<std::nullopt_t, std::optional<int>>> foo;
}
Possible damage:
I cannot think of any at the moment. I never encountered a case where compilation error inside of lambda would be of any use. I believe this shouldn't affect existing code, as std::common_type is conversion rules friendly (not 100% sure though).
What you're missing is that the rules are like that for a reason. Deduction is
intentional that it requires all return statements to return the same thing,
to prevent one type from being accidentally converted to another or -- worse
-- changing the function's return type when adding or removing a return
statement after refactoring.
Problem:When trying to mixstd::optional with lambdas, I run into some rather annoying issue. I need to always use trailing return type syntax to get it deduced correctly in situations like this (e.g. whenreturn std::nulloptcomes first):[](auto args)
{
if (!check(args))
return std::nullopt;
return foo(args); //returns some std::optional<T>
}
The above will not compile, as std::nullopt_t cannot be converted to std::optional<T>.
Context:I'm trying to implement try_in_sequence, which basically executes functions passed as arguments one by one, until one succeeds (e.g. returns non-empty optional). Writing return type on every one of the 4-5 lambdas per function call is daunting. I understand that lambda's return type is consistent with auto, but it doesn't seem to be practical in this case.
Proposal:Use std::common_type<return_stm_types...> as return type. E.g. useautoon each return statement individually, then deduce common type using `std::common_type`.std::common_type<std::nullopt_t, std::optional<int>>indeed yieldsstd::optional<int>, as shown in shown in this example:
#include <type_traits>
#include <optional>
template <typename>
struct undefined;
int main()
{
using common_t = std::common_type_t<std::nullopt_t, std::optional<int>>;
static_assert(std::is_same_v<std::optional<int>, common_t>);
//undefined<std::common_type_t<std::nullopt_t, std::optional<int>>> foo;
}
Possible damage:I cannot think of any at the moment. I never encountered a case where compilation error inside of lambda would be of any use. I believe this shouldn't affect existing code, asis conversion rules friendly (not 100% sure though).std::common_type
--
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/7772686a-7e80-4c28-a87b-c5dc11450cc6%40isocpp.org.
| From: Richard Smith Sent: Monday, June 25, 2018 1:24 PM Reply To: std-pr...@isocpp.org Subject: Re: [std-proposals] Apply std::common_type_t to deduce final return type in lambda return type deduction |
We could at least avoid common_type by instead talking about as if by ?:
Sent from my BlackBerry portable Babbage Device
From: Richard SmithSent: Monday, June 25, 2018 1:24 PMReply To: std-pr...@isocpp.orgSubject: Re: [std-proposals] Apply std::common_type_t to deduce final return type in lambda return type deduction
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/7772686a-7e80-4c28-a87b-c5dc11450cc6%40isocpp.org.
--
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 view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CAOfiQqkzrG-PaTDHdq8Yk8m212%2Birb4hfFOefDmdUjH6vNON4A%40mail.gmail.com.
--
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/20180625183948.5193804.45505.56022%40gmail.com.
On 25 June 2018 at 11:39, Tony V E <tvan...@gmail.com> wrote:We could at least avoid common_type by instead talking about as if by ?:Yes; I wish the language rule had originally been defined that way.
The self-recursive function case doesn't seem sufficiently compelling to me to take priority over supporting multiple returns with different types.
To know what the return value of a function is, you have to look up the rules of ?:'s common typing. Is that something we need to do? Or rather, is the gain from this really worth the cost?
#include <optional>#define matcher [&]()->std::optional
#include <iostream>template <typename T>std::ostream& operator<<(std::ostream& os, const std::optional<T>& val){ if (val.has_value()) os << *val; else os << "*null*"; return os;}
int main() { int target = 0; auto zero_matcher = matcher<int>{ if (target == 0) return target; else return std::nullopt; }; std::cout << zero_matcher() << '\n'; target = 3; std::cout << zero_matcher() << '\n';}When I think of it now, it seems like some clojure-esque solution would be easier to introduce. The language supported alternative to matcher perhaps would be a better idea:
#include <optional>
#define matcher [&]()->std::optional
#include <iostream>
template <typename T>
std::ostream& operator<<(std::ostream& os, const std::optional<T>& val)
{
if (val.has_value())
os << *val;
else
os << "*null*";
return os;
}
int main() {
int target = 0;
auto zero_matcher = matcher<int>{
if (target == 0)
return target;
else
return std::nullopt;
};
std::cout << zero_matcher() << '\n';
target = 3;
std::cout << zero_matcher() << '\n';
}
Now, the declaration probably should be like a template declaration, otherwise the only implementation would be virtual call style. Also, if it indeed looks like a template, it could integrate well with concepts and have already existing functionality to apply type erasure on them, like std::function and clojure shown on MeetingC++.
On Monday, June 25, 2018 at 3:29:11 PM UTC-4, Richard Smith wrote:On 25 June 2018 at 11:39, Tony V E <tvan...@gmail.com> wrote:We could at least avoid common_type by instead talking about as if by ?:Yes; I wish the language rule had originally been defined that way.... it is defined that way. `std::common_type` is defined in terms of the rules for `?:`, not the other way around.
The self-recursive function case doesn't seem sufficiently compelling to me to take priority over supporting multiple returns with different types.But do we really want to support that? Or more to the point, do we really need to avoid writing a type name this badly?Return type deduction is not free. You're taking an important piece of information which is well-specified in a specific location and obscuring it. Being able to inspect what a function does quickly is a good thing, and having to peer through its code to figure out what's going on is a bad thing.So for me, in order for the gains of return type deduction to outweigh the drawbacks, one of the following must be true:1. The return type is obvious based on the function's name and its parameters. A function named `add` that takes two parameters naturally returns whatever type is the sum of those parameters. So telling us what we can work out for ourselves is pointless redundancy.2. The function is very short, 5 statements or less. Telling us what we can see from within the function quite easily is pointless redundancy.3. The return type is difficult to type (large metaprogramming thing based on the types of parameters) or impossible to type (lambdas). Writing a big, long thing at the beginning (or end) of our function makes it difficult to read or understand.If none of these are true, I would say that return type deduction makes code harder to deal with rather than easier. A function with multiple return statements almost certainly fails #2. #3 can still happen, but not in the case the OP outlined when dealing with a clear case of `optional<int>`.So what we're left with is something that only rarely makes code easier to read. While simultaneously not catching mistakes where you happen to accidentally change the function's return type.Overall, I just think that this is way too much of a corner case to put it into the standard. I hate to use principles as bludgeons for features I don't like, but Bjarne's "Remember the Vasa" post would seem to speak to this. It's not making the language easier to use; it's making the language more expert-friendly, at the detriment to ease of use.To know what the return value of a function is, you have to look up the rules of ?:'s common typing. Is that something we need to do? Or rather, is the gain from this really worth the cost?
I think there are examples when it is worth the cost (particularly, the cases where the function has a primary return statement and some bailouts that return nullptr or nullopt or similar).
On Tuesday, June 26, 2018 at 2:26:56 AM UTC+6, Nicol Bolas wrote:To know what the return value of a function is, you have to look up the rules of ?:'s common typing. Is that something we need to do? Or rather, is the gain from this really worth the cost?I just thought that it would be easier than introducing some other construct.
What I'd like is basically an equivalent to the matcher macro below:#include <optional>#define matcher [&]()->std::optional#include <iostream>template <typename T>std::ostream& operator<<(std::ostream& os, const std::optional<T>& val){if (val.has_value())os << *val;elseos << "*null*";return os;}int main() {int target = 0;auto zero_matcher = matcher<int>{if (target == 0)return target;elsereturn std::nullopt;};std::cout << zero_matcher() << '\n';target = 3;std::cout << zero_matcher() << '\n';}
[&]()
{
if(target)
return target;
return nullopt;
}[&]()
{
if(target)
return std::optional<int>(target);
return nullopt;
}On 25 June 2018 at 13:26, Nicol Bolas <jmck...@gmail.com> wrote:On Monday, June 25, 2018 at 3:29:11 PM UTC-4, Richard Smith wrote:On 25 June 2018 at 11:39, Tony V E <tvan...@gmail.com> wrote:We could at least avoid common_type by instead talking about as if by ?:Yes; I wish the language rule had originally been defined that way.... it is defined that way. `std::common_type` is defined in terms of the rules for `?:`, not the other way around.Sorry, we have miscommunicated: "the language rule" I'm talking about is return type deduction, and how it handles the case of multiple types (not common_type). It (return type deduction) is not defined as using ?: to compute a common type, as you are no doubt aware.The self-recursive function case doesn't seem sufficiently compelling to me to take priority over supporting multiple returns with different types.But do we really want to support that? Or more to the point, do we really need to avoid writing a type name this badly?Return type deduction is not free. You're taking an important piece of information which is well-specified in a specific location and obscuring it. Being able to inspect what a function does quickly is a good thing, and having to peer through its code to figure out what's going on is a bad thing.So for me, in order for the gains of return type deduction to outweigh the drawbacks, one of the following must be true:1. The return type is obvious based on the function's name and its parameters. A function named `add` that takes two parameters naturally returns whatever type is the sum of those parameters. So telling us what we can work out for ourselves is pointless redundancy.2. The function is very short, 5 statements or less. Telling us what we can see from within the function quite easily is pointless redundancy.3. The return type is difficult to type (large metaprogramming thing based on the types of parameters) or impossible to type (lambdas). Writing a big, long thing at the beginning (or end) of our function makes it difficult to read or understand.If none of these are true, I would say that return type deduction makes code harder to deal with rather than easier. A function with multiple return statements almost certainly fails #2. #3 can still happen, but not in the case the OP outlined when dealing with a clear case of `optional<int>`.So what we're left with is something that only rarely makes code easier to read. While simultaneously not catching mistakes where you happen to accidentally change the function's return type.Overall, I just think that this is way too much of a corner case to put it into the standard. I hate to use principles as bludgeons for features I don't like, but Bjarne's "Remember the Vasa" post would seem to speak to this. It's not making the language easier to use; it's making the language more expert-friendly, at the detriment to ease of use.To know what the return value of a function is, you have to look up the rules of ?:'s common typing. Is that something we need to do? Or rather, is the gain from this really worth the cost?I think there are examples when it is worth the cost (particularly, the cases where the function has a primary return statement and some bailouts that return nullptr or nullopt or similar). Conversely, I'm not convinced that supporting return type deduction for (directly) recursive functions is worth the cost -- that seems like the expert-only feature in this context.
Conversely, having a single general type unification algorithm that is used in all contexts where it makes sense makes the language simpler and more beginner-friendly (I seem to recall a good blog post on this subject -- perhaps from Eric Lippert -- a few years back, about how the C# language was simplified by using the same rules for ?: type unification and for type unification in return type deduction, but I can't find it right now...). A beginner expects to be able to use nullptr where they would have used a value of a pointer type, and saying you can't do that in a return statement in a function with a deduced return type is an unnecessary complication. ("Why can't the compiler just work it out for itself? The intended return type is obvious.")
On Monday, June 25, 2018 at 6:12:57 PM UTC-4, Richard Smith wrote:On 25 June 2018 at 13:26, Nicol Bolas <jmck...@gmail.com> wrote:On Monday, June 25, 2018 at 3:29:11 PM UTC-4, Richard Smith wrote:On 25 June 2018 at 11:39, Tony V E <tvan...@gmail.com> wrote:We could at least avoid common_type by instead talking about as if by ?:Yes; I wish the language rule had originally been defined that way.... it is defined that way. `std::common_type` is defined in terms of the rules for `?:`, not the other way around.Sorry, we have miscommunicated: "the language rule" I'm talking about is return type deduction, and how it handles the case of multiple types (not common_type). It (return type deduction) is not defined as using ?: to compute a common type, as you are no doubt aware.The self-recursive function case doesn't seem sufficiently compelling to me to take priority over supporting multiple returns with different types.But do we really want to support that? Or more to the point, do we really need to avoid writing a type name this badly?Return type deduction is not free. You're taking an important piece of information which is well-specified in a specific location and obscuring it. Being able to inspect what a function does quickly is a good thing, and having to peer through its code to figure out what's going on is a bad thing.So for me, in order for the gains of return type deduction to outweigh the drawbacks, one of the following must be true:1. The return type is obvious based on the function's name and its parameters. A function named `add` that takes two parameters naturally returns whatever type is the sum of those parameters. So telling us what we can work out for ourselves is pointless redundancy.2. The function is very short, 5 statements or less. Telling us what we can see from within the function quite easily is pointless redundancy.3. The return type is difficult to type (large metaprogramming thing based on the types of parameters) or impossible to type (lambdas). Writing a big, long thing at the beginning (or end) of our function makes it difficult to read or understand.If none of these are true, I would say that return type deduction makes code harder to deal with rather than easier. A function with multiple return statements almost certainly fails #2. #3 can still happen, but not in the case the OP outlined when dealing with a clear case of `optional<int>`.So what we're left with is something that only rarely makes code easier to read. While simultaneously not catching mistakes where you happen to accidentally change the function's return type.Overall, I just think that this is way too much of a corner case to put it into the standard. I hate to use principles as bludgeons for features I don't like, but Bjarne's "Remember the Vasa" post would seem to speak to this. It's not making the language easier to use; it's making the language more expert-friendly, at the detriment to ease of use.To know what the return value of a function is, you have to look up the rules of ?:'s common typing. Is that something we need to do? Or rather, is the gain from this really worth the cost?I think there are examples when it is worth the cost (particularly, the cases where the function has a primary return statement and some bailouts that return nullptr or nullopt or similar). Conversely, I'm not convinced that supporting return type deduction for (directly) recursive functions is worth the cost -- that seems like the expert-only feature in this context.Perhaps, but we already support that. Features that already exist no longer need to justify themselves (unless you're debating removing them, which has to have a lot more justification due to being a breaking change). Proposed features need to justify themselves. And they need to make sure that code doesn't get worse.
Conversely, having a single general type unification algorithm that is used in all contexts where it makes sense makes the language simpler and more beginner-friendly (I seem to recall a good blog post on this subject -- perhaps from Eric Lippert -- a few years back, about how the C# language was simplified by using the same rules for ?: type unification and for type unification in return type deduction, but I can't find it right now...). A beginner expects to be able to use nullptr where they would have used a value of a pointer type, and saying you can't do that in a return statement in a function with a deduced return type is an unnecessary complication. ("Why can't the compiler just work it out for itself? The intended return type is obvious.")But the intended return type is not "obvious", as evidenced by the fact that you need to create a bunch of special rules to decide what it should be. The expression alone is insufficient.Yes, `return nullptr;` would represent that the function returns a pointer type. But... what pointer type? You don't know; you now have to go fishing around for another `return` statement to figure out what it actually returns. You don't even know if it's a raw pointer; it could be a `unique_ptr` or whatever. And if you don't know what pointer type a function returns, how can you use it?Yes, you can argue that the existing recursion rules have the same problem. I'm not exactly happy with them either, but in their defense, recursion is a lot more rare than wanting to do the kind of stuff you're talking about. So the problem that recursive calls create (ie: having to look at another `return` statement) is not often encountered.Code is read more often than it is written. So maybe let's not optimize for the latter when it makes the former really hard.
--
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/0367f321-1422-4437-80a3-716aa0d7dae7%40isocpp.org.
As this is just a new syntax for more clearly specifying something that we already have, I'm not sure how much support it will gain. But I think it's elegant enough for some discussion - this current thread is not the place for it, but if you repost as a new thread it would be beneficial.