
std::vector<X> xs(10); unsigned long n = std::accumulate( xs.begin(), xs.end(), 0UL, _1 + (&_2) ->* &X::count);
This program adds up the "count" members in a vector of X objects. Now, no offense intended, but this is nasty. There's just too much extra syntax required for something so simple, plus you have to make the type into a pointer just so you can dereference it Before I realized I needed to take the address of _2, I had:
unsigned long n = std::accumulate( xs.begin(), xs.end(), 0UL, _1 + _2 ->* &X::count);
which is only slightly better (but incorrect). I realize that I can use bind:
unsigned long n = std::accumulate( xs.begin(), xs.end(), 0UL, _1 + bind(&X::count,_2));
but that's slightly counter-intuitive since the order of the target object and its member pointer are reversed. It'd be nice to allow something like:
unsigned long n = std::accumulate( xs.begin(), xs.end(), 0UL, _1 + _2.at(&X::count));
while if _2 were to refer to a pointer type, we could say:
unsigned long n = std::accumulate( xs.begin(), xs.end(), 0UL, _1 + _2->at(&X::count));
Dave Abrahams
I tried to overload operator->() for a lambda object, but the C++ rule is that it _Must_ return a pointer, in this case to an object which has a member fn "at". So you are toast, because I couldn't figure out how to hold a pointer to a lambda object and recognize it as a delayed object holding a member pointer to vector<X>::at. When we aren't given the information about the member pointer. I'm working on a paper to allow us to overload operator.(), but the last time it was discussed in committee it burned the hair off of nearby dogs and scared children. So I'm waiting until the Redmond meeting so I won't have far to drag my burnt carcass home. Meantime it again highlights that a core language change to allow anonymous fns is needed so this whole mechanism of BLL can go away. // PROPOSED CHANGE IN SOME FUTURE PAPER unsigned long n = std::accumulate( xs.begin(), xs.end(), 0UL, long (long lhs, X const &rhs) // inline fn with no name. { return lhs + rhs.count; } ); Yours, -Gary-

"Powell, Gary" <powellg@amazon.com> wrote in message news:16D81990DBE42642A8CA977A55FF116A0BD397@ex-mail-sea-02.ant.amazon.com...
// PROPOSED CHANGE IN SOME FUTURE PAPER
unsigned long n = std::accumulate( xs.begin(), xs.end(), 0UL, long (long lhs, X const &rhs) // inline fn with no name. { return lhs + rhs.count; } );
Yeah, but do you propose to make local names visible inside the anonymous function, or not? If you do, you open a big can of worms for implementations (recursive nested functions, need I say more?), if you don't, BLL can still do things that anonymous functions can not. Anonymous functors could help some, but their syntax won't be quite as compact as your example above. ...Max...

"Powell, Gary" <powellg@amazon.com> writes:
std::vector<X> xs(10); unsigned long n = std::accumulate( xs.begin(), xs.end(), 0UL, _1 + (&_2) ->* &X::count);
This program adds up the "count" members in a vector of X objects. Now, no offense intended, but this is nasty. There's just too much extra syntax required for something so simple, plus you have to make the type into a pointer just so you can dereference it Before I realized I needed to take the address of _2, I had:
unsigned long n = std::accumulate( xs.begin(), xs.end(), 0UL, _1 + _2 ->* &X::count);
which is only slightly better (but incorrect). I realize that I can use bind:
unsigned long n = std::accumulate( xs.begin(), xs.end(), 0UL, _1 + bind(&X::count,_2));
but that's slightly counter-intuitive since the order of the target object and its member pointer are reversed. It'd be nice to allow something like:
unsigned long n = std::accumulate( xs.begin(), xs.end(), 0UL, _1 + _2.at(&X::count));
while if _2 were to refer to a pointer type, we could say:
unsigned long n = std::accumulate( xs.begin(), xs.end(), 0UL, _1 + _2->at(&X::count));
Dave Abrahams
I tried to overload operator->() for a lambda object, but the C++ rule is that it _Must_ return a pointer,
It can return a proxy whose operator->() returns a proxy whose operator->() ... returns a pointer.
in this case to an object which has a member fn "at".
Right; what's the problem?
So you are toast, because I couldn't figure out how to hold a pointer to a lambda object and recognize it as a delayed object holding a member pointer to vector<X>::at.
I'm not doing vector<X>::at; please re-read the example. Pick another name, say "member": unsigned long n = std::accumulate( xs.begin(), xs.end(), 0UL, _1 + _2->member(&X::count)); We could think of other syntaxes, probably.
I'm working on a paper to allow us to overload operator.(), but the last time it was discussed in committee it burned the hair off of nearby dogs and scared children. So I'm waiting until the Redmond meeting so I won't have far to drag my burnt carcass home.
Meantime it again highlights that a core language change to allow anonymous fns is needed so this whole mechanism of BLL can go away.
Sure. But in the meantime I'd like a slightly sweeter syntax for member access. -- Dave Abrahams Boost Consulting www.boost-consulting.com

Meantime it again highlights that a core language change to allow anonymous fns is needed so this whole mechanism of BLL can go away.
// PROPOSED CHANGE IN SOME FUTURE PAPER
unsigned long n = std::accumulate( xs.begin(), xs.end(), 0UL, long (long lhs, X const &rhs) // inline fn with no name. { return lhs + rhs.count; } );
I was hoping the syntax would be: unsigned long n = std::accumulate( xs.begin(), xs.end(), 0UL,{ return _1 + _2.count; } ); With the option of this when you want different names: unsigned long n = std::accumulate( xs.begin(), xs.end(), 0UL, lambda(lhs,rhs){ return lhs + rhs.count; } ); Incidentally, frustrated with the limits of BLL syntax I've moved to using macros for the most common cases in one of my programs (e.g. for each element in a specific container). I went through and replaced all std::for_each calls, and all for/while loops. The macro looks very much like the syntax above and the code ended up shorter, clearer, and also uncovered some bugs: it turns out I'd written a loop with "i!=c.begin()" instead of "i!=c.end()". The macro always gets that right. Max Motovilov wrote:
Yeah, but do you propose to make local names visible inside the anonymous function, or not? If you do, you open a big can of worms for implementations (recursive nested functions, need I say more?)
I think local names visible inside the anonymous function is the expected behaviour. Can you say more about the potential problems (or point me to a paper/discussion if this is well-discussed somewhere else)? Darren

"Darren Cook" <darren@dcook.org> wrote in message news:404668C7.8010501@dcook.org...
I think local names visible inside the anonymous function is the expected behaviour.
Can you say more about the potential problems (or point me to a paper/discussion if this is well-discussed somewhere else)?
Let's say the inner (anonymous, in this case) function makes recursive calls. Direct recursion may be disallowed for an anonymous function but, since a pointer to it exists somewhere, I don't see a way to prohibit indirect recursion - other than by fiat (i.e. saying that effects of such shall be undefined). Now you have this function creating multiple frames on the stack with its own variables AND accessing the stack frame of its containing function - this, at the very least, means that a pointer to such frame has to be passed in the call to inner function. In the discussed case, the inner function is not called by the containing function directly but by some other code which has no idea whether the pointer passed to it refers to a regular or an anonymous function, hence a complication of the call protocol for everybody. Or, "anonymity/locality" becomes a modifier akin to const and volatile and anonymous functions are no longer usable by non-template code expecting a function pointer (though this may be an acceptable compromise). Come to think of it, local function does not even have to be recursive to cause this problem - since a pointer to it has been passed through [multiple] other functions before dereferencing and call, it (local function) has no way of finding the containing function's stack frame unless it has been passed an explicit pointer to it. [I re-read this paragraph and wished for _1, _2 to complement "it" in English] Hmm.... we can also think of what will happen with nested anonymous functions :-) ...Max...

On Wed, Mar 03, 2004 at 01:34:50PM -0800, Powell, Gary wrote:
unsigned long n = std::accumulate( xs.begin(), xs.end(), 0UL, long (long lhs, X const &rhs) // inline fn with no name. { return lhs + rhs.count; } );
This seems to cast out the type inference present in BLL. I think that's one of the nicer concepts in the lambda library, and a real boon to programming. In my limited experience with SML/OCaml, type inference seems a better, safer, and more convenient way to program, albeit requiring a lot more compiler intelligence. As BLL shows, though, this is quite possible, though perhaps limited, with existing C++. Something like: unsigned long n = std::accumulate( xs.begin(), xs.end(), 0UL, lambda( lhs, rhs ) { return lhs + rhs.count; } ); (Replace 'lambda' with 'function' if you have an aversion to FP nomenclature.) This just reads more easily to me, and you haven't sacrificed type safety. If you need to resolve type ambiguity, that's one thing, but requiring unnecessary type information seems like a loss in functionality from BLL. Cheers! -- Shannon Stewman | Let us walk through the waning night, Caught in a whirlpool, | As dawn-rays tickle our toes, the dew soothes A quartering act: | Our blistered soles, and damp bones stir Solitude or society? | As crimson cracks under the blue-grey sky.

On Wed, Mar 03, 2004 at 01:34:50PM -0800, Powell, Gary wrote:
unsigned long n = std::accumulate( xs.begin(), xs.end(), 0UL, long (long lhs, X const &rhs) // inline fn with no name. { return lhs + rhs.count; } );
This seems to cast out the type inference present in BLL. I think that's one of the nicer concepts in the lambda library, and a real boon to programming. In my limited experience with SML/OCaml, type inference seems a better, safer, and more convenient way to program, albeit requiring a lot more compiler intelligence. As BLL shows,
"Shannon Stewman" <stew@uchicago.edu> wrote in message news:20040304025205.GD832@uchicago.edu... though,
this is quite possible, though perhaps limited, with existing C++.
How about: unsigned long n = std::accumulate( xs.begin(), xs.end(), 0UL, auto(long lhs, const auto& rhs) // inline fn with no name. { return lhs + rhs.count; } ); ? Jonathan

Jonathan Turkanis wrote:
"Shannon Stewman" <stew@uchicago.edu> wrote in message news:20040304025205.GD832@uchicago.edu...
On Wed, Mar 03, 2004 at 01:34:50PM -0800, Powell, Gary wrote:
unsigned long n = std::accumulate( xs.begin(), xs.end(), 0UL, long (long lhs, X const &rhs) // inline fn with no
name.
{ return lhs + rhs.count; } );
This seems to cast out the type inference present in BLL. I think that's one of the nicer concepts in the lambda library, and a real boon to programming. In my limited experience with SML/OCaml, type inference seems a better, safer, and more convenient way to program, albeit requiring a lot more compiler intelligence. As BLL shows,
though,
this is quite possible, though perhaps limited, with existing C++.
How about:
unsigned long n = std::accumulate( xs.begin(), xs.end(), 0UL, auto(long lhs, const auto& rhs) // inline fn with no name. { return lhs + rhs.count; } );
I would prefer also eliding the argument types: unsigned long n = std::accumulate( xs.begin(), xs.end(), 0UL, lambda(lhs, rhs) { return lhs + rhs.count; } ); Most of the time, you also do not know the type of the arguments before hand. -- Joel de Guzman http://www.boost-consulting.com http://spirit.sf.net

"Joel de Guzman" <joel@boost-consulting.com> wrote in message news:4046A2AE.8020408@boost-consulting.com...
Jonathan Turkanis wrote:
How about:
unsigned long n = std::accumulate( xs.begin(), xs.end(), 0UL, auto(long lhs, const auto& rhs) // inline fn
with no
name. { return lhs + rhs.count; } );
I would prefer also eliding the argument types:
unsigned long n = std::accumulate( xs.begin(), xs.end(), 0UL, lambda(lhs, rhs) { return lhs + rhs.count; } );
Most of the time, you also do not know the type of the arguments before hand.
That's why I used 'auto'. (Maybe I should have used it for the first argument position, too.) Isn't it useful to have a way to specify whether the argument is to be passed by value, reference, const reference, etc. ? Jonathan

On Wed, 3 Mar 2004, Jonathan Turkanis wrote:
"Joel de Guzman" <joel@boost-consulting.com> wrote in message news:4046A2AE.8020408@boost-consulting.com...
Jonathan Turkanis wrote:
How about:
unsigned long n = std::accumulate( xs.begin(), xs.end(), 0UL, auto(long lhs, const auto& rhs) // inline fn
with no
name. { return lhs + rhs.count; } );
I would prefer also eliding the argument types:
unsigned long n = std::accumulate( xs.begin(), xs.end(), 0UL, lambda(lhs, rhs) { return lhs + rhs.count; } );
Most of the time, you also do not know the type of the arguments before hand.
That's why I used 'auto'. (Maybe I should have used it for the first argument position, too.)
Isn't it useful to have a way to specify whether the argument is to be passed by value, reference, const reference, etc. ?
This has been brought up in the standards committee (use of auto as a parameter type defines a template implicitly), not for lambdas but for normal named functions. Implicit templates would provide a fairly compact and well defined syntax for lambdas, but the response to such implicit templates in the standards committee was mild. Lambdas have more serious problems to be solved first, though, already being discussed elsewhere in this thread. BLL lambdas are unsafe in many ways, e.g. references to free variables can become dangling. This does not occur often in practice, as BLL lambdas are seldom stored anywhere, and are gone after the evaluation of the enclosing full expression anyway. But we can encounter problems with BLL too: int* i = new int; *i = 1; function<int,int> f = (*i += _1); delete i; f(1); // *i is gone here Similar problems occur if lambdas are returned from functions. The question then becomes, is this kind of unsafety acceptable for a built-in lambda facility, or should the compiler support closures, which would require much more than just a convenient syntax for creating function objects on the fly. Jaakko

"Jaakko Jarvi" <jajarvi@cs.indiana.edu> wrote in message news:Pine.LNX.4.58.0403032341250.581@eddie.osl.iu.edu...
On Wed, 3 Mar 2004, Jonathan Turkanis wrote:
I would prefer also eliding the argument types:
unsigned long n = std::accumulate( xs.begin(), xs.end(), 0UL, lambda(lhs, rhs) { return lhs + rhs.count; } );
Most of the time, you also do not know the type of the arguments before hand.
That's why I used 'auto'. (Maybe I should have used it for the first argument position, too.)
Isn't it useful to have a way to specify whether the argument is to be passed by value, reference, const reference, etc. ?
This has been brought up in the standards committee (use of auto as a parameter type defines a template implicitly), not for lambdas but for normal named functions.
I know about this; I was attempting a minor unification of proposed extensions.
Implicit templates would provide a fairly compact and well defined syntax for lambdas, but the response to such implicit templates in the standards committee was mild.
That was my response too, when I first saw it. Was the response to using 'auto' as a return type more positive?
Lambdas have more serious problems to be solved first, though, already being discussed elsewhere in this thread. BLL lambdas are unsafe in many ways, e.g. references to free variables can become dangling. This does not occur often in practice, as BLL lambdas are seldom stored anywhere, and are gone after the evaluation of the enclosing full expression anyway.
I think lambda's are rarely stored because it's hard calculate their types. This could raise problems if yet another proposed use of auto is accepted: auto x = [complex lambda expression];
Similar problems occur if lambdas are returned from functions.
Which would be easy to do with 'auto' return types. Looks like there are a lot of problems lurking in this neck of the woods. Jonathan

Implicit templates would provide a fairly compact and well defined syntax for lambdas, but the response to such implicit templates in the standards committee was mild.
That was my response too, when I first saw it. Was the response to using 'auto' as a return type more positive?
Yes. That's still on the agenda. Jaakko

"Jonathan Turkanis" <technews@kangaroologic.com> writes: | > Lambdas have more serious problems to be solved first, though, | > already being discussed elsewhere in this thread. | > BLL lambdas are unsafe in many ways, e.g. references | > to free variables can become dangling. This does not occur often in | > practice, as BLL lambdas are seldom stored anywhere, and are gone | after | > the evaluation of the enclosing full expression anyway. | | I think lambda's are rarely stored because it's hard calculate their | types. This could raise problems if yet another proposed use of auto | is accepted: | | auto x = [complex lambda expression]; Yes, and that should be anticipated. People have already been storing function objects (in the traditional C++ sense), there is no reason to believe they would not attempt the same thing with lambdas. -- Gaby

"Gabriel Dos Reis" <gdr@integrable-solutions.net> wrote in message news:m365dgdlm3.fsf@uniton.integrable-solutions.net...
Yes, and that should be anticipated. People have already been storing function objects (in the traditional C++ sense), there is no reason to believe they would not attempt the same thing with lambdas.
Consider struct X { auto x; }; how can we know the size of X? Should an initialization be required, eg struct X { auto x = ...; }; ? br Thorsten

"Thorsten Ottosen" <nesotto@cs.auc.dk> writes: | "Gabriel Dos Reis" <gdr@integrable-solutions.net> wrote in message | news:m365dgdlm3.fsf@uniton.integrable-solutions.net... | | > Yes, and that should be anticipated. People have already been storing | > function objects (in the traditional C++ sense), there is no reason to | > believe they would not attempt the same thing with lambdas. | | Consider | | struct X | { | auto x; | }; | | how can we know the size of X? That is not supposed to be valid C++, regardless of lambdas, so I don't quite understand your question. However, you cn have this: template<class T> struct foo_helper { T data; }; template<class T> foo_helper<T> foo(const T& x) { .... } auto x = foo(...); -- Gaby

"Gabriel Dos Reis" <gdr@integrable-solutions.net> wrote in message news:m3ptbn7anv.fsf@uniton.integrable-solutions.net...
"Thorsten Ottosen" <nesotto@cs.auc.dk> writes: [sinp] | Consider | | struct X | { | auto x; | }; | | how can we know the size of X?
That is not supposed to be valid C++, regardless of lambdas, so I don't quite understand your question.
ok, I just understood that people would want to store these lambda expression. So why not as members too? br Thorsten

"Thorsten Ottosen" <nesotto@cs.auc.dk> writes: | "Gabriel Dos Reis" <gdr@integrable-solutions.net> wrote in message | news:m3ptbn7anv.fsf@uniton.integrable-solutions.net... | > "Thorsten Ottosen" <nesotto@cs.auc.dk> writes: | [sinp] | > | Consider | > | | > | struct X | > | { | > | auto x; | > | }; | > | | > | how can we know the size of X? | > | > That is not supposed to be valid C++, regardless of lambdas, so I don't | > quite understand your question. | | ok, I just understood that people would want to store these lambda | expression. So why not | as members too? Yes, they may do so as showed in the previous message. But, there is no such things as "auto x" for data member declaration. You may, however, have the "auto" paremeter as a class template paremeter which you can in turn use to declare the type of "x". -- Gaby

On Thu, Mar 04, 2004 at 12:03:25AM -0500, Jaakko Jarvi wrote:
int* i = new int;
*i = 1;
function<int,int> f = (*i += _1);
delete i;
f(1); // *i is gone here
Similar problems occur if lambdas are returned from functions.
The question then becomes, is this kind of unsafety acceptable for a built-in lambda facility, or should the compiler support closures, which would require much more than just a convenient syntax for creating function objects on the fly.
How is this worse than: static int* i( new int(2378) ); typedef (void *)(void) void_fxn; void something(void) { *i += 5; } void_fxn return_something(void) { *i = 0; return &something; } void exercise_something(void) { void_fxn fxn = return_something(); delete i; fxn(); } Admittedly this feels a bit contrived, but errors like this happen not too inrequently in C/C++. Take the contrived but quite legal case: void bad_function(void) { int* i1 = new int; *i1 = 0; delete i1; *i1 = 2378; // shouldn't be legal } When you play with explicit heap management, you're responsible for making sure the object's lifetime is longer than its references (or pointers). If you can do this with named functions and function pointers, what's the objection to creating a similar problem in lambda functions? Best, -- Shannon Stewman | Let us walk through the waning night, Caught in a whirlpool, | As dawn-rays tickle our toes, the dew soothes A quartering act: | Our blistered soles, and damp bones stir Solitude or society? | As crimson cracks under the blue-grey sky.

On Thu, 4 Mar 2004, Shannon Stewman wrote:
How is this worse than:
static int* i( new int(2378) );
typedef (void *)(void) void_fxn;
void something(void) { *i += 5; }
void_fxn return_something(void) { *i = 0; return &something; }
void exercise_something(void) { void_fxn fxn = return_something(); delete i; fxn(); }
Admittedly this feels a bit contrived, but errors like this happen not too inrequently in C/C++. Take the contrived but quite legal case:
void bad_function(void) { int* i1 = new int; *i1 = 0;
delete i1;
*i1 = 2378; // shouldn't be legal }
When you play with explicit heap management, you're responsible for making sure the object's lifetime is longer than its references (or pointers). If you can do this with named functions and function pointers, what's the objection to creating a similar problem in lambda functions?
My example was a bit contrived too. Here's another example (with imaginary syntax): auto foo() { int i; return auto(int x) { return i + x; } } auto x = foo(); x(1); // is i still there? So my point was that equipped with more (seemingly easy) tools, erroneous code may be more likely. We can get similar effects in the language now, but one must work harder to stumble into them. In my view, the choices for dealing with lambdas in C++ are: 1. Not care about the dangers. With power comes responsibility. 2. Restrict lambdas enough to make them safer but less powerful - Cannot store lambdas in data structures - Cannot return lambdas from functions 3. Add real closures to C++ - E.g. force all variables that occur free in lambdas to be allocated on the heap (and add some kind of garbage collection for those ...) Of these: 1. May very well be the choice for C++, it is in the spirit of the language :) 2. Might be a compromise that would still be useful. (you could e.g. pass a lambda to an STL algorithm) 3. seems hard. Best, Jaakko

Jaakko Jarvi <jajarvi@cs.indiana.edu> writes: | My example was a bit contrived too. | Here's another example (with imaginary syntax): | | auto foo() { | int i; | return auto(int x) { return i + x; } | } Is it different from int& f() { int i; return i; } ? In -a- model I discussed a while ago with the second author of decltype/auto proposal, lambdas are unnamed constants that can be implemented by local classes. The body of the lambda would be forwarded-to by the appropriate overload of the call operator, i.e. your example would be foo: () -> auto { int i; struct xxx { operator(): (int x) -> auto { return i + x; } }; }; That is a very simple model that just provides a syntactic sugar (modulo use of auto in return type) for what you can do in current C++. In particular, it is clear that the reference to "i" is a hard error by application of current rules governing local classes. I do not believe in local variables that silently escape their lexical scopes. If you really want your automatic variables to espace, you already have ways to say so; but I don't think they should be the common cases that should be "optimized for". Note that I want the lambdas to be constants, in particular I would like to be able to use them as template arguments. Note also the type inference that can refer to local classes :-) -- Gaby

Gabriel Dos Reis <gdr@integrable-solutions.net> writes:
In -a- model I discussed a while ago with the second author of decltype/auto proposal, lambdas are unnamed constants that can be implemented by local classes. The body of the lambda would be forwarded-to by the appropriate overload of the call operator, i.e. your example would be
foo: () -> auto { int i;
struct xxx { operator(): (int x) -> auto { return i + x; } }; };
Is foo missing a return statement?
That is a very simple model that just provides a syntactic sugar (modulo use of auto in return type) for what you can do in current C++. In particular, it is clear that the reference to "i" is a hard error by application of current rules governing local classes.
I do not believe in local variables that silently escape their lexical scopes. If you really want your automatic variables to espace, you already have ways to say so; but I don't think they should be the common cases that should be "optimized for".
Note that I want the lambdas to be constants, in particular I would like to be able to use them as template arguments. Note also the type inference that can refer to local classes :-)
Sounds lovely ;-) -- Dave Abrahams Boost Consulting www.boost-consulting.com

David Abrahams <dave@boost-consulting.com> writes: | Gabriel Dos Reis <gdr@integrable-solutions.net> writes: | | > In -a- model I discussed a while ago with the second author of | > decltype/auto proposal, lambdas are unnamed constants that can be | > implemented by local classes. The body of the lambda would be | > forwarded-to by the appropriate overload of the call operator, | > i.e. your example would be | > | > foo: () -> auto { | > int i; | > | > struct xxx { | > operator(): (int x) -> auto { return i + x; } | > }; | > }; | | Is foo missing a return statement? Yes: It is missing return xxx(); Thanks! [...] | > Note that I want the lambdas to be constants, in particular I would | > like to be able to use them as template arguments. Note also the type | > inference that can refer to local classes :-) | | Sounds lovely ;-) :-) How else would I have my expression templates without turning C++ into Haskell? ;-) -- Gaby

On Sun, 8 Mar 2004, Gabriel Dos Reis wrote:
Jaakko Jarvi <jajarvi@cs.indiana.edu> writes:
| My example was a bit contrived too. | Here's another example (with imaginary syntax): | | auto foo() { | int i; | return auto(int x) { return i + x; } | }
Is it different from
int& f() { int i; return i; }
?
In -a- model I discussed a while ago with the second author of decltype/auto proposal, lambdas are unnamed constants that can be implemented by local classes. The body of the lambda would be forwarded-to by the appropriate overload of the call operator, i.e. your example would be
foo: () -> auto { int i;
struct xxx { operator(): (int x) -> auto { return i + x; } }; };
That is a very simple model that just provides a syntactic sugar (modulo use of auto in return type) for what you can do in current C++. In particular, it is clear that the reference to "i" is a hard error by application of current rules governing local classes.
Right, variables with local storage cannot be used in local classes. Now this rule would forbid the above (which is good), but it would also rule out useful and safe lambdas: auto foo(int x, int y) { int i = x+y; // just some computation to motivate a local variable vector<int> v; ... transform(v.begin(), v.end(), v.begin(), auto(int x) { return x+i; }); } Which you can currently write using BLL as: transform(v.begin(), v.end(), v.begin(), _1 + i); (BLL stores a copy to i internally) Jaakko

Jaakko Jarvi <jajarvi@cs.indiana.edu> writes: | On Sun, 8 Mar 2004, Gabriel Dos Reis wrote: | | > Jaakko Jarvi <jajarvi@cs.indiana.edu> writes: | > | > | My example was a bit contrived too. | > | Here's another example (with imaginary syntax): | > | | > | auto foo() { | > | int i; | > | return auto(int x) { return i + x; } | > | } | > | > Is it different from | > | > int& f() { int i; return i; } | > | > ? | > | > | > In -a- model I discussed a while ago with the second author of | > decltype/auto proposal, lambdas are unnamed constants that can be | > implemented by local classes. The body of the lambda would be | > forwarded-to by the appropriate overload of the call operator, | > i.e. your example would be | > | > foo: () -> auto { | > int i; | > | > struct xxx { | > operator(): (int x) -> auto { return i + x; } | > }; | > }; | > | > That is a very simple model that just provides a syntactic sugar | > (modulo use of auto in return type) for what you can do in current | > C++. In particular, it is clear that the reference to "i" is a hard | > error by application of current rules governing local classes. | | Right, variables with local storage cannot be used in local classes. | Now this rule would forbid the above (which is good), but it would also | rule out useful and safe lambdas: Those who are considered safe would have markers sayign they are safe to export, or they would have markers saying only their values are needed. | | auto foo(int x, int y) { | int i = x+y; // just some computation to motivate a local variable | vector<int> v; | ... | transform(v.begin(), v.end(), v.begin(), | auto(int x) { return x+i; }); Hmm, I do not see the difference between the "i" here and the "i" above. They are both automatic. But, if you want to tell the compiler that it is OK to use the value of a local variable then use lambda lifting, e.g. transform(v.begin(), v.vend(), v.begin() (auto x) ((int y) auto { return y + x; }) (i)); which has the same effect as struct xxx { const int x; xxx(int i) : x(i) { } operator() const: (int y) auto { return y + x; } }; transform(v.begin(), v.end(), v.begin(), xxx(i)); If you wanted to use the storage, int accu = 0; for_each(v.begin(), v.end(), (auto& s) ((int x) void { s += x; }) (accu)); which has the same effect as: struct xxxx { int& s; xxx(int& s) : s(s) { } operator(): (int x) void { s += x; } }; for_each(v.begin(), v.end(), xxxx(accu)); | Which you can currently write using BLL as: | | transform(v.begin(), v.end(), v.begin(), _1 + i); | | (BLL stores a copy to i internally) what about struct stream { // ... int send(message); private: stream(const stream&); // not implemented, prevent copy stream& operator=(const stream&); // ditto; }; stream out("127.0.0.1:6001"); // ... for_each(v.begin(), v.end(), out.send(message(_1))); ? Does it copy out? The usual pass-by-value semantics would be still possible, modulo explicit annotation. Plus, you would have the choice to say explicitly const ref vs. plain copy vs. plain ref. In the simple-minded model I discussed, the above would be stream out(127.0.0.1:6001"); // ... for_each(v.begin(), v.end(), (stream& s) ((int x) { s.send(x); }) (out)); -- Gaby

On Mar 8, 2004, at 11:40 AM, Gabriel Dos Reis wrote:
Jaakko Jarvi <jajarvi@cs.indiana.edu> writes:
| On Sun, 8 Mar 2004, Gabriel Dos Reis wrote: | | > Jaakko Jarvi <jajarvi@cs.indiana.edu> writes: | > | | Right, variables with local storage cannot be used in local classes. | Now this rule would forbid the above (which is good), but it would also | rule out useful and safe lambdas:
Those who are considered safe would have markers sayign they are safe to export, or they would have markers saying only their values are needed.
| | auto foo(int x, int y) { | int i = x+y; // just some computation to motivate a local variable | vector<int> v; | ... | transform(v.begin(), v.end(), v.begin(), | auto(int x) { return x+i; });
Hmm, I do not see the difference between the "i" here and the "i" above. They are both automatic.
But, if you want to tell the compiler that it is OK to use the value of a local variable then use lambda lifting, e.g.
transform(v.begin(), v.vend(), v.begin() (auto x) ((int y) auto { return y + x; }) (i));
Yes, but this is a quite complex and hard to read way to express something simpler. And it seems that in the inner lambda, x would be a variable from an outer scope with automatic storage, just like i is, and would therefore have to be rejected by the current local class rules. <snip corresponding function object classes> Yes, I think all the building blocks are there, function object classes can be used as the implementation for lambdas, but the rules of referring to automatic storage variables would need to be more permissive than the ones of local classes.
| Which you can currently write using BLL as: | | transform(v.begin(), v.end(), v.begin(), _1 + i); | | (BLL stores a copy to i internally)
what about
struct stream { // ... int send(message); private: stream(const stream&); // not implemented, prevent copy stream& operator=(const stream&); // ditto; };
stream out("127.0.0.1:6001"); // ... for_each(v.begin(), v.end(), out.send(message(_1)));
? Does it copy out?
One would have to write this as: for_each(v.begin(), v.end(), bind(&stream::send, ref(out), _1)); The important point is 'ref', which instructs to store a reference, not a copy. The default is 'store a copy' (except for in some contexts, such as on the left of += operator). Cheers, Jaakko
The usual pass-by-value semantics would be still possible, modulo explicit annotation. Plus, you would have the choice to say explicitly const ref vs. plain copy vs. plain ref.
In the simple-minded model I discussed, the above would be
stream out(127.0.0.1:6001"); // ... for_each(v.begin(), v.end(), (stream& s) ((int x) { s.send(x); }) (out));
-- Gaby _______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
-- Jaakko Järvi email: jajarvi@cs.indiana.edu -- Post Doctoral Fellow phone: +1 (812) 855-3608 -- Pervasive Technology Labs fax: +1 (812) 855-4829 -- Indiana University, Bloomington

Jaakko Jarvi <jajarvi@cs.indiana.edu> writes: | On Mar 8, 2004, at 11:40 AM, Gabriel Dos Reis wrote: | | > Jaakko Jarvi <jajarvi@cs.indiana.edu> writes: | > | > | On Sun, 8 Mar 2004, Gabriel Dos Reis wrote: | > | | > | > Jaakko Jarvi <jajarvi@cs.indiana.edu> writes: | > | > | > | | > | Right, variables with local storage cannot be used in local classes. | > | Now this rule would forbid the above (which is good), but it would | > also | > | rule out useful and safe lambdas: | > | > Those who are considered safe would have markers sayign they are safe | > to export, or they would have markers saying only their values are | > needed. | > | > | | > | auto foo(int x, int y) { | > | int i = x+y; // just some computation to motivate a local | > variable | > | vector<int> v; | > | ... | > | transform(v.begin(), v.end(), v.begin(), | > | auto(int x) { return x+i; }); | > | > Hmm, I do not see the difference between the "i" here and the "i" | > above. They are both automatic. | > | > But, if you want to tell the compiler that it is OK to use the value | > of a local variable then use lambda lifting, e.g. | > | > transform(v.begin(), v.vend(), v.begin() | > (auto x) ((int y) auto { return y + x; }) (i)); | > | | | Yes, but this is a quite complex and hard to read way to express | something simpler. Escaping variables are no simple :-) I doubt there is a simple criteria that says which ones are "good" and which ones are "bad", and those may depend on the context, thus let the user labels the good ones :-) It is possible to have a design that makes copy by default, but it handles only a very limited set (those for which functions/operations are expected to take values) at the cost of penalizing the reference semantics[1]. In a language like C++ where values/references look like the same syntactically but leak differently, that opens door for traps more often than needed. Yes, one can advance the argument that the situation already exists (see recent min/max thread); but do we need more ropes, especially when we want to talk people into using the feature? I believe that we should first optimize for the most simple common cases. Release the product, and extend later. Lambdas are handy and fun to program with; I'm less convinced that we would succeed in evangelizing C++ programmers into lambdas in the next 10-15 years, where traditional, well-developed, functional programming languages have failed for half a decade. Well, before this message provokes a deluge of mails, I would probably point out that I'm a big fan of lambdas; but realistically I don't see C++ brainwashing armies of programmers into lambdas. This lambda-conversion, if it were to happen, need not be done in a one single big-step. Just think about the perception of lambdas before the STL and after, say, 1998. If you optimize for the most common simple cases, then people will use it, get adicted and want more. That is essentially what STL did. But, I see we are in violent agreement on one fundamental point: lambdas in C++ should be bound to lexical scopes (e.g. implemented by local classes). That provides opportunities for efficiency and they can be effectively optimized (with the hope that compilers understand the semantics of scopes, which some of them do). If you let the possibility to escape by default, compilers would have more difficulty optimizing and would provide less effective support. This has been done before. The experience was not that conclusive[2]. | And it seems that in the inner lambda, x would be a variable from an | outer scope | with automatic storage, just like i is, and would therefore have to be | rejected | by the current local class rules. Hmm, I don't get that one. "x" in the inner lambda is the "x" bound by the outer (auto x). Therefore it is not rejected. Or am I being dense? Footnotes: [1] Simple constructs like struct foo { int bar() { return 666; } }; template<class T> int apply(foo* p, T f) { return (p->*f)(); } int main() { foo t[5]; for (int i = 0; i < 5; ++i) apply(t + i, &foo::bar); } are not effectively optimized by (common) compilers (especially, indirect calls through pointer to members or pointer to functions). Yes, in the abstract they can (and should) do better. But, in practice, few do correctly. [2] Lisp. -- Gaby

On Mar 8, 2004, at 5:45 PM, Gabriel Dos Reis wrote:
Jaakko Jarvi <jajarvi@cs.indiana.edu> writes: Well, before this message provokes a deluge of mails, I would probably point out that I'm a big fan of lambdas; but realistically I don't see C++ brainwashing armies of programmers into lambdas. This lambda-conversion, if it were to happen, need not be done in a one single big-step. Just think about the perception of lambdas before the STL and after, say, 1998. If you optimize for the most common simple cases, then people will use it, get adicted and want more. That is essentially what STL did. But, I see we are in violent agreement on one fundamental point: lambdas in C++ should be bound to lexical scopes (e.g. implemented by local classes). That provides opportunities for efficiency and they can be effectively optimized (with the hope that compilers understand the semantics of scopes, which some of them do). If you let the possibility to escape by default, compilers would have more difficulty optimizing and would provide less effective support. This has been done before. The experience was not that conclusive[2].
Yes, we're in agreement. We just have to define the 'most common simple case' :) :) Seriously, with built-in lambdas we should be able to provide something that is more, or at least as, convenient than BLL or bind, and having to do things like lambda lifting for cases where BLL and bind would offer a straightforward solution seems unsatisfactory. So I'm just wondering whether there is something in between the "no reference to outside scopes" and full closures, which would hit the sweet-spot for C++ better. Some visible client side syntax would be fine, and probably good, to explicitly state what to do with such references (copy, create a reference, ...)
| And it seems that in the inner lambda, x would be a variable from an | outer scope | with automatic storage, just like i is, and would therefore have to be | rejected | by the current local class rules.
Hmm, I don't get that one. "x" in the inner lambda is the "x" bound by the outer (auto x). Therefore it is not rejected.
From the point of view of the inner lambda, that is "defined in outer scope". Consider the following. Here's the same thing simplified even more (and with more conventional lambda syntax for clarity, not to be considered as a suggestion for C++). void foo(int x) { ... lambda a. x; // rejected, referring to x which is defined in an outer scope } void foo(int x) { (lambda a. (lambda b. a)) (x); // now x is ok, but if you convert (lambda b. a) to a // local class, then a is a reference to variable with automatic // storage, and would be rejected } Dave's going to throw us to std.c++.moderated or elsewhere soon, so we may better take this off-line soon :) Best, Jaakko

So I'm just wondering whether there is something in between the "no reference to outside scopes" and full closures, which would hit the sweet-spot for C++ better.
Some visible client side syntax would be fine, and probably good, to explicitly state what to do with such references (copy, create a reference, ...)
I've always been of the opinion that if you allowed the lambdas to curry, then you could pass in whatever outer level variables you need at the time of declaration and it would return back a function object representing the rest of the parameters needed. For instance: // loop through vec adding val to each of the elements int func(std::vector<int>& vec, int val) { std::for_each( arr.begin(), arr.end(), lambda (int v, int& elem ) { elem += v; }( val ) ); } In the above example, the lambda takes two arguments, an int and an int reference. Then, we call the lambda with only one argument (the int parameter). This returns a new lambda that only takes one parameter (an int reference). With this syntax, you could explicitly pass in the outer level variables that you wanted to use and it is consistent with other functional programming languages. Also, this does not carry the overhead associated with closures or dynamic behavior or anything of the sort. Thanks! Tanton

"Tanton Gibbs" <thgibbs@deltafarms.com> writes: | > So I'm just wondering whether there is something in between the "no | > reference to outside scopes" and full closures, which would hit the | > sweet-spot for C++ better. | > | > Some visible client side syntax would be fine, and probably good, | > to explicitly state what to do with such references (copy, create | > a reference, ...) | | I've always been of the opinion that if you allowed the lambdas to curry, | then you could pass in whatever outer level variables you need at the time | of declaration and it would return back a function object representing the | rest of the parameters needed. | | For instance: | | // loop through vec adding val to each of the elements | int func(std::vector<int>& vec, int val) { | std::for_each( arr.begin(), arr.end(), | lambda (int v, int& elem ) { | elem += v; | }( val ) ); | } | | In the above example, the lambda takes two arguments, an int and an int | reference. Then, we call the lambda with only one argument (the int | parameter). This returns a new lambda that only takes one parameter (an int | reference). With this syntax, you could explicitly pass in the outer level | variables that you wanted to use and it is consistent with other functional | programming languages. Also, this does not carry the overhead associated | with closures or dynamic behavior or anything of the sort. Yes, that is the stanadard version of lambda lifting. OK, let's take this to private mails with Jaakko and come back when we feel we have something :-) -- Gaby

| > transform(v.begin(), v.vend(), v.begin() | > (auto x) ((int y) auto { return y + x; }) (i)); | > | | | Yes, but this is a quite complex and hard to read way to express | something simpler.
Yeah, the corresponding lambda in Python is: labmbda x,y: x+y It'd be nice if we could get to something that simple. -- Dave Abrahams Boost Consulting www.boost-consulting.com

David Abrahams <dave@boost-consulting.com> writes: | > | > transform(v.begin(), v.vend(), v.begin() | > | > (auto x) ((int y) auto { return y + x; }) (i)); | > | > | > | | > | | > | Yes, but this is a quite complex and hard to read way to express | > | something simpler. | | Yeah, the corresponding lambda in Python is: | | labmbda x,y: x+y | | It'd be nice if we could get to something that simple. Yes, and it is one the simplest common cases. The correspnding formulation in C++ would be as simple as well, e.g. (auto x, auto y) { x + y; } (three months ago, Bjarne showed another candidate: { x, y; | x + y; }) The main issue in this discussion, however, concerns what to do when a reference is made to an automatic variable in an enclosing scope. -- Gaby

On Tue, Mar 09, 2004 at 03:32:02PM +0100, Gabriel Dos Reis wrote:
David Abrahams <dave@boost-consulting.com> writes:
<snip>
| labmbda x,y: x+y | | It'd be nice if we could get to something that simple.
Yes, and it is one the simplest common cases. The correspnding formulation in C++ would be as simple as well, e.g.
(auto x, auto y) { x + y; }
(three months ago, Bjarne showed another candidate: { x, y; | x + y; })
The main issue in this discussion, however, concerns what to do when a reference is made to an automatic variable in an enclosing scope.
Older versions of python do this: z = 155 fxn = lambda x, y=z: x+y fxn(2) # returns 157 In seem to remember that newer versions have a form of closuring. How about: int z = 155; (auto x, auto y=z) { x + y; } It's not the greatest syntax, and it's often pretty annoying, but I think it's a tad more readable than the proposed. Best, -- Shannon Stewman | Let us walk through the waning night, Caught in a whirlpool, | As dawn-rays tickle our toes, the dew soothes A quartering act: | Our blistered soles, and damp bones stir Solitude or society? | As crimson cracks under the blue-grey sky.

Gabriel Dos Reis wrote:
Jaakko Jarvi <jajarvi@cs.indiana.edu> writes:
My example was a bit contrived too. Here's another example (with imaginary syntax):
auto foo() { int i; return auto(int x) { return i + x; } }
Is it different from
int& f() { int i; return i; }
?
Yes, it's a bit different, since the reference return is explicit. I can choose between "int & f()" and "int f()" and the semantics are fairly straightforward. In a lambda, bind by value and bind by reference are equally useful, and we should have a (straightforward) way to choose between them. And it seems to be that the default should be by value since it's safe, and by-ref should probably be designated by the usual ref(i) construct (*&i is also an option, but it's somewhat of a wart).

"Peter Dimov" <pdimov@mmltd.net> writes: | Gabriel Dos Reis wrote: | > Jaakko Jarvi <jajarvi@cs.indiana.edu> writes: | > | >> My example was a bit contrived too. | >> Here's another example (with imaginary syntax): | >> | >> auto foo() { | >> int i; | >> return auto(int x) { return i + x; } | >> } | > | > Is it different from | > | > int& f() { int i; return i; } | > | > ? | | Yes, it's a bit different, since the reference return is explicit. I can I'm afraid, that changes nothing. From existing experiences. | choose between "int & f()" and "int f()" and the semantics are fairly | straightforward. If you think it is just a matter of ref, then about the value-based version int* f() { int i; return &i; } There is no bit of difference, actually. Unfortunately, it happens with people not noticing. [ That is how we get books that teach people not doing it :-) ] -- Gaby

Jaakko Jarvi <jajarvi@cs.indiana.edu> writes: | > Isn't it useful to have a way to specify whether the argument is to be | > passed by value, reference, const reference, etc. ? | | This has been brought up in the standards committee | (use of auto as a parameter type defines a template implicitly), | not for lambdas but for normal named functions. You're right that is not part of any formal proposal but if you ask the other co-author of decltype/auto proposal, he would tell you that he had been playing with that idea for sometime now :-) (Since at least the first version of that proposal :-)) As I commented last week, I would like to see the proposal to go a step further and allows type inference that can refer to local classes, e.g: operator*: (const auto& x, const auto& y) -> auto { using lhs_type = decltype(x); using rhs_type = decltype(y); struct dot { lhs_type lhs; rhs_type rhs; dot(lhs_type l, rhs_type r) : lhs(l), rhs(r) { } // evaluation on demand operator(): (auto x) const -> auto { return lhs(x) * rhs(x); } }; return dot(x, y); } (It would be even better if we could get "concepts", but that is a different story). E.g. I would like to see less needs for clumsy/brittle constructs needed to support expression templates that some domain specific libraries seem to require. -- Gaby

Shannon Stewman wrote:
On Wed, Mar 03, 2004 at 01:34:50PM -0800, Powell, Gary wrote:
unsigned long n = std::accumulate( xs.begin(), xs.end(), 0UL, long (long lhs, X const &rhs) // inline fn with no name. { return lhs + rhs.count; } );
This seems to cast out the type inference present in BLL. I think that's one of the nicer concepts in the lambda library, and a real boon to programming. In my limited experience with SML/OCaml, type inference seems a better, safer, and more convenient way to program, albeit requiring a lot more compiler intelligence. As BLL shows, though, this is quite possible, though perhaps limited, with existing C++.
I agree. In many cases, type inference is a necessity since you do not know the type of the arguments before hand. Take a simple Spirit parser, for example: std::string s; identifier = (+alpha_p) [ ref(s) = construct_<std::string>(arg1, arg2) ] ; At grammar declaration time, we don't (can't) know the exact type of arg1 and arg2. We simply know that they are iterators. Cheers, -- Joel de Guzman http://www.boost-consulting.com http://spirit.sf.net
participants (12)
-
Darren Cook
-
David Abrahams
-
Gabriel Dos Reis
-
Jaakko Jarvi
-
Joel de Guzman
-
Jonathan Turkanis
-
Max Motovilov
-
Peter Dimov
-
Powell, Gary
-
Shannon Stewman
-
Tanton Gibbs
-
Thorsten Ottosen