Here is a contrasting proposal for how to handle overload sets as function arguments. While a lot more verbose in the simple case of just naming a function (where instead of just naming the function you have to actually enumerate the arguments list twice), I think it offers more flexibility in writing short lambdas. There are some motivating examples in the proposal. It's also getting pretty verbose to write correct generic functions with regards to SFINAE and noexcept, so this attempts to make it a little bit easier - at least in the easy case.Also I'm proposing a shorter syntax for forwarding arguments. I picked unary>> because it looks like moving something forward but really anything shorter would be nice. std::forward<Arg>(arg) is particularly verbose, especially in a generic lambda where you have to write std::forward<decltype(arg)>(arg), where the actual forwarding just dominates the code itself.
Minor correction to your "Effects on Existing Code" section: the character sequence => can appear in legal C++ programs today. For instance, X<&Y::operator=>. That said, I've searched a large codebase for this (>100MLoC) and the only occurrence of => (outside of comments, string literals, etc.) was in a compiler test suite.
Also, some corner cases like default argument values and variables already defined are on the list you might want to address. Like `auto x = x => x => x =>x;` and `auto x = x => x = x => x = x;` are non-obvious to me. (`Is = x` an assignment / initialization in caller (later called) code, or a default parameter value?)
My wishlist entry is support for short nullary lambdas in addition to these.
auto x = x => x => x => x;
auto x = [](auto&& x){
return [](auto&& x){
return [](auto&& x){
return x;
};
};
};
auto x = x => x = x => x = x;
auto x = [](auto&& x){
return x = [](auto&& x){
return x = x;
};
};
auto just5 = () => 5;
auto maybe5 = (x=5) => x;
Here is a contrasting proposal for how to handle overload sets as function arguments. While a lot more verbose in the simple case of just naming a function (where instead of just naming the function you have to actually enumerate the arguments list twice), I think it offers more flexibility in writing short lambdas. There are some motivating examples in the proposal. It's also getting pretty verbose to write correct generic functions with regards to SFINAE and noexcept, so this attempts to make it a little bit easier - at least in the easy case.Also I'm proposing a shorter syntax for forwarding arguments. I picked unary>> because it looks like moving something forward but really anything shorter would be nice. std::forward<Arg>(arg) is particularly verbose, especially in a generic lambda where you have to write std::forward<decltype(arg)>(arg), where the actual forwarding just dominates the code itself.
I would like to suggest exstending the => expr to be valid replacement for the function body for any kind of function. I mean that:
template<typename C>
auto begin(C&& c) => std::forward<C>(c).begin();
Would be valid definition, equivalent to:
template<typename C>
inline auto begin(C&& c)
noexcept(std::forward<C>(c).begin())
-> decltype(std::forward<C>(c).begin())
{ return std::forward<C>(c).begin(); }
The following restriction will apply:
1) Function with expression-body cannot be forward declared.
2) Explicit inline
4) Can have only auto introducer
As this will use decltype(expr) deduction (not decltype(auto)) it will make the function SFINAE'able on the function body, and will basically resolve problems listed in http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0238r0.html paper.
On Wednesday, October 12, 2016 at 12:13:31 AM UTC-4, Tomasz wrote:I would like to suggest exstending the => expr to be valid replacement for the function body for any kind of function. I mean that:
template<typename C>
auto begin(C&& c) => std::forward<C>(c).begin();
Would be valid definition, equivalent to:
template<typename C>
inline auto begin(C&& c)
noexcept(std::forward<C>(c).begin())
-> decltype(std::forward<C>(c).begin())
{ return std::forward<C>(c).begin(); }
The following restriction will apply:
1) Function with expression-body cannot be forward declared.
2) Explicit inline
4) Can have only auto introducer
As this will use decltype(expr) deduction (not decltype(auto)) it will make the function SFINAE'able on the function body, and will basically resolve problems listed in http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0238r0.html paper.
But returning `decltype(expr)` is (SFINAE-aside) equivalent to `auto`. Which means that it will always return by value. So if `expr` results in a reference... too bad; it's returning a value now.
This will be difficult to parse. Requiring lookahead past the ) whenever we see a ( is likely not going to be acceptable. Handling "(identifier-list) =>" would already be a rather special case, but is probably manageable because you can defer handling the identifier-list part until after you reach the ")" and see whether there's a "=>" next. But once you add in default arguments / assignment-expressions (where the rules for valid expressions are subtly different, especially since earlier parameter names are in scope in the default arguments of later parameters), you can't use that same strategy.
auto f(/* ... */) = expr;
On Wed, Oct 12, 2016 at 1:06 AM, Nicol Bolas <jmck...@gmail.com> wrote:
> But most of the time for such functions, you want decltype(expr) mechanics.
For non-capturing cases, maybe. But in general, I can't say
much without data.
> By default, it will
> use `decltype(expr)`. However, we can force `std::decay_t<decltype(expr)>`
> by saying `=> auto expr`.
>
> [...]
>
> It would be a bit inconsistent, since lambdas normally default to `auto`
> rather than `decltype(auto)`. But considering that we're using different
> syntax, it would probably work out OK.
You may have noticed that in terms of teachability (yes, it
should be a word if there is a "C++ discussion mode" in
email clients), you are adding complexity on top of
inconsistency, doubling the work plus fearing people
away...
On 10/11/16 20:32, Barry Revzin wrote:
Here is a contrasting proposal for how to handle overload sets as
function arguments. While a lot more verbose in the simple case of just
naming a function (where instead of just naming the function you have to
actually enumerate the arguments list twice), I think it offers more
flexibility in writing short lambdas. There are some motivating examples
in the proposal. It's also getting pretty verbose to write correct
generic functions with regards to SFINAE and noexcept, so this attempts
to make it a little bit easier - at least in the easy case.
Also I'm proposing a shorter syntax for forwarding arguments. I picked
unary>> because it looks like moving something forward but really
anything shorter would be nice. std::forward<Arg>(arg) is particularly
verbose, especially in a generic lambda where you have to write
std::forward<decltype(arg)>(arg), where the actual forwarding just
dominates the code itself.
IMHO, this new syntax makes the code more obscure. The proposed abbreviated lambdas no longer have any resemblence with functions and blend in the surrounding code too much (yes, this is a bad thing).
2016-10-11 23:56 GMT+02:00 Andrey Semashev <andrey....@gmail.com>:On 10/11/16 20:32, Barry Revzin wrote:
Here is a contrasting proposal for how to handle overload sets as
function arguments. While a lot more verbose in the simple case of just
naming a function (where instead of just naming the function you have to
actually enumerate the arguments list twice), I think it offers more
flexibility in writing short lambdas. There are some motivating examples
in the proposal. It's also getting pretty verbose to write correct
generic functions with regards to SFINAE and noexcept, so this attempts
to make it a little bit easier - at least in the easy case.
Also I'm proposing a shorter syntax for forwarding arguments. I picked
unary>> because it looks like moving something forward but really
anything shorter would be nice. std::forward<Arg>(arg) is particularly
verbose, especially in a generic lambda where you have to write
std::forward<decltype(arg)>(arg), where the actual forwarding just
dominates the code itself.
IMHO, this new syntax makes the code more obscure. The proposed abbreviated lambdas no longer have any resemblence with functions and blend in the surrounding code too much (yes, this is a bad thing).
+1While I would like a more compact syntax for lambdas my eyes seem to be really adjusted to brackets so I would prefer such syntax:(x) => {i < 1000}
On Wed, Oct 12, 2016 at 2:29 PM, Domen Vrankar <domen....@gmail.com> wrote:
>
> (x) => {i < 1000}
>
> So mandatory parentheses around parameters even if there is only a single
> one and mandatory curly braces around function body even though it is a one
> liner. Don't care too much about return statement, semicolon or [ ]
> brackets in this case - in fact I even prefer them gone.
Read [] as the introductory λ notation:
[] x -> x < 1000
From: Nicol Bolas Sent: Wednesday, October 12, 2016 4:30 PM To: ISO C++ Standard - Future Proposals |
Reply To: std-pr...@isocpp.org Subject: Re: [std-proposals] An abbreviated lambda syntax |
2016-10-11 23:56 GMT+02:00 Andrey Semashev <andrey....@gmail.com>:On 10/11/16 20:32, Barry Revzin wrote:
Here is a contrasting proposal for how to handle overload sets as
function arguments. While a lot more verbose in the simple case of just
naming a function (where instead of just naming the function you have to
actually enumerate the arguments list twice), I think it offers more
flexibility in writing short lambdas. There are some motivating examples
in the proposal. It's also getting pretty verbose to write correct
generic functions with regards to SFINAE and noexcept, so this attempts
to make it a little bit easier - at least in the easy case.
Also I'm proposing a shorter syntax for forwarding arguments. I picked
unary>> because it looks like moving something forward but really
anything shorter would be nice. std::forward<Arg>(arg) is particularly
verbose, especially in a generic lambda where you have to write
std::forward<decltype(arg)>(arg), where the actual forwarding just
dominates the code itself.
IMHO, this new syntax makes the code more obscure. The proposed abbreviated lambdas no longer have any resemblence with functions and blend in the surrounding code too much (yes, this is a bad thing).
+1
inline void f()
{
...
}
inline void f() noexcept(false)
{
...
}
// OK: calls #1
invoke([](auto&& x) noexcept(noexcept(test(std::forward<decltype(x)>(x))))
-> decltype(test(std::forward<decltype(x)>(x)))
{
return test(std::forward<decltype(x)>(x));
});
invoke(x => test(forward(x)));
or
invoke([](x) => {test(forward(x))});
Is not that different and I see the later as a good compromise between the two.
Terser syntax yes, removing everything so that it blurs with the surrounding not so much.
Regards,
Domen
Is [](funcname) about to be valid syntax?
On 10/13/16 13:15, Domen Vrankar wrote:
> 2016-10-13 11:55 GMT+02:00 Domen Vrankar <domen....@gmail.com
> <mailto:domen....@gmail.com>>:
>
> Even with the long example from the proposal that is using noexcepti
> with only one parameter with auto&& removing brackets would not make
> much difference while on the other hand preseving brackets and removing
> the rest makes a huge difference:
>
> |// OK: calls #1 *invoke*([](auto&& *x*)
> noexcept(noexcept(test(std::forward<decltype(x)>(x)))) ->
> decltype(test(std::forward<decltype(x)>(x))) { return
> *test*(std::*forward*<decltype(x)>(*x*)); });
>
> |
> |invoke(x => test(forward(x)));
> |
> |or
> |
> |invoke([](x) => {test(forward(x))});
> |
> |
> |
> |Is not that different and I see the later as a good compromise between
> the two.
> |
> |
> Terser syntax yes, removing everything so that it blurs with the
> surrounding not so much.
I think the original problem should be decomposed into several smaller
ones, which can be dealt with separately.
For instance, I would be interested in a proposal that makes the
explicit template argument to std::forward unnecessary. Regardless
lambdas, this alone would be helpful in general.
Another step is to enable compilers deduce noexcept automatically when
possible (which lambdas surely qualify). Again, this would be a
generally useful addition.
Having done just those two you shorten your lambda by more than half:
invoke([](auto&& x) -> decltype(auto)
{ return test(std::forward(x)); });
Noexcept deduction, if we allow it, must be something we ask for on regular function declarations. Why? Because it becomes part of the interface of an API. And yet, it's not actually part of the function's interface. It's something that changes based on the implementation, since it is deduced from the implementation.
Users can detect if an API of yours is noexcept. And if it is, then they can write code that requires this. If it were automatically deduced, then users would have to maintain it even if they didn't intend for the function to be noexcept.
Lambdas are not used as the interface in an API, so allowing them to deduce noexcept may be OK. But this is not something that should generally be imposed on a function.
template<class Func>
inline decltype(auto) wrapper(Func &&f) noexcept(noexcept(std::forward<Func>(f)()))
{
std::forward<Func>(f)();
}
On Thursday, October 13, 2016 at 7:16:11 PM UTC+3, Nicol Bolas wrote:Noexcept deduction, if we allow it, must be something we ask for on regular function declarations. Why? Because it becomes part of the interface of an API. And yet, it's not actually part of the function's interface. It's something that changes based on the implementation, since it is deduced from the implementation.
Users can detect if an API of yours is noexcept. And if it is, then they can write code that requires this. If it were automatically deduced, then users would have to maintain it even if they didn't intend for the function to be noexcept.
Lambdas are not used as the interface in an API, so allowing them to deduce noexcept may be OK. But this is not something that should generally be imposed on a function.How inline functions are different in this sense? How
template<class Func>
inline decltype(auto) wrapper(Func &&f) noexcept(noexcept(std::forward<Func>(f)()))
{
std::forward<Func>(f)();
}
is "API-stable"?
On 10/13/16 19:16, Nicol Bolas wrote:
> On Thursday, October 13, 2016 at 6:37:10 AM UTC-4, Andrey Semashev wrote:
>
> For instance, I would be interested in a proposal that makes the
> explicit template argument to std::forward unnecessary. Regardless
> lambdas, this alone would be helpful in general.
>
>
> I don't think there is a way to do that.
Not without some sort of compiler magic or a new language feature. But
I'm ok if magic is involved.
> And even if there was, you
> still have the noise of `std::forward` cluttering up the function.
And I like that it's there. It makes the intention more obvious, same as
when you use std::move or std::ref.
If it has to be requested explicitly then it
should be something short and simple, like this:
void foo() noexcept(auto)
{
// ...
}
> Lambdas are not used as the interface in an API, so allowing them to
> deduce noexcept may be OK. But this is not something that should
> generally be imposed on a function.
Ok, so we make noexcept(auto) the implicit default for lambdas but not
other functions. I'm ok with that.
> The usefulenss of the `=>` syntax is that it solves about 60% of the
> problem. It solves multiple problems all at once. You can only get such
> a solution by focusing on the lambda problem itself. If you decompose
> the problems, you'll get decomposed solutions with them. These may make
> the syntax shorter, but they won't necessarily fix the problem.
But when you introduce the new syntax you only solving the problem for
lambdas (as I opined earlier, in a not good way) leaving other parts of
the language disconnected.
template<typename ...Ts> auto something(Ts &&...ts) => {some_func(ts...)};
> I'm not against decomposition per-se. But any such decomposition needs
> to remain focused on the specific problems.
>
> 1: Parameter declarations in lambdas. Having to specify a type rather
> than getting `auto&&`.
>
> 2: Specifying return types and noexcept explicitly in order to properly
> return an expression, with SFINAE intact.
>
> 3: Specifying std::forward<...> for the use of forwarding reference
> parameters.
>
> It seems to me that, in terms of code burden, #2 is the one that takes
> up the most room, followed by #3. #1, while not unimportant, is not as
> gigantic as the others.
>
> Solving #2 is essentially about having a function whose entire body is
> "return <expr>;". The `=> expr` syntax discussed in this thread seems
> like a good fit here, especially since it could also be applied to
> regular function declarations.
I disagree. I mean, you are correct if all you want to do is optimize
the very specific case - a lambda with a single return statement. But my
point is that we should not be optimizing that one single case, when the
same problems exist in other cases as well.
Noexcept specifications are
yet more verbose for functions that are more complex than a lazy return.
Callability forwarding (i.e. SFINAE-friendliness) is yet more difficult.
These problems need solving in general, and by induction in lambda
functions.
Consider also that the function call syntax currently is self-sufficient
in a way that you know which arguments you are discarding (i.e. moving
or forwarding) and which you do not. With the new syntax this is no
longer the case since it changes the way function arguments are
interpreted (i.e. implicit forward).
// none of these do any forwarding
[](auto&& x) -> decltype(twice(x)) { return twice(x); }
[](auto&& x) => twice(x);
[](x) => twice(x);
// all of these do forwarding
[](auto&& x) -> decltype(twice(std::forward<decltype(x)>(x))) { return twice(std::forward<decltype(x)>(x)); }
[](auto&& x) => twice(std::forward<decltype(x)>(x));
[](x) => twice(std::forward<decltype(x)>(x));
[](x) => twice(>>x);
Here is a contrasting proposal for how to handle overload sets as function arguments. While a lot more verbose in the simple case of just naming a function (where instead of just naming the function you have to actually enumerate the arguments list twice), I think it offers more flexibility in writing short lambdas. There are some motivating examples in the proposal. It's also getting pretty verbose to write correct generic functions with regards to SFINAE and noexcept, so this attempts to make it a little bit easier - at least in the easy case.Also I'm proposing a shorter syntax for forwarding arguments. I picked unary>> because it looks like moving something forward but really anything shorter would be nice. std::forward<Arg>(arg) is particularly verbose, especially in a generic lambda where you have to write std::forward<decltype(arg)>(arg), where the actual forwarding just dominates the code itself.
From: szollos...@gmail.com Sent: Saturday, October 15, 2016 3:36 AM |
To: ISO C++ Standard - Future Proposals Reply To: std-pr...@isocpp.org Subject: Re: [std-proposals] An abbreviated lambda syntax |
Since you're creating a new operator, I would like to have some explanation of the precedence of this operator. I imagine that it would be at precedence level 3, along with the other prefix operators.
You also don't make it clear if the new abbreviated lambda parameter declaration requires the use of `=>` or not. That is, is it legal to do `[](x) {foo(x + 1);}`? Similarly, it isn't completely clear that `>>` can be used outside of the context of terse lambda expressions.
[](arg ) { return sizeof(arg); }
What do you think?
Like `x + 1` is a 'lifted' or to-be-inlined expression, a lambda where x is auto-forwarded because you don't get a new type at all, just a direct substitution of x. Or maybe `x` means forward x.
transform(first, last, _ + 1);
sort(first, last, _ > _);
transform(first, last, [&](auto&& x) { return x+1; });
sort(first, last, [&](auto&& a, auto&& b) { return a > b; });
Going to reply to a bunch of people in one go:Nicol:Since you're creating a new operator, I would like to have some explanation of the precedence of this operator. I imagine that it would be at precedence level 3, along with the other prefix operators.Yes, that would seem about right. And >> would be usable anywhere.Nicol:You also don't make it clear if the new abbreviated lambda parameter declaration requires the use of `=>` or not. That is, is it legal to do `[](x) {foo(x + 1);}`? Similarly, it isn't completely clear that `>>` can be used outside of the context of terse lambda expressions.One problem that comes up with allowing for omitting the type (and this is as true for the => notation as it is for the normal lambda notation) is a potential ambiguity:
[](arg ) { return sizeof(arg); }is this a lambda taking a single unnamed variable of type arg and returning that size, or is it a lambda taking a forwarding reference named arg and returning the size of the type it refers to? To allow the terse naming for normal lambdas, it would have to be the latter - but there are undoubtedly lambdas in existence that take unnamed arguments. And this could break code if that code relies on side effects of constructing those arguments. Or at the very least will suddenly start having warnings about unused variables.I would like for the terse syntax to be used anywhere, but this case concerns me.
This reminds me a bit of how Scala allows you to do lambdas with _. There you can write:
transform(first, last, _ + 1);
sort(first, last, _ > _);to mean:
transform(first, last, [&](auto&& x) { return x+1; });
sort(first, last, [&](auto&& a, auto&& b) { return a > b; });
I may be wrong about the implicit capture by reference, but at least something to that effect.
What do you think?I do not understand your use of : and what it means in the context of lambdas, sorry.
shall be exactly equivalent to the function:auto fct(auto&& x) => test(x);
Vicenteauto fct(auto&& x) noexcept(noexcept(test(x))) -> decltype(test(x)) { return test(x); }
Yes, he considered that. It's right there in the document he posted ;) Search for "Abbreviated Function Syntax".
Except for unary>>, these features were proposed and rejected in
Portland. I don't see much in this
proposal that would make the objections made at that time moot.
The problem with a single expression that is not inside braces is that
use cases where the lambda would be immediately
called instead of passed as an argument are unattainable, whereas such
problems don't arise with traditional lambdas.
There are also readability concerns with lambdas where the body is not braced.
On 16 October 2016 at 22:48, Nicol Bolas <jmck...@gmail.com> wrote:
>
>
> On Sunday, October 16, 2016 at 3:43:03 PM UTC-4, Ville Voutilainen wrote:
>>
>> On 16 October 2016 at 22:28, Barry Revzin <barry....@gmail.com> wrote:
>> >>>
>> >>
>> >> Yes, he considered that. It's right there in the document he posted ;)
>> >> Search for "Abbreviated Function Syntax".
>> >
>> >
>> > Yep! Although here's a rewritten version where I'm clarifying that there
>> > are
>> > three independent proposals here: => expr, unary>>, and omitting types
>> > in
>> > lambdas. Hopefully this'll be clearer that the intent of => is to be
>> > used in
>> > both lambdas and functions.
>>
>>
>> Except for unary>>, these features were proposed and rejected in
>> Portland. I don't see much in this
>> proposal that would make the objections made at that time moot.
>
>
> Could you provide some information on what those proposals were and what the
> objections were?
That would be http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3418.pdf.
The problem with an untyped argument is that it's ambiguous - does it
mean an unnamed parameter of a certain
type, or a parameter of a deduced type? The former is already used by
existing code, so this is a breaking change.
The problem with a single expression that is not inside braces is that
use cases where the lambda would be immediately
called instead of passed as an argument are unattainable, whereas such
problems don't arise with traditional lambdas.
There are also readability concerns with lambdas where the body is not braced.
On 17 October 2016 at 00:42, Nicol Bolas <jmck...@gmail.com> wrote:
> So what about having untyped arguments, so long as it isn't ambiguous? That
> is, `&&name`, `const &name`, `*name`, etc.
And how do you unambiguously have an unnamed by-value parameter?
Except that's not what ?: does. If your back-tick things create lambdas, then that's very different from how ?: works. And if your back-tick things don't create lambdas, then it's not clear what's going on.
(Incidentally, I don't think `=>` should allow a braced expression; that
would give an entirely new and strange meaning to braces. *Parentheses*
on the other hand would make sense. Actually, I think `=>{expr}` should
be equivalent to `{ return {expr}; }`, which of course only makes sense
if the return type is specified.)
I updated the proposal to expand on one of the ideas (using >> not only as a substitute for std::forward, but also in lambda-capture to decay-copy, adding the ability to capture parameter packs not only by value and reference but also by decay-copy) and narrow another (omitting type-names in lambdas only applies to those introduces with =>).If this seems reasonable and agreeable, I'd like to add it to the next mailing.
--
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/b6280220-06c4-4913-8f50-6a13297655ad%40isocpp.org.
[&obj](...args) => obj.func(>>args...)
should probably be
[&obj](args...) => obj.func(>>args...)
I updated the proposal to expand on one of the ideas (using >> not only as a substitute for std::forward, but also in lambda-capture to decay-copy, adding the ability to capture parameter packs not only by value and reference but also by decay-copy) and narrow another (omitting type-names in lambdas only applies to those introduces with =>).If this seems reasonable and agreeable, I'd like to add it to the next mailing.
I updated the proposal to expand on one of the ideas (using >> not only as a substitute for std::forward, but also in lambda-capture to decay-copy, adding the ability to capture parameter packs not only by value and reference but also by decay-copy) and narrow another (omitting type-names in lambdas only applies to those introduces with =>).If this seems reasonable and agreeable, I'd like to add it to the next mailing.
[]:(x, ...y) {};
void func_name:(x, ...y) {}; //I'm a template!
I updated the proposal to expand on one of the ideas (using >> not only as a substitute for std::forward, but also in lambda-capture to decay-copy, adding the ability to capture parameter packs not only by value and reference but also by decay-copy) and narrow another (omitting type-names in lambdas only applies to those introduces with =>).If this seems reasonable and agreeable, I'd like to add it to the next mailing.
--
You received this message because you are subscribed to a topic in the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this topic, visit https://groups.google.com/a/isocpp.org/d/topic/std-proposals/uu7mRNXnf8Q/unsubscribe.
To unsubscribe from this group and all its topics, 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/b6280220-06c4-4913-8f50-6a13297655ad%40isocpp.org.
<abbrev_lambda.html>
Hi,Like it, but miss some parens on the rhs of '=>', esp. because of precedence.Just an idea: gcc & clang & to some extend, intel cc already have ({ expr }} which 'has the value of' (definition on this differs per compiler) expr.
If you said, [&](x, y) => ({ x < y }) as the new syntax, you could:- have a distinct paren type,- make it look familiar to those used to statement expressions,
- solve most precedence issues,
- might optionally allow for ({ stmt; expr }) for some statement (perhaps step-by-step)- might optionally allow for ({ return expr; }), which returns to the caller of the context defining the lambda (think of e.g. accumulate() over range of ints via mul: you want to return early if you see a 0 in the input); UB if context is left,
- might optionally allow for break, continue or even throw, <cough>goto/switch in the defining context.
Hmm. I really don't think the part about omitting the types is going to fly, parsing wise. Basing it on the use of `=>` syntax requires look-ahead, and that's an uphill battle. As a possible alternative, consider this:
[]:(x, ...y) {};
[](x, y) => x > y // this proposal
[]@(x, y) => x > y // : or any other identifier before the parameter list as a marking
// to indicate that there's no type on any of the parameters
[](@x, @y) => x > y // some marking on the parameters themselves to indicate that there's
// no type (like : or ^ or %, probably not & or *)
[](&&x, &&y) => x > y // this is actually in the original generic lambda proposal: making
// the type-specifier optional.
Like it, but miss some parens on the rhs of '=>', esp. because of precedence.
Just an idea: gcc & clang & to some extend, intel cc already have ({ expr }} which 'has the value of' (definition on this differs per compiler) expr. If you said, [&](x, y) => ({ x < y }) as the new syntax, you could:
- have a distinct paren type,- make it look familiar to those used to statement expressions,- solve most precedence issues,- might optionally allow for ({ stmt; expr }) for some statement (perhaps step-by-step)
- might optionally allow for ({ return expr; }), which returns to the caller of the context defining the lambda (think of e.g. accumulate() over range of ints via mul: you want to return early if you see a 0 in the input); UB if context is left,- might optionally allow for break, continue or even throw, <cough>goto/switch in the defining context.Thus you could capture continuation replacing statements. All the rest I'd keep the same in the proposal, I really like the idea of simplified lambdas.
Or we can just do away with the entire parenthetical block and specify abbreviated parameters in the square bracket block after some delimiter. For example:
[a, b : c, d] a + b + c + d
Here, a and b are captures, and c and d are arguments.
Cheers,
V.
--
You received this message because you are subscribed to a topic in the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this topic, visit https://groups.google.com/a/isocpp.org/d/topic/std-proposals/uu7mRNXnf8Q/unsubscribe.
To unsubscribe from this group and all its topics, 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/4f050b88-5176-4451-a77f-5cd8925c4f59%40isocpp.org.
[captures...](args...) => expr
// would expand to
[captures...](args...) -> decltype(expr) { return expr; }
[captures...](args...) => { body; }
// would expand to
[captures...](args...) -> decltype(body, void()) { body; }
[captures...](args...) => { s0; s1; s2; }
// would expand to
[captures...](args...) -> decltype(s0, s1, s2, void()) { s0; s1; s2; }
[captures...](args...) => { s0; s1; return s2; }
// would expand to
[captures...](args...) -> decltype(s0, s1, s2) { s0; s1; return s2; }
// With a example `json` variant type whose alternatives are:
// * double
// * string
// * vector<json>
// * map<string, json>
json j = get_json_response();
match(j)([](double x) => { /* ... */ },
[](string x) => { /* ... */ },
[](auto recurse, vector<json> x) => { /* ... */ for(auto& v : x) recurse(v); },
[](auto recurse, map<string, json> x) => { /* ... */ for(auto& [_,v] : x) recurse(v); });
[captures...](args...) => { complex_body; }
// would expand to (with P0315)
[captures...](args...) -> decltype([captures...](Ts... xs){ complex_body; }(std::declval<Ts>(xs)...)) { complex_body; }
Are there any news regarding this proposal? (https://wg21.link/p0573)
I'm really really looking forward to this, as it would not only improve code readability immensely, but also open up opportunities for terse and intuitive syntax.
However, one thing that I think would be very important to add to the proposal is the ability to use compound statements after =>.
As far as I can see, the proposed new syntax only allows:
[captures...](args...) => expr
// would expand to
[captures...](args...) -> decltype(expr) { return expr; }
I propose an extension of that, which would support arbitrary compound statements:
[captures...](args...) => { body; }
// would expand to
[captures...](args...) -> decltype(body, void()) { body; }
In case of multiple statements, multiple `decltype` clauses would be generated:
[captures...](args...) => { s0; s1; s2; }
// would expand to
[captures...](args...) -> decltype(s0, s1, s2, void()) { s0; s1; s2; }
In case of a final return, the generated trailing return type would not have the final `void()`:
[captures...](args...) => { s0; s1; return s2; }
// would expand to
[captures...](args...) -> decltype(s0, s1, s2) { s0; s1; return s2; }
As an example, having SFINAE-friendly lambdas with blocks could allow recursive variant visitation to be implemented as follows:
auto l = [](auto x){ x.foo(); };static_assert(!std::is_invocable<decltype(l), int>::value);
prog.cc:3:24: error: request for member 'foo' in 'x', which is of non-class type 'int' auto l = [](auto x){ x.foo(); }; ~~^~~
auto l = [](auto x) -> decltype(x.foo(), void()) { x.foo(); };static_assert(!std::is_invocable<decltype(l), int>::value);
And this compiles
l(0)
l(0)
On sábado, 25 de março de 2017 09:14:28 PDT Vittorio Romeo wrote:
> Were you referring to the `...` in `args...`?
No. I thought it was a placeholder.
> It does, but the lambda is not generic anymore. You example compiles
> because the error does not occur during instantiation, it occurs during
> overload resolution.
Right, but why does the lambda have to be generic?
> In my example, the
> l(0)
> call expression is valid - it's the instantiation of `l<int>` that is
> invalid.
>
> In your example, the
> l(0)
> call expression is itself invalid - that can be detected by `is_invocable`
> without causing an hard error.
I know.
Please be clear: what's the advantage of using
=> { statements; }
over
{ statements; }
for the same lambda? What can the suggested syntax do that the existing syntax
can't?