lambda non-parameter variable binding

39 views
Skip to first unread message

Andy Glew

unread,
Dec 3, 1999, 3:00:00 AM12/3/99
to

Brief:
====

In previous posts I waffled on whether lambda functions should be provided.
This exercise has led me to what I believe is a much more satisfactory syntax
proposal for lambda functors, one that allows all forms of binding to be
supported, and which extends GNU C++'s statement expressions.

A short example:

for(int i=0;i != N;i++) {
foo(
({
struct lambda : public functor_t {
int& i = enclosing_scope::i;
void operator() () const { cout << "hello from inside derived functor " << i << endl; }
} lambda;
})
);
}

Detail:
=====

(Discussion placed on web page http://www.cs.wisc.edu/~glew/programming-languages.html)


I have long been an advocate of lambda functions for general purpose
programming languages like C and Pascal. Many years ago I wrote a preprocessor
that took lambda function definitions and converted them into regular functions.

However, I now slightly back off from this.

Experience shows that lambda functions are insufficient; what is required, in
most cases, are lambda functors. "Functor" in the usual C++ sense: an object
that carries state around, and a function invoked on this object, with other parameters.
Moreover, how non-parameter values used in the lambda function text are bound is
an open issue: should they be bound as values, or as references to variables in
the local scope where the lambda is defined? Both are useful: value binding
allows the lambda to transcend the local scope, reference binding allows techniques
similar to the infamous Jensen's device to be used.

In general, therefore, a functor object must be defined, with data members bound
as desired, for the lambda function. In conventional C and Pascal this was an
onerous task, involving a definition far away from use. Separating use from definition
is bad, if it can be avoided. However, in C++ classes can be defined almost anywhere:
e.g. the line before the function or template invocation that the lambda functor should
be passed to. You can get even better if you use GNU C++'s statement expression syntax:
below is an example of a pseudo-lambda functor definition, using a statement expression,
right in function foo's argument list:

foo(({
struct derived_functor_t : public functor_t {
void operator() () const { cout << "hello from inside derived functor " << endl; }
} functor;
functor;
}));

I call this a pseudo-lambda because it is not completely anonymous.
Therefore, rather than creating a lambda function syntax and then having to
resolve issues of binding, I think it is better to tweak this existing, proven, statement
function syntax. To whit:

*** It should be possible to declare an anonymous struct or class that employs
inheritance (in the above example we inherit from a public functor_t base class
with a virtual operator());

*** In the above example, the statement expression
({ struct s { ... } ss; })
has as its value the address of ss, not the value of ss; hence the repetition
({ struct s { ... } ss; ss; })
Having this statement's result be the value would be more flexible.

*** It should be possible to declare an anonymous value of the structure.

*** In general, we might reserve the identifier "lambda" to be used in any circumstance
*** Most importantly, the syntax for initializing data members of the functor should be
improved. Initializing members bound by value is not so bad, but initializing members
bound by reference is circuitous. I suggest a syntax that allows data members to be
initialized "at their site", potentially accessing values in the enclosing scope.
Combining all of the above might lead to the following syntax
(example extended to demonstrate binding by reference):

for(int i=0;i != N;i++) {
foo(
({ // G++ style statement expression
struct lambda : public functor_t {
int& i = enclosing_scope::i;
void operator() () const { cout << "hello from inside derived functor " << i << endl; }
} lambda;
})
);
}


[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]
[ about comp.lang.c++.moderated. First time posters: do this! ]

[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std...@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]


Andy Glew

unread,
Dec 6, 1999, 3:00:00 AM12/6/99
to
> Note finally that this somewhat obviates the need for lambda:
> instead of defining the lambda in the parameter, it is defined
> on the previous line in a non-lambda manner. Enough to kill
> any lambda proposal?

Replying to myself: yes, probably.

Lambda functions have two main advantages:
a) they allow the function to be defined where used,
rather than declared somewhere else
b) they don't require a meaningfull name to be created.

C++'s more liberal rules about definitions allow a class
definition containing a functor to be placed almost anywhere
in the code. In standard C++, the line before the function
or template call that you want to pass the lambda function or functor to.

In fact, if something like GNU C++'s statement expressions made
it into the language, the definition can actually be in the function
argument list. An example is supplied below.

This makes lambda advantage a) almost irrelevant.

The remaining lambda advantage is anonymity.
That's still a real advantage, but not so significant
if the definition is nearby and can be tightly scoped,
as it can be in a statement expression.


Therefore I conclude, somewhat sadly (since I have been a longstanding
advocate of lambdas), that lambdas provide insufficient new value to
justify adding to the language. Particularly since the issues of non-parameter
variable binding almost forces us into a functor approach.

If something must be added, I would rather add GNU C++'s
statement expressions rather than anonymous lambdas.

Statement expressions make the "pseudo-lambdas" above
even more convenient, and, since they have been implemented
in GCC for many years, have more experience behind them.

===

I am quite happy to observe that this pseudo-lambda approach
is an alternative to the arcane "binder" notation of the STL.

===

#include <iostream>
#include <string>

struct functor_t {
virtual void operator() () const { cout << "hello from inside base functor" << endl; }
};

void
foo( const functor_t& f )
{
f();
}

int
main( int argc, char** argv )
{


foo(({
struct derived_functor_t : public functor_t {
void operator() () const { cout << "hello from inside derived functor " << endl; }
} functor;
functor;
}));
}

---

Andy Glew

unread,
Dec 6, 1999, 3:00:00 AM12/6/99
to
A little while back there was discussion about lambda functions,
as a possible extension to the standard. (Hence the posting to
comp.std.c++; posting to comp.lang.c++.moderated because
the original discussion occurred there, I believe. Apologies to
Deja News readers who can't prevent themselves from seeing
this twice.)

One issue raised was about binding of non-parameter variables.
E.g. in something like
integrate( 0.0,1.0, lambda float (float x) { return x + y; } ) )
y is not a parameter. Should y be copied by value from
the enclosing scope, or by reference - immaterial here, but
possibly mattering in other situations.

I expressed the fear that this was a slippery slope: that reference
binding obviously has issues with dangling references, which might
lead to desires to have proper continuations, .... inevitably leading
to a big, overblown, proposal that would be rejected by the standardization
committee, and which would leave those of us who would be happy with
more modest forms of lambda high and dry.


Here's a sketch of a modest proposal:


Obviously, non-parameter variables for lambdas involve creating a functor
- an object which holds this state, which can be passed to appropriate
templates, and to which () can be applied.

STOP RIGHT HERE!!! Unless there is a generic functor that can be passed
around dynamically, not just used for static templates, even this may be
sufficient to kill a lambda function / functor proposal.

Certainly, there are many uses to naked lambdas, raw function pointers,
without a state-holding object. Again, it would be a pity to lose these simple
forms of lambda functions, just because any proposal for a complicated
form of lambda functor grows too big.

A dynamic functor necessitates a virtual function

OKAY, NOW LET'S GO ON: remembering that if this gets too hairy,
we should just fall back to a naked lambda function, not a functor.
Don't throw the baby out with the bath water.


The gist of my proposal is that syntax

lambda rettype ( parmlist ) { text using parmvars + non-parm vars }

should be considered equivalent to defining an anonymous object
with functor semantics:

class anonymous_lambda_object {
private:
non-parm-vars = copies from the enclosing scope;
public:
rettype operator() ( parmlist ) {
text
}
} tmp;

E.g.

lambda float (float x) { return x + y; } )

equivalent to

class anonymous_lambda_object {
private:
typeof(extern-Y) y = extern-Y;
public:
float operator() ( float x ) { return x + this->y; }
} tmp;
integrate( 0.0,1.0, tmp )

This anonymous class would be considered to be declared
in the enclosing class scope, but initialized with values from
the enclosing function scope.

Here I have adopted what IMHO is the simplest and safest
proposal for binding: non-parameter variables would be bound,
by default, to the values in the enclosing function scope.

However, we should always allow people to hang themselves:
i.e. we should allow binding by reference.

I would propose to do this by allowing them to explicitly declare the
object that will carry the functor state.

class non_anonymous_lambda_object {
private:
float& y = y; // bind reference to immediate scope
public:
float operator() ( float x ) { return x + this->y; }
} tmp;
integrate( 0.0,1.0, tmp )


Note that I have used an extended form of initialization at the
definition site here. You can get around this, but it gets increasingly clumsy.

Note finally that this somewhat obviates the need for lambda:
instead of defining the lambda in the parameter, it is defined
on the previous line in a non-lambda manner. Enough to kill
any lambda proposal?

J.Barfurth

unread,
Dec 6, 1999, 3:00:00 AM12/6/99
to

Andy Glew <gl...@cs.wisc.edu> schrieb in im Newsbeitrag:
824hbl$e...@spool.cs.wisc.edu...

> C++'s more liberal rules about definitions allow a class
> definition containing a functor to be placed almost anywhere
> in the code. In standard C++, the line before the function
> or template call that you want to pass the lambda function or functor
to.

Wrt templates there still remains a problem, if access to local state is
required:

Local functions, passed as function pointers, wont do, as they cannot
use non-static state from the surrounding scope.

OTOH local classes cannot be used as template arguments. If local
classes were given (external) linkage, the need for lambdas would
indeed dimish enormously.

IMHO it should not be too hard to allow local classes to have linkage.
Classes local to functions defined with internal linkage would
presumably have internal linkage as well. But with anonymous namespaces,
the need for functions with internal linkage isn't there any more.
Local block scopes would probably be treated analogous to anonymous
namespaces.

From
namespace N { void foo() { { struct A {}; std::vector<A> v;
std::cout << typeid(v).name(); } } }
the output might be:
"std::vector<N::foo__void::block__0::A, std::allocator<...> >"

What issues actually led to the decision to deprive local classes of
linkage ?

The ability to shoot one's own foot by keeping references to local
variables beyond their lifetime is already present in current C++. I
can't see how this would be made worse by that change.

As the uncertainty about the various way local state could be bound in
lambdas shows, this simple (?) change would much better fit the spirit
of the language: The programmer is in charge to explicitly specify
whether to bind by reference or by value - simply by appropriately
initializing (functor) members.

Whether an extension to allow defining a local class inline (by
'statement expressions' or somehow adapted from Java's anonymous class
syntax) would be beneficial, is another matter of discussion. Personally
I find the style less readable, but this may be a matter of getting used
to it.

-- Jörg Barfurth


[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]
[ about comp.lang.c++.moderated. First time posters: do this! ]

[ comp.std.c++ is moderated. To submit articles, try just posting with ]

Tom Payne

unread,
Dec 6, 1999, 3:00:00 AM12/6/99
to

In comp.std.c++ Andy Glew <gl...@cs.wisc.edu> wrote:

> Brief:
> ====

> In previous posts I waffled on whether lambda functions should be provided.
> This exercise has led me to what I believe is a much more satisfactory syntax
> proposal for lambda functors, one that allows all forms of binding to be
> supported, and which extends GNU C++'s statement expressions.

> A short example:

> for(int i=0;i != N;i++) {
> foo(
> ({
> struct lambda : public functor_t {
> int& i = enclosing_scope::i;
> void operator() () const { cout << "hello from inside derived functor " << i << endl; }
> } lambda;
> })
> );
> }


Very impressive!! Thanks.

Apparently lambda, being local, cannot be passed as a template
parameter. The following example shows a hack that gets around that
limitation.

Tom Payne

------------------------------------------------------------------

#include <iostream>
#include <algorithm>
#include <set>
#include <map>
#include <queue>
#include <utility>
#include <functional>

template<class T1, class T2>
struct functor {
virtual T1 operator()( T2 x ) = 0;
};

template<class T1, class T2>
struct func {
functor<T1,T2>* f;
func( functor<T1,T2>* g ) : f(g) {}
virtual int operator()( const int x ){ (*f)(x); }
};

main() {

set<int> myset;

int i;
do{
cout << "Gimme an int! (zero to terminate) ";
cin >> i;
myset.insert(i);
} while ( i != 0 );

cout << "The numbers are: " << endl;
for( set<int>::iterator j = myset.begin(); j != myset.end(); ++j ){
cout << *j << " ";
}
cout << endl;

cout << "And their sum is " << endl;
int sum = 0;
for_each (
myset.begin(),
myset.end(),
func<int,const int> (({
struct lambda : functor<int, const int> {
int& sum;
int operator()( const int x ){
return sum = sum + x;
}
lambda( int& y ) : sum(y) {}
} on( sum );
}))
);
cout << sum;
cout << endl;

Gavin Collings

unread,
Dec 7, 1999, 3:00:00 AM12/7/99
to

In article <8245oh$7...@spool.cs.wisc.edu>,

Andy Glew <gl...@cs.wisc.edu> wrote:
> One issue raised was about binding of non-parameter variables.
> E.g. in something like
> integrate( 0.0,1.0, lambda float (float x) { return x + y; } ) )
> y is not a parameter. Should y be copied by value from
> the enclosing scope, or by reference - immaterial here, but
> possibly mattering in other situations.

The fact that we're even talking about binding here reflects the fact
that a lambda is being viewed as a class that acts like a function. I
prefer to think of it as a function with state. Local functions in
other block structured languages (and in C++) have access to their
enclosing scope by default. So I would resolve this by stating that
lambdas are scoped like functions, not like classes and have direct
access to all variables at their point of definition. This is clearly
equivalent to binding by reference as you put it.

> I expressed the fear that this was a slippery slope: that reference
> binding obviously has issues with dangling references, which might
> lead to desires to have proper continuations, .... inevitably leading
> to a big, overblown, proposal that would be rejected by the
standardization
> committee, and which would leave those of us who would be happy with
> more modest forms of lambda high and dry.

It's obviously a problem, but not just for lambdas. If you're going to
pass around things dynamically, you have to accept the responsibility
for ownership/lifetimes of referenced entities.


> A dynamic functor necessitates a virtual function

Conceptually, but this could be made implicit by rule: all lambdas must
provide an operator(). The problem with inheritance is it places
restrictions on the argument list to the operator() function. But
still, maybe we could leave that to the programmer and support
inheritance only among lambdas (see below)

> The gist of my proposal is that syntax
>
> lambda rettype ( parmlist ) { text using parmvars + non-parm
vars }
>
> should be considered equivalent to defining an anonymous object
> with functor semantics:
>
> class anonymous_lambda_object {
> private:
> non-parm-vars = copies from the enclosing scope;
> public:
> rettype operator() ( parmlist ) {
> text
> }
> } tmp;
>

[...]

>
> class non_anonymous_lambda_object {
> private:
> float& y = y; // bind reference to immediate scope
> public:
> float operator() ( float x ) { return x + this->y; }
> } tmp;
> integrate( 0.0,1.0, tmp )
>
> Note that I have used an extended form of initialization at the
> definition site here. You can get around this, but it gets
> increasingly clumsy.

It seems, you've abandoned your new syntax when it was going so well.
I think if a new syntax is worth doing, it's worth doing well. It
should provide benefits for defining functors as well as point of
invocation lambdas. What is missing is a clean way of defining member
variables of class scope, since the class definition is implicit. I
would go for something like.

lambda ret-type [opt-name]( parm-list ) [: public base-lambda]
{
member float f = y;
...
base-lambda(); // call base class version.
...
return ret-type();
};

Where member is a new keyword to allow implicit declaration of member
variables and the parm-list must be inheritance compatible with that
defined in base-lambda. I suppose that a lambda definition expression
should return a reference to, or the address of the created lambda.

--
Gavin Collings
gcol...@sperry-sun.co.uk


Sent via Deja.com http://www.deja.com/
Before you buy.

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]
[ about comp.lang.c++.moderated. First time posters: do this! ]

[ comp.std.c++ is moderated. To submit articles, try just posting with ]

Tom Payne

unread,
Dec 8, 1999, 3:00:00 AM12/8/99
to

In comp.lang.c++.moderated Gavin Collings <gcol...@sperry-sun.co.uk> wrote:
> In article <8245oh$7...@spool.cs.wisc.edu>, Andy Glew <gl...@cs.wisc.edu> wrote:
[...]

> [...]

How about

lambda int ( const int& x ) : sum(y) { static int& sum; return sum += y; }

exactly represents

struct whatever : unary_function {
int& sum;
whatever(int& y) : sum(y) {}
int operator() ( const int& x ) { return sum+=y; }
}

???

Specifically, static locals become member data -- it makes little
difference whether they are static, since these functors are
single-instance/single-operator classes. Initialization lists
provide a standard mechanism for controlling the initialization
of the member data.

Tom Payne

Gavin Collings

unread,
Dec 9, 1999, 3:00:00 AM12/9/99
to
In article <82jo6e$qtj$1...@pravda.ucr.edu>,

Tom Payne <t...@roam-thp2.cs.ucr.edu> wrote:
>
> How about
>
> lambda int ( const int& x ) : sum(y) { static int& sum; return sum
+= y; }
>
> exactly represents
>
> struct whatever : unary_function {
> int& sum;
> whatever(int& y) : sum(y) {}
> int operator() ( const int& x ) { return sum+=y; }
> }
>
> ???
>
> Specifically, static locals become member data -- it makes little
> difference whether they are static, since these functors are
> single-instance/single-operator classes. Initialization lists
> provide a standard mechanism for controlling the initialization
> of the member data.

I considered both using a constructor style initializer list and the
static keyword. I liked the parallel with constructor initializers,
but, since they fail to introduce the type of the variable they are
only part of the solution. This means that a separate declaration is
necessary anyway, and, that being the case, why not use it for
initialization? The second problem is that it got in the way of my
proposed inheritance syntax.

The keyword static could be used as you say, but my reasons for
introducing another one were: (i) static has far too many meanings
already. More seriously (ii) using it in this way would conflict with
its existing use in member functions where it names a variable
with "global lifetime". In retrospect, it's a shame that static in
member functions wasn't defined that way. It's a matter of taste,
however.

We do need static/member data, though. Part of the advantage of
functors over functions is that they maintain state over multiple
invocations.

The only matter I'm in two minds over, is where to place the lambda and
name, the other way being:

int lambda( const int & x )
{
member int & sum = y;
return sum += x;
}

with optional name and inheritance clause.

It's more like a function, as opposed to a class, which reflects my
preferred view of functors. And it lets "lambda" look like a dummy
function name for anonymous definitions. In fact, I prefer it.

--
Gavin Collings
gcol...@sperry-sun.co.uk


Sent via Deja.com http://www.deja.com/
Before you buy.

---

Tom Payne

unread,
Dec 11, 1999, 3:00:00 AM12/11/99
to
In comp.lang.c++.moderated Tom Payne <t...@roam-thp2.cs.ucr.edu> wrote:

> Apparently lambda, being local, cannot be passed as a template
> parameter. The following example shows a hack that gets around that
> limitation.

Wrong!

> template<class T1, class T2>
> struct functor {
> virtual T1 operator()( T2 x ) = 0;
> };

> template<class T1, class T2>
> struct func {
> functor<T1,T2>* f;
> func( functor<T1,T2>* g ) : f(g) {}
> virtual int operator()( const int x ){ (*f)(x); }

^^^ ^^^^^^^^^
T1 T2 // minor error
> };
[...]


> for_each (
> myset.begin(),
> myset.end(),
> func<int,const int> (({
> struct lambda : functor<int, const int> {
> int& sum;
> int operator()( const int x ){
> return sum = sum + x;
> }
> lambda( int& y ) : sum(y) {}
> } on( sum );
> }))
> );

The real problem is that the lifetime of the lambda functor is the
duration of constructor of func, after which the functor's f pointer
dangles.

All of my attempts at a workaround have run into one or more of the
following problems:
* the fact that local classes/structs like lambda can't be used to
as template parameters.
* if the function really thinks that lambda is a functor and copies
it, then it slices lambda down to a functor.
* the lifetime of the temporaries tied to const references is too
short.

Tom Payne


[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]
[ about comp.lang.c++.moderated. First time posters: do this! ]

[ comp.std.c++ is moderated. To submit articles, try just posting with ]

Jens Kilian

unread,
Dec 11, 1999, 3:00:00 AM12/11/99
to
Gavin Collings <gcol...@sperry-sun.co.uk> writes:

> I considered both using a constructor style initializer list and the
> static keyword. I liked the parallel with constructor initializers,
> but, since they fail to introduce the type of the variable they are
> only part of the solution. This means that a separate declaration is
> necessary anyway, and, that being the case, why not use it for
> initialization? The second problem is that it got in the way of my
> proposed inheritance syntax.

A lambda in which you need to *declare* free variables is broken. The compiler
already has all the information it needs - let it do the name lookup.

> The only matter I'm in two minds over, is where to place the lambda and
> name, the other way being:
>
> int lambda( const int & x )
> {
> member int & sum = y;
> return sum += x;
> }
>
> with optional name and inheritance clause.
>
> It's more like a function, as opposed to a class, which reflects my
> preferred view of functors. And it lets "lambda" look like a dummy
> function name for anonymous definitions. In fact, I prefer it.

My preferred version would still be

lambda<return type> (parameters) { body }

This should be relatively easy to parse, easier than anything that starts
with the type.

IMHO etc.,

Jens.
--
mailto:j...@acm.org phone:+49-7031-464-7698 (HP TELNET 778-7698)
http://www.bawue.de/~jjk/ fax:+49-7031-464-7351
PGP: 06 04 1C 35 7B DC 1F 26 As the air to a bird, or the sea to a fish,
0x555DA8B5 BB A2 F0 66 77 75 E1 08 so is contempt to the contemptible. [Blake]

Dave Harris

unread,
Dec 12, 1999, 3:00:00 AM12/12/99
to

gcol...@sperry-sun.co.uk (Gavin Collings) wrote:
> int lambda( const int & x )
> {
> member int & sum = y;
> return sum += x;
> }

I like this too. And I agree with your earlier comment, about binding by
reference being the default. That being the case, why do you need "sum"
here? And why do you need the return value? Eg:

int average( int *first, int *last ) {
int sum = 0;
for_each( first, last, void lambda( int item ) {
sum += item;
} );
return sum / (last - first);
}

Given these rules, I am not sure whether "member" is worth it. In many
cases it doesn't matter whether binding is by reference or by copy. Where
it does matter, by reference will often be correct. Quite often we can
manage by adding a variable to the enclosing scope even if it is not
strictly needed there.

As far as I can tell, the main case where you really need "member" and
copy semantics is when the enclosing scope does not have a long enough
lifetime, which is to say, when the lambda passed outside of the scope in
which it is defined. Did I miss this case being addressed in this thread?
I think it has to use the "new" keyword somehow.

unary_function<int,int> *get_inc_by( int y ) {
return new int lambda( int x ) {
member int m_y = y;
return x+m_y;
};
}

Let's write that out in full:

unary_function<int,int> *get_inc_by( int y ) {
struct inc_by : unary_function<int,int> {
int m_y;
inc_by( int y ) : m_y(y) {}
int operator()( int x ) { return x+m_y; }
};
return new inc_by( y );
}

Personally I don't think this is too bad as it stands. It is a more
complex case anyway, so the gain from lambda is smaller. I suspect we'll
want even more complications, like extra member functions or ways to
access instance variables from outside the class. The important thing is
that the functors written out long-hand be compatible with ones produced
by the lambda notation. Then the easy things are easy and the hard things
are possible.

I agree with J.Barfurth that there should be a way to make local classes
with external linkage. Perhaps adding the word "external" to the
definition somewhere, eg after the "struct" or "class", would do.

Dave Harris, Nottingham, UK | "Weave a circle round him thrice,
bran...@cix.co.uk | And close your eyes with holy dread,
| For he on honey dew hath fed
http://www.bhresearch.co.uk/ | And drunk the milk of Paradise."

Gavin Collings

unread,
Dec 14, 1999, 3:00:00 AM12/14/99
to
In article <memo.19991211...@btinternet.com>,

bran...@cix.co.uk wrote:
>
> gcol...@sperry-sun.co.uk (Gavin Collings) wrote:
> > int lambda( const int & x )
> > {
> > member int & sum = y;
> > return sum += x;
> > }
>
> I like this too. And I agree with your earlier comment, about
> binding by reference being the default. That being the case, why do
> you need "sum" here? And why do you need the return value?

You don't. I was lazy and directly transliterated an earlier example
without semantic intervention. It wasn't meant to be an example of
good form, rather how to apply a new syntax.


> Given these rules, I am not sure whether "member" is worth it. In
> many cases it doesn't matter whether binding is by reference or
> by copy. Where it does matter, by reference will often be correct.
> Quite often we can manage by adding a variable to the enclosing scope
> even if it is not strictly needed there.

True, but I'd like this syntax to have benefits beyond lambda
functions. Namely, easier definition of non-anonymous functors in line
with current usage (syntax is "int lambda name( ... )". Member there
is more important to support encapsulation.

> As far as I can tell, the main case where you really need "member"
> and copy semantics is when the enclosing scope does not have a long
> enough lifetime, which is to say, when the lambda passed outside of
> the scope in which it is defined. Did I miss this case being
> addressed in this thread?

It has been mentioned: A comment by Andy Glew that passing lambdas
around necessitates inheritance, and by others that life time issues
are the responsibility of the programmer. I agree with both of these.


> I think it has to use the "new" keyword somehow.
>
> unary_function<int,int> *get_inc_by( int y ) {
> return new int lambda( int x ) {
> member int m_y = y;
> return x+m_y;
> };
> }

Usage of new looks natural.

> Let's write that out in full:
>
> unary_function<int,int> *get_inc_by( int y ) {
> struct inc_by : unary_function<int,int> {
> int m_y;
> inc_by( int y ) : m_y(y) {}
> int operator()( int x ) { return x+m_y; }
> };
> return new inc_by( y );
> }

Actually, the syntax I was proposing makes the inheritance explicit:-

return new int lambda( int x ) : public unary_function<int,int>{...}

I see little alternative to this as signature of operator() can vary
and needs to be checked.


> Personally I don't think this is too bad as it stands. It is a more
> complex case anyway, so the gain from lambda is smaller. I suspect
> we'll want even more complications, like extra member functions or
> ways to access instance variables from outside the class. The
> important thing is that the functors written out long-hand be
> compatible with ones produced by the lambda notation. Then the easy
> things are easy and the hard things are possible.

Hmmm. Extra member functions - probably I would handle this by allowing
access to base class/lambda members. Defining them in the lambda
itself would probably make the lambda complex enough to merit using the
extended/old syntax anyway. Of course, you could always pass a lambda
to the lambda! It does raise the issue of access control, however.
The member syntax could be extended to "private member", "protected
member" etc. defaulting to private as with classes. Given this, I
don't see any problem with using the current inheritance rules for
classes, with a possible restriction that operator() should be
unambiguous.

> I agree with J.Barfurth that there should be a way to make local
> classes with external linkage. Perhaps adding the word "external" to
> the definition somewhere, eg after the "struct" or "class", would do.

I don't think this necessitates another keyword. Unless someone can
convince me that giving local classes (or lambda equivalents) linkage
by default has significant drawbacks, that is.

I'd like to throw a couple of other ideas into the pot: -

1) I read the other day that lisp would have been a good deal easier to
learn if the name lambda had been replaced by make-procedure or
similar. That being the case, why not use a keyword "function".

2) With all the talk about RVO going on in another thread, why not hold
the result in a member variable and return by reference. e.g.

complex &lambda add_two( complex a, complex b )
{
member complex result( a + b );
return result;
}

You could go even further, as other languages have, and use a "result"
keyword to make some of this implicit.

complex &lambda add_two( const complex & a, const complex & b )
{
member complex result( a + b );
return result;
}

complex lambda add_two( const complex & a, const complex & b )
: result( a + b )
{}

Note, return by reference implied, not written; as is the
definition "private member complex result"; a constructor style
initializer is used to avoid default a constructor invocation on
result; the value of result at the end of the function is
automatically returned (by reference). This is admittedly a little
half-baked: others may have more insight.

--
Gavin Collings
gcol...@sperry-sun.co.uk


Sent via Deja.com http://www.deja.com/
Before you buy.
---

Andy Glew

unread,
Dec 15, 1999, 3:00:00 AM12/15/99
to

> > As far as I can tell, the main case where you really need "member"
> > and copy semantics is when the enclosing scope does not have a long
> > enough lifetime, which is to say, when the lambda passed outside of
> > the scope in which it is defined. Did I miss this case being
> > addressed in this thread?
>
> It has been mentioned: A comment by Andy Glew that passing lambdas
> around necessitates inheritance, and by others that life time issues
> are the responsibility of the programmer. I agree with both of these.

??

I never said anything about lambdas *requiring* inheritance.
I'd be particularly unlikely to, since I am one of those people
who rather dislike inheritance.

However, inheritance is convenient if you wish to
manipulate a lambda object when its class definition has gone
out of scope. However, only the most basic inheritance is required:
passing in of a void* that can be cast to an object that only the
lambda function knows about, said object storing the
value copied state the lambda needs.

===

I've been sitting out here, but I am really uncomfortable with y'all
talking about reference binding.

Sure, I want to permit reference binding, but I do *not* want
it to be the default. Making reference binding the default rather
than value-copy binding would violate the C++ principle of
not paying for anything unless you use it, since pass by reference
for simple scalar (int, float, double) is significantly slower than
pass by value.


[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]
[ about comp.lang.c++.moderated. First time posters: do this! ]

[ comp.std.c++ is moderated. To submit articles, try just posting with ]

Tom Payne

unread,
Dec 15, 1999, 3:00:00 AM12/15/99
to

In comp.lang.c++.moderated Gavin Collings <gcol...@sperry-sun.co.uk> wrote:

> Hmmm. Extra member functions - probably I would handle this by allowing
> access to base class/lambda members. Defining them in the lambda
> itself would probably make the lambda complex enough to merit using the
> extended/old syntax anyway. Of course, you could always pass a lambda
> to the lambda!

I'm not exactly sure what you have in mind, but I think it is useful
to consider the problem of defining bind2nd(). Mathematically,
bind2nd() is:

lambda(f,y){ lambda(x){ f(x,y) } }
^^^^^^^^^^^^^^^^^^^
an expression with free variables f and y

>From the C++ perspective, that first lambda is constructing a lambda
object, i.e., it is a function that invokes a nontrivial constructor
on the object that represents lambda(x){ f(x,y) }, a constructor
that takes a binary function and an object in its domain as arguments.

The implementation of bind2nd() in Stroustrup's C++ Prog Lang deals
with the issue by binding f and y to members (op and arg2,
respectively) of a functor (binder2nd) that has a constructor that
takes f and y as arguments. In Stroustrup's implementation the
underlined expression above, in effect, turns into "binder2nd(f,y)":

template <class BinOp>
class binder2nd
: public unary_function<BinOp::first_argument_type,BinOP::result_type> {
protected:
BinOp op;
typename BinOp::second_agurment_type agr2;
public:
binder2nd(const BinOp& x, const typeneame BinOp::second_aggument_type& v)
: op(x), agr2(v) {}
result_type operator() (const first_argument_type& x) const {
return op(x,agr2);
}
};

template <class BinOp, class T>
binder2nd<BinOp> bind2nd(const BinOp& op, const T& v) {
return binder2nd<BinOp>(op,v);
}

So, what's not to like about this implementation? Well, it requires
two disjoint declarations non-local declarations, each of which
declares a new identifier, and it is syntactically tedious. I'd
prefer a notation that can be embedded in subexpressions (including
those that are used as template parameters) and that
introduces/requires no new names. That's asking a lot.

Stroustrup's scheme works well but raises some questions:

* Why couldn't binder2nd be a local class, say within bind2nd? I'm
guessing that:
- We'd have to add a pure virtual definition of operator() to
unary_fuction. (But that should have been done anyway. Right?)
- The return type of bind2nd would have to be
unary_operator<whatever>.
- We'd only be able to pass a binder2nd by reference-to-base-class.
- To avoid a dangling reference, bind2nd would have to allocate
(new) the binder2nd. But who's going to deallocate it?

* Rather than bothering with bind2nd at all, can't we directly or
indirectly call binder2nd's constructor? (AFIK, construtors can
infer types, just as template functions can.) That approach is not
so general, however:
- It requires that the return value be of a struct/class type.
- If that type is user-defined, the corresponding constructor
may already have been defined by the user.
- binder2nd already has an overloaded operator(), which gets
invoked when we apply binder2nd to an argument list. The
function of bind2nd is to provide an entity that, when
applied to an argument list, invokes binder2nd's constructor.

* Why can't bind2nd be a functor-returning functor that is defined
via the same lambda defining mechanisms used for, say, int-valued
functors?
- AFIK, we would have to provide the template with type arguments
at every call to bind2nd, since template classes are much dumber
than template functions about being able to infer types.
- The type of the thing that bind2nd returns, i.e., binder2nd, is
NOT a standard lambda expression. Rather, it is a parameterized
scheme for generating lambda expressions, i.e., it has a complex
constructor. So, we need a mechanism for not only defining
lambda expressions but for defining such parameterized schemes
of lambda expressions.

Tom Payne


[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]
[ about comp.lang.c++.moderated. First time posters: do this! ]

[ comp.std.c++ is moderated. To submit articles, try just posting with ]

Gavin Collings

unread,
Dec 16, 1999, 3:00:00 AM12/16/99
to

In article <836fpb$j72$1...@pravda.ucr.edu>,
Tom Payne <t...@roam-thp2.cs.ucr.edu> wrote:
> In comp.lang.c++.moderated Gavin Collings <gcollings@sperry-

sun.co.uk> wrote:
> > Of course, you could always pass a lambda to the lambda!
>
> I'm not exactly sure what you have in mind, but I think it is useful
> to consider the problem of defining bind2nd(). Mathematically,
> bind2nd() is:
>
> lambda(f,y){ lambda(x){ f(x,y) } }
> ^^^^^^^^^^^^^^^^^^^
> an expression with free variables f and y

OK, let's keep that in mind. But first let me point out that inline
expression of this is trivial (using proposed syntax)

void f( list<int> & c )
{
const int seven = 7;

find_if( c.begin(), c.end(), bool lambda( int first )
{
member less<int> op();
return op( first, seven );
} );
}

But I think you're more interested in talking about templates.
Naturally, it should be possible to parameterize "named lambdas" just
as classes and functions (well more like functions actually - see below)

> So, what's not to like about this implementation? Well, it requires
> two disjoint declarations non-local declarations, each of which
> declares a new identifier, and it is syntactically tedious. I'd
> prefer a notation that can be embedded in subexpressions (including
> those that are used as template parameters) and that
> introduces/requires no new names. That's asking a lot.
>
> Stroustrup's scheme works well but raises some questions:
>
> * Why couldn't binder2nd be a local class, say within bind2nd? I'm
> guessing that:

See below.

> * Rather than bothering with bind2nd at all, can't we directly or
> indirectly call binder2nd's constructor? (AFIK, construtors can
> infer types, just as template functions can.) That approach is
> not so general, however:

I'm not sure that you're right about constructors and type inference
(unless you mean template member constructors?) Anyway I don't like
the approach.

> * Why can't bind2nd be a functor-returning functor that is defined
> via the same lambda defining mechanisms used for, say, int-valued
> functors?

That sounds nice. In fact, both your first and third approaches can be
combined with existing C++ syntax (implementation at the end). You're
right about template type arguments not being inferred, of course, and
I propose a special rule for any new syntax introduced: templated
(named) lambdas can infer their types based on the arguments to operator
() - i.e. they act more like functions than classes which has been a
recurring theme of mine.

It turns out that return type can be made that of a "named lambda" sub-
expression, which happens to inherit from unary_function<whatever>.
That means that all slicing and virtual operator() issues disappear.
Return by value eliminates the need to handle dangling references.


Implementation in Proposed Syntax
---------------------------------

// Forward declare template bind2nd::unary_first (subexpression)
// This is only necessary to allow the return type to be main lambda
// return type to be specified. We could get around it by a special
// rule that allows lambdas to define their return type internally
// somehow (see previous discussion about keyword result?)

template <typename BinOp>
BinOp::result_type lambda bind2nd::unary_first
( const BinOp::first_argument_type & x );


template <typename BinOp>
bind2nd::unary_first<BinOp>
lambda bind2nd( const BinOp & f,
const BinOp::second_argument_type & second )
{
member BinOp op( f );
member BinOp::second_argument_type y( second );

// definition of subexpression must be in internal scope
// to access bound operation and variable

BinOp::result_type
lambda unary_first( const BinOp::first_argument_type & x )
: public
unary_function<BinOp::first_argument_type,BinOp::result_type>
{
return op( x, y );
};

return unary_first<BinOp>();
};

// Consider making the () at the end of the return statement
// unnecessary, since, following my conviction that lambdas are
// more like functions than classes, a function definition provides
// a function "object" to call - so it should be with "lambdas".


Implementation in Current C++ (give or take a typename or two?)
-----------------------------

template <typename BinOp> struct bind_second
{
BinOp op;
BinOp::second_argument_type y;

bind_second() {}

struct unary_first
: unary_function<BinOp::first_argument_type, BinOp::result_type>
{
bind_second & mine; // skirt around scoping issues

unary_first( bind_second & init_mine ) : mine( init_mine ) {}

BinOp::result_type operator()( const
BinOp::first_argument_type & x )
{
return mine.op( x, mine.y );
}
};

unary_first operator()( const BinOp & f, const
BinOp::second_argument_type & second )
{
op = f; // done here rather than constructor to emphasize
y = second; // similarity with lambda expression.

return unary_first( *this );
}
};


// Test code:

list<int> c;
list<double> d;

c.push_back( 8 );
c.push_back( 7 );
c.push_back( 6 );
c.push_back( 5 );
c.push_back( 4 );
c.push_back( 3 );
c.push_back( 2 );
c.push_back( 1 );

d.push_back( 5.0 );
d.push_back( 4.0 );
d.push_back( 3.0 );
d.push_back( 2.0 );
d.push_back( 1.0 );

const int seven = 7;
const double three = 3.0;

list<int>::iterator it;
list<double>::iterator dt;

it = find_if( c.begin(), c.end(),
bind_second<less<int> >()(less<int>(), seven ));
dt = find_if( d.begin(), d.end(),
bind_second<less<double> >()(less<double>(), three));

cout << "Found " << *it << " and " << *dt << endl;

--
Gavin Collings
gcol...@sperry-sun.co.uk


Sent via Deja.com http://www.deja.com/
Before you buy.

Tom Payne

unread,
Dec 18, 1999, 3:00:00 AM12/18/99
to

Gavin Collings wrote:
: Tom Payne <t...@roam-thp2.cs.ucr.edu> wrote:


: > Gavin Collings wrote:
: > >
: > > Of course, you could always pass a lambda to the lambda!
: >
: > I'm not exactly sure what you have in mind, but I think it is useful
: > to consider the problem of defining bind2nd(). Mathematically,
: > bind2nd() is:
: >
: > lambda(f,y){ lambda(x){ f(x,y) } }
: > ^^^^^^^^^^^^^^^^^^^
: > an expression with free variables f and y

: OK, let's keep that in mind. But first let me point out that inline
: expression of this is trivial (using proposed syntax)

[... example snipped ...}
: But I think you're more interested in talking about templates.


: Naturally, it should be possible to parameterize "named lambdas" just
: as classes and functions (well more like functions actually - see below)

Right, given f and y, it's straightforward to write a functor that
represents lambda(x){ f(x,y) }. The trick is to define an entity that
does this uniformly for all f and y, and does it regardless of the
types of f and y.

: > So, what's not to like about this implementation? Well, it requires


: > two disjoint declarations non-local declarations, each of which
: > declares a new identifier, and it is syntactically tedious. I'd
: > prefer a notation that can be embedded in subexpressions (including
: > those that are used as template parameters) and that
: > introduces/requires no new names. That's asking a lot.
: >
: > Stroustrup's scheme works well but raises some questions:
: >
: > * Why couldn't binder2nd be a local class, say within bind2nd? I'm
: > guessing that:

: See below.

: > * Rather than bothering with bind2nd at all, can't we directly or
: > indirectly call binder2nd's constructor? (AFIK, construtors can
: > infer types, just as template functions can.) That approach is
: > not so general, however:

: I'm not sure that you're right about constructors and type inference
: (unless you mean template member constructors?)

I'm wrong. According to Stroustrup, The C++ Prog Lang, p. 335:

Note that class template parameters are never deduced. ... If we need
to create an object of a deduced type, we can often do that by calling
a function to do the creation ...

Which is exactly the function of bind2nd. It deduces the type of
binder2nd to be created and calls binder2nd's constructor with the proper
parameters.

: Anyway I don't like the approach.

I'd like it if it worked.

: > * Why can't bind2nd be a functor-returning functor that is defined


: > via the same lambda defining mechanisms used for, say, int-valued
: > functors?

: That sounds nice. In fact, both your first and third approaches can be
: combined with existing C++ syntax (implementation at the end). You're
: right about template type arguments not being inferred, of course, and
: I propose a special rule for any new syntax introduced: templated
: (named) lambdas can infer their types based on the arguments to operator
: () - i.e. they act more like functions than classes which has been a
: recurring theme of mine.

Agreed. Whatever C++ entity (or system of entities) represents a lambda
expression, it should be:
* able to infer types, the way that template functions can
* able to carry state/bindings
* able to be passed to STL algorithms as a functor (i.e. it must have
traits)
* anonymous, i.e., not require programmer provided naming.

: It turns out that return type can be made that of a "named lambda" sub-


: expression, which happens to inherit from unary_function<whatever>.
: That means that all slicing and virtual operator() issues disappear.

Good.

: Return by value eliminates the need to handle dangling references.

Reference-returning functors are a bad idea. Those that don't return
dangling references must dynamically allocate their returned values,
thereby, leaking memory.

[... implemenation in the proposed syntax snipped...]

: Implementation in Current C++ (give or take a typename or two?)
: -----------------------------

: template <typename BinOp> struct bind_second
: {
: BinOp op;
: BinOp::second_argument_type y;

: bind_second() {}

: struct unary_first : unary_function<BinOp::first_argument_type,
BinOp::result_type>
: {
: bind_second & mine; // skirt around scoping issues

: unary_first( bind_second & init_mine ) : mine( init_mine ) {}

: BinOp::result_type operator()( const
: BinOp::first_argument_type & x )
: {
: return mine.op( x, mine.y );
: }
: };

: unary_first operator()( const BinOp & f, const
: BinOp::second_argument_type & second )
: {
: op = f; // done here rather than constructor to emphasize
: y = second; // similarity with lambda expression.
: return unary_first( *this );

: }
: };

So, bind_second provides the binding context for unary_first, which
simply carries a reference back to that context, thereby avoiding some
complexity in the constructor of unary_first at the expense of some
convolution in the constructor-like operator() of bind_second. Hmmmm.

Tom Payne

Dave Harris

unread,
Dec 20, 1999, 3:00:00 AM12/20/99
to
gl...@cs.wisc.edu (Andy Glew) wrote:
> Making reference binding the default rather than value-copy binding
> would violate the C++ principle of not paying for anything unless you
> use it, since pass by reference for simple scalar (int, float, double)
> is significantly slower than pass by value.

I don't agree with this argument. Pass by reference may be slower for some
scalar types but it is typically quicker for large types. It's swings and
roundabouts.

A reference is a new name for an object rather than a new object, so it is
conceptually cheaper than a copy. (Admittedly in some places the
difference can be optimised away with either scheme.)

Dave Harris, Nottingham, UK | "Weave a circle round him thrice,
bran...@cix.co.uk | And close your eyes with holy dread,
| For he on honey dew hath fed
http://www.bhresearch.co.uk/ | And drunk the milk of Paradise."

Dave Harris

unread,
Dec 20, 1999, 3:00:00 AM12/20/99
to
gcol...@sperry-sun.co.uk (Gavin Collings) wrote:
> [...] I'd like this syntax to have benefits beyond lambda functions.

> Namely, easier definition of non-anonymous functors in line with
> current usage (syntax is "int lambda name( ... )". Member there
> is more important to support encapsulation.

I'm not entirely comfortable with this aim. I suppose I think that if
these things have their own state then they're full objects and ought to
be treated as such. It also feels weird to write something in the body of
the function which creates a declaration outside of it. I don't feel that
way about the implicit references to the enclosing scope because they are
merely implementation detail (eg that can be optimised away). But I expect
I'll get over it.


> Actually, the syntax I was proposing makes the inheritance explicit:-
>
> return new int lambda( int x ) : public unary_function<int,int>{...}
>
> I see little alternative to this as signature of operator() can vary
> and needs to be checked.

I was thinking the type of the template parameters could be deduced from
the signature of the lambda. (And the "unary_" could be omitted.) Do you
mean the signature of operator() can be different to the signature of the
lambda? I don't see why that is necessary or desirable.

Thus:
std::function<int,int> *get_inc_by( int y ) {
return new float lambda( int x ) {


member int m_y = y;
return x + m_y;
};
}

would be a type error because "float lambda( int x )" is a subtype of
"std::function<float,int>", which is a different base class to the one
get_inc_by() is declared to return.

I don't mind if the inheritance can also be specified explicitly, as an
option. Although it is another step towards verbosity which reduces the
conciseness value of lambdas with members.


> I don't think this necessitates another keyword. Unless someone can
> convince me that giving local classes (or lambda equivalents) linkage
> by default has significant drawbacks, that is.

I would prefer that. It is a language change rather than extension,
though. I suppose it doesn't much matter as long as local classes don't
produce name clashes (which implementations can avoid much as they do for
anonymous namespaces).

I don't really understand why the current language makes them
non-external, and I didn't want to mess with something I didn't understand
:-)


> 2) With all the talk about RVO going on in another thread, why not hold
> the result in a member variable and return by reference.

I don't see any reason to forbid it, but neither do I think this justifies
"member". "Result" looks like a separate issue not directly concerned with
lambdas. That is, you should be able to use them in normal functions and
they should be thrashed out in that context first.

Dave Harris, Nottingham, UK | "Weave a circle round him thrice,
bran...@cix.co.uk | And close your eyes with holy dread,
| For he on honey dew hath fed
http://www.bhresearch.co.uk/ | And drunk the milk of Paradise."

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]
[ about comp.lang.c++.moderated. First time posters: do this! ]

[ comp.std.c++ is moderated. To submit articles, try just posting with ]

Gavin Collings

unread,
Dec 21, 1999, 3:00:00 AM12/21/99
to
In article <836hk6$s...@spool.cs.wisc.edu>,

"Andy Glew" <gl...@cs.wisc.edu> wrote:
> I've been sitting out here, but I am really uncomfortable with y'all
> talking about reference binding.
>
> Sure, I want to permit reference binding, but I do *not* want
> it to be the default. Making reference binding the default rather

> than value-copy binding would violate the C++ principle of
> not paying for anything unless you use it, since pass by reference
> for simple scalar (int, float, double) is significantly slower than
> pass by value.

I'm not sure reference binding is the right way of putting it. What I
was talking about was a scope rule. So in, a sense, access is direct.
I made a comment that it's equivalent to reference binding, but really
it's a different way of thinking about it: lambdas should be
scoped like functions. Do we complain about the principle of not
paying for anything when we access the enclosing scope of a member
function? We don't insist that member functions access member
variables by value even though it may be more efficient for basic
types. Have you been following the call_traits<> stuff on boost? I'm
not even convinced that the speed advantage is all that great after
optimization.

Gavin Collings

unread,
Dec 22, 1999, 3:00:00 AM12/22/99
to
In article <memo.19991216...@btinternet.com>,

bran...@cix.co.uk wrote:
> gcol...@sperry-sun.co.uk (Gavin Collings) wrote:
> > [...] I'd like this syntax to have benefits beyond lambda functions.

> > Namely, easier definition of non-anonymous functors in line with
> > current usage (syntax is "int lambda name( ... )". Member there
> > is more important to support encapsulation.
>
> I'm not entirely comfortable with this aim. I suppose I think that if
> these things have their own state then they're full objects and ought
> to be treated as such. It also feels weird to write something in the
> body of the function which creates a declaration outside of it. I
> don't feel that way about the implicit references to the enclosing
> scope because they are merely implementation detail (eg that can be
> optimised away). But I expect I'll get over it.

It does feel strange, but possibly no more so than statics in
functions. I do feel the requirement to carry state is an important
one, though. And I would like to kill two birds with one stone -
lambdas and functors. Consider the alternatives: Constructor style
initialisers are attractive, but not general since type specifiers are
missing. Another might be an additional set of braces (or other
syntactic unit), but the increase in verbosity seems unacceptable. I
suppose we could use the parameter list and maybe even the static
keyword (I don't think its meaning is defined here) along with the
default parameter syntax : -

int lambda( int i, static int j = 1 ) // one time per object
{ // initialisation
return (i * j++);
}


> I was thinking the type of the template parameters could be deduced
> from the signature of the lambda. (And the "unary_" could be
> omitted.) Do you mean the signature of operator() can be different to
> the signature of the lambda? I don't see why that is necessary or
> desirable.

No, I really meant that, even in this day of generic programming, some
code relies on virtual functions, and a virtual overloaded operator()
should be checked against the super-type.

> I don't mind if the inheritance can also be specified explicitly, as
> an option. Although it is another step towards verbosity which
> reduces the conciseness value of lambdas with members.

So you're proposing an implicit inheritance from library classes? I
suppose I'm a little uncomfortable with linking a *language* change to
*library* code. One difficulty is that the library would need
signatures for arbitrary number of parameters (how far do you go?).

I'm actually not convinced that expressing this sub-type relationship
is as necessary as I first thought. Your code above is an example of
something returning a lambda. Tom Payne brought up a very similar
example in this thread relating to bind2nd.

Referring to your example, the difficulty arises in expressing the
return type of get_inc_by. If get_inc_by is defined as a functor, and
the returned lambda is an embedded functor, the return type is
expressable exactly (without inheritance): -

struct get_inc_by
{
int m_y;

struct impl
{
get_inc_by & scope;
impl( get_inc_by & init_scope ) : scope( init_scope ) {}
int operator()( int x ) { return x + scope.m_y; }
};

impl operator()( int y ) { m_y = y; return impl( *this ); }
};

This is easier in C++ than in the syntax we've been discussing. The
reason is that the return type of get_inc_by's operator() is in scope
at the function definition. If get_inc_by is defined as a "lambda"
this is not true. C++'s inside out declaration syntax doesn't help, in
fact, if we write it that way, the scope problem resurfaces: -

int lambda impl
(
lambda get_inc_by( int y, static int m_y = y )
{
return impl(); // impl now visible
}
)
( int x )
{
return x + m_y; // problem m_y not in scope ??
};

I'm not entirely sure what can be done here. The main alternative
seems to be a special rules allowing the typedef equivalent of "member"
whereby a lambda can define it's own return type internally: -

result_type lambda get_inc_by( int y, static int m_y = y )
{
return int lambda result_type( int x ) { return m_y + x };
};

That seems fairly succinct. And the need for lambdas to return other
lambdas seems a worthy aim.


> > 2) With all the talk about RVO going on in another thread, why not
> > hold the result in a member variable and return by reference.
>

> I don't see any reason to forbid it, but neither do I think this
> justifies "member". "Result" looks like a separate issue not directly
> concerned with lambdas. That is, you should be able to use them in
> normal functions and they should be thrashed out in that context
> first.

Yes, it could be left as an optimisation to the programmer, anyway: -

ut & lambda add_two( const ut & x,
const ut & y,
static ut result = ut( x + y ) )
{
return result;
}

This is probably more in line with C++'s out in the open spirit.

Tom Payne

unread,
Dec 22, 1999, 3:00:00 AM12/22/99
to
In comp.lang.c++.moderated Andy Glew <gl...@cs.wisc.edu> wrote:

>> > As far as I can tell, the main case where you really need "member"
>> > and copy semantics is when the enclosing scope does not have a long
>> > enough lifetime, which is to say, when the lambda passed outside of
>> > the scope in which it is defined. Did I miss this case being
>> > addressed in this thread?
>>
>> It has been mentioned: A comment by Andy Glew that passing lambdas
>> around necessitates inheritance, and by others that life time issues
>> are the responsibility of the programmer. I agree with both of these.

> ??

> I never said anything about lambdas *requiring* inheritance.

Often lambdas need to be derived from unary_function or
binary_function and to inherit their traits.

Tom Payne

Tom Payne

unread,
Dec 23, 1999, 3:00:00 AM12/23/99
to
Gavin Collings wrote:
: Tom Payne <t...@roam-thp2.cs.ucr.edu> wrote:
: > I think it is useful to consider the problem of defining bind2nd().
: > Mathematically, bind2nd() is:
: >
: > lambda(f,y){ lambda(x){ f(x,y) } }
: > ^^^^^^^^^^^^^^^^^^^
: > an expression with free variables f and y

[...]

: Naturally, it should be possible to parameterize "named lambdas" just


: as classes and functions (well more like functions actually - see below)

[...]

: Implementation in Proposed Syntax
: ---------------------------------

We should be able to get by with something less complex, e.g.:

template<class BinOp>
BinOp::result_type(BinOp::first_argument_type) // result-type
bind2nd( const BinOp& f, const BinOp::second_argument_type& y ) // params
{ // body
return // a unary_function
BinOp::result_type // having this result type
( BinOp::first_argument_type& x ) // and this parameter list
{ // and this body
return f(x,y);
}
;
}

Specifically, I'm suggesting that:

* C-style type expressions be allowed wherever an equivalent type
name would be permitted.

* A function declaration with the identifier removed be allowed
wherever the corresponding defined function name would have been
allowed.

* A unary (respectively, binary) function be considered to be a
unary_function (respectively, binary_function) and be considered to
have the corresponding traits.

* Bindings established by parameter lists persist in objects
constructed in the scope of those bindings.

Lambda expressions that bind some of their variables to external
expressions (lvalues as well as rvalues) into standard C++ can be
translated into standard C++ by adaptations of the STL implementation
of bind1st and bind2nd.

Tom Payne

Tom Payne

unread,
Dec 23, 1999, 3:00:00 AM12/23/99
to
In comp.lang.c++.moderated Gavin Collings <gcol...@sperry-sun.co.uk> wrote:
> In article <memo.19991216...@btinternet.com>,
> bran...@cix.co.uk wrote:
>> gcol...@sperry-sun.co.uk (Gavin Collings) wrote:

> I do feel the requirement to carry state is an important
> one, though.

It's essential if we want to bind parameters. Consider
lambda(x){a*x}. Either this is a functor class, whose instances are
parameterized via a, or we have to have separate entry points for the
functions lambda(x){1*x}, lambda(x){2*x}, etc. -- not a practical
alternative.

> And I would like to kill two birds with one stone - lambdas and
> functors.

Lambda abstraction binds variables in a function body to parameters in
a parameter list. We already know how to bind parameters to external
expressions (both lvalues and rvalues), e.g., bind1st binds the first
of two parameters and bind2nd binds the second of two. One way to
proceed is to first bind all of the variables of interest to parameters
and then bind the appropriate parameters to whatever expressions. For
instance to translate the function

template<class T>
T(T) // result-type
affine( T a, T b ) // parameter list
{
return T( T x ){ return a*x+b; }; // body
}

into C++, we can place the following definitions at file scope:

template<class T0, class T1, class T2, class T3>
struct Lambda : ternary_function<T1,T2,T3,T0> {
T0 operator()( T1 a, T2 x, T3 b ){ return a*x+b; }
}; // <-----body---->

template<class T>
binder1of2<T,T> affine( T a, T b ){
return bind1of2( bind3of3( Lambda<T,T,T,T>(),b),a);
}

In general, Lambda is a template functor that parameterizes all
variables that are to be bound to paramters or to expressions. We
then use bindNofM to bind the parameters corresponding to the latter
to their respective expressions. The template function simply infers
the appropriate type parameters for Lambda's default constructor.
(Let's say that bindNofM binds by reference and implement vbindNofM
for binding by value.)
[...]


> No, I really meant that, even in this day of generic programming, some
> code relies on virtual functions, and a virtual overloaded operator()
> should be checked against the super-type.

IIRC, unary_function doesn't have an operator() -- much to my surprise.

Tom Payne

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]
[ about comp.lang.c++.moderated. First time posters: do this! ]

[ comp.std.c++ is moderated. To submit articles, try just posting with ]

Andy Glew

unread,
Dec 24, 1999, 3:00:00 AM12/24/99
to

> > I'm not entirely comfortable with this aim. I suppose I think that if
> > these things have their own state then they're full objects and ought
> > to be treated as such.
>
> It does feel strange, but possibly no more so than statics in
> functions. I do feel the requirement to carry state is an important
> one, though. And I would like to kill two birds with one stone -
> lambdas and functors. Consider the alternatives: Constructor style
> initialisers are attractive, but not general since type specifiers are
> missing.


My viewpoint is that we are really creating objects.
Lambda functors, lambda functions with state, are just objects;
lambda functions without state can be considered degenerate
objects.

Why not reuse object declaration and definition syntax?

The only awkwardness is that initializing data members
of the object is awkward, requiring a level of indirection.

Which leads me back to my proposal, of simply allowing
"in-line" initializations of class data members:

struct lambda {
int& x = enclosing_local_scope_x;
int operator(int y) { return y*x; }
}

as equivalent to the lambda-ful

int lambda( int y ) { return x * y; }

which offers less control over binding of non-parameters.

Moreover, this object-oriented lambda is more flexible
- you can use it to define systems of lambdas, functions that call
each other, rather than being arbitrarily limited.

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]
[ about comp.lang.c++.moderated. First time posters: do this! ]

[ comp.std.c++ is moderated. To submit articles, try just posting with ]

Dave Harris

unread,
Dec 28, 1999, 3:00:00 AM12/28/99
to
gl...@cs.wisc.edu (Andy Glew) wrote:
> struct lambda {
> int& x = enclosing_local_scope_x;
> int operator(int y) { return y*x; }
> }

OK... looking at this properly... I do quite like it. Some comments:

I'd prefer to drop the enclosing_local_scope part. Previously you seemed
to have it as a reserved word which used the scope disambiguation syntax.
I think we can just require the functor to avoid reusing names of
variables from the enclosing scope if it wants to get at them.

Do we actually need the word "lambda"? Can we not make an anonymous class
by making a normal class without a name?

Would the "int &x = " syntax for simultaneously declaring and initialising
a member extend to normal classes? What would happen if the class also had
explicit constructors?

struct Test1 {
int m_x( 1 ); // OK; implicit Test1::Test1().
};

struct Test2 {
int m_x( 1 );
Test2() : m_x( 2 ) (} // ??
Test2( int x ) : m_x( x ) (} // ??
Test2( const Test2 &rhs ) (} // ??
};

It might be reasonable to treat it as a kind of default which was used
only for enclosing constructors which did not initialise it explicitly. Or
perhaps it would be clearer if explicit initialisation was forbidden. (You
could of course assign to it in the body of the constructor.)

I think this *would* be useful generally. It ensures that the variable is
always initialised even if someone adds a new constructor and forgets it.
It avoids repeating the initialisation value many times. It's not just a
short hand.

Finally, I would still like to be able to get at variables from the
enclosing scope without declaring a member for them explicitly. And if
this is done, I still think it should have reference semantics.

Putting these suggestions together:

double average( const std::vector<int> &vec ) {
double sum = 0;
std::for_each( vec.begin(), vec.end(), struct {
void operator() ( int x ) {
sum += x;
}
} );
return sum / vec.size();
}

struct functor {
virtual float operator()( int x ) = 0;
virtual ~functor() {};
};

functor *get_inc_by( float y ) {
return new struct : functor {
float m_y = y;
float operator()( int x ) {
return m_y + x;
}
};
}

If this can be made to work, it avoids adding any new keywords. I think
most of the semantics can be figured out from the syntax. It's not as
concise as I would like, but that's hard to get in a manifestly typed
system.

Dave Harris, Nottingham, UK | "Weave a circle round him thrice,
bran...@cix.co.uk | And close your eyes with holy dread,
| For he on honey dew hath fed
http://www.bhresearch.co.uk/ | And drunk the milk of Paradise."

---

f...@stlport.org

unread,
Dec 28, 1999, 3:00:00 AM12/28/99
to
Guys, I am surprised we are talking about non-reference binding
at all - what if lambda wants to _modify_ non-parameter variable
from enclosing scope ? I think this is much more serious concern
that the speed which could only be slightly better for builtin types
and worth for others. I also believe that referring to enclosing-scope
variables should work exactly as if they would be referred from, say,
an inner block - that is intuitive and calls for references. Sure
compilers might be free to detect that the builtin variables are not
modified within the lambda and optimizing it with pass-by-value, same
way as they should be free to determine them being constant.

I also think introducing initialization syntax to initialize any
extra variables from external ones is reduntant and confusing
(at least to the parser) - if there is a need to keep some initial
values, why not defining extra variable in the enclosing scope,
assign it before use of lambda and refer to it then ?

I like the following syntax :

....
int y;
// this is what I mean about initialization syntax
int y_at_first_call = y;

lambda<bool>(const int& x) {
--y;
return ( x < y || x > y_at_first_call);
}


That sounds reasonably straightforward and simple to implement,
and it adds only one new keyword "lambda".

Layout of resulting functor object is obvious :

struct lambda_obj {

typedef bool result_type;
// not sure about nth_argument_type for n > 2
typedef const int& first_argument_type;

int& __y;
int& __y_at_first_call;

lambda_obj(int& y, int y_at_first_call) : __y(y),

__y_at_first_call(y_at_first_call) {}

bool operator (first_argument_type __x ) {
--__y;
return ( __x < __y || __x > __y_at_first_call);
}
};

At the point of creation, compiler should call lambda_obj
constructor with references to corresponding variables from
external scope passed.


-Boris.

> gl...@cs.wisc.edu (Andy Glew) wrote:
> > Making reference binding the default rather than value-copy binding
> > would violate the C++ principle of not paying for anything unless you
> > use it, since pass by reference for simple scalar (int, float, double)
> > is significantly slower than pass by value.
>

> I don't agree with this argument. Pass by reference may be slower for some
> scalar types but it is typically quicker for large types. It's swings and
> roundabouts.
>
> A reference is a new name for an object rather than a new object, so it is
> conceptually cheaper than a copy. (Admittedly in some places the
> difference can be optimised away with either scheme.)
>

Sent via Deja.com http://www.deja.com/
Before you buy.

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]
[ about comp.lang.c++.moderated. First time posters: do this! ]

[ comp.std.c++ is moderated. To submit articles, try just posting with ]

Tom Payne

unread,
Dec 30, 1999, 3:00:00 AM12/30/99
to
In comp.lang.c++.moderated f...@stlport.org wrote:
> Guys, I am surprised we are talking about non-reference binding
> at all - what if lambda wants to _modify_ non-parameter variable
> from enclosing scope ? I think this is much more serious concern
> that the speed which could only be slightly better for builtin types
> and worth for others. I also believe that referring to enclosing-scope
> variables should work exactly as if they would be referred from, say,
> an inner block - that is intuitive and calls for references.
[...]

The underlying question seems to be: What should be the meaning of
the x in int(int y){return x*y;}?

1) Those who like the nested functions of say Pascal will prefer
that x refer to whichever of its namesakes is visible at the
construction of int(int y){return x*y;}.

2) The rest prefer x to be a subobject initialized with the
instantaneous value/binding of that namesake.

Either approach can represent other. In the first case, we can
represent the int(int y){return x*y;} of the second via

bind1st( int(int x,int y){return x*y;}, x )

In the second case, we can represent the int(int y){return x*y;} of
the first via

int(int)( int& x ){ return int(int y){return x*y;}; }( x )

Under the second approach, any binding for x can be passed as an
argument to the outer lambda function --- so lambda notation can offer
the same control over binding as "in-line" initialization of class
data members.

Also, under the second approach, bind1st has a lambda definition:

template<Op>
unary_function<Op::second_argument_type,Op::result_type>
(Op f, Op::first_argument_type x)
{ return Op::result_type(Op::second_argument_type){return f(x,y);}; }

while, under the first approach, a lambda function has no state (and
nowhere to store bindings).


There is a third approach, the C++ default, which IIRC is like the
first approach but prohibits locally defined classes from (directly)
referring to local variables. In such a case,

bind1st( int(int& x,int y){return x*y;}, x )

establishes a binding of the first kind, while

bind1st( int(int x,int y){return x*y;}, x )

establishes a binding of the second kind.

> I also think introducing initialization syntax to initialize any
> extra variables from external ones is reduntant and confusing

[...]

I agree, and prefer lambda notaion (with the superfluous "lambda"
omitted) over standard functor syntax.

Tom Payne
---

Dave Harris

unread,
Dec 31, 1999, 3:00:00 AM12/31/99
to

f...@stlport.org () wrote:
> if there is a need to keep some initial values, why not defining
> extra variable in the enclosing scope, assign it before use of
> lambda and refer to it then ?

Because of what happens when the life-time of the lambda is longer than
that of the enclosing scope. You get a dangling reference.

The intent of the "get_inc_by" example is to return a functor that
increments its argument by an arbitrary amount. This amount has to be
stored somewhere. It can't be stored in the enclosing stack frame because
the lambda is *returned* by the function which defines it, and the stack
frame is popped.

Dave Harris, Nottingham, UK | "Weave a circle round him thrice,
bran...@cix.co.uk | And close your eyes with holy dread,
| For he on honey dew hath fed
http://www.bhresearch.co.uk/ | And drunk the milk of Paradise."

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]
[ about comp.lang.c++.moderated. First time posters: do this! ]

[ comp.std.c++ is moderated. To submit articles, try just posting with ]

Andy Glew

unread,
Jan 1, 2000, 3:00:00 AM1/1/00
to

Dave Harris <sc...@btinternet.com>

> gl...@cs.wisc.edu (Andy Glew) wrote:
> > struct lambda {
> > int& x = enclosing_local_scope_x;
> > int operator(int y) { return y*x; }
> > }
>
> OK... looking at this properly... I do quite like it. Some comments:
>
> I'd prefer to drop the enclosing_local_scope part. Previously you seemed
> to have it as a reserved word which used the scope disambiguation syntax.
> I think we can just require the functor to avoid reusing names of
> variables from the enclosing scope if it wants to get at them.

That's a reasonable choice.

I just think it inconsistent, given that I can have conflicting names
in so many other places - e.g. I can do
class c {
int x;
void set_x( int x ) { this->x = x; }
};

Actually, hmm... isn't this already correct C, if I recall?

int x;
{
int x = x;
...
}

If so, then we have a precedent that allows access to the enclosing
scope's value, even if names are conflicting. I'd still prefer a disambiguating
value.


> Do we actually need the word "lambda"? Can we not make an anonymous class
> by making a normal class without a name?

struct {


int& x = enclosing_local_scope_x;
int operator(int y) { return y*x; }
}

works. But, in my existing work with lambdas using virtual functions,
I find that I often need to use inheritance.

struct : public base_class {


int& x = enclosing_local_scope_x;
int operator(int y) { return y*x; }
}

does not seem to be accepted (at least not by GCC).

> Would the "int &x = " syntax for simultaneously declaring and initialising
> a member extend to normal classes?

I would hope so.

> What would happen if the class also had
> explicit constructors?
>
> struct Test1 {
> int m_x( 1 ); // OK; implicit Test1::Test1().
> };
>
> struct Test2 {
> int m_x( 1 );
> Test2() : m_x( 2 ) (} // ??
> Test2( int x ) : m_x( x ) (} // ??
> Test2( const Test2 &rhs ) (} // ??
> };
>
> It might be reasonable to treat it as a kind of default which was used
> only for enclosing constructors which did not initialise it explicitly. Or
> perhaps it would be clearer if explicit initialisation was forbidden. (You
> could of course assign to it in the body of the constructor.)
>
> I think this *would* be useful generally. It ensures that the variable is
> always initialised even if someone adds a new constructor and forgets it.
> It avoids repeating the initialisation value many times. It's not just a
> short hand.

Exactly.

I presently use a template to accomplish the same thing.
The template provides a default value for a member;
the value can be overridden in an explicit constructor.

struct Test1 {
init<int,1> m_x;
};

struct Test2 {
init<int,1> m_x;
Test2() : m_x( 2 ) (} // overrides
Test2( int x ) : m_x( x ) (} // overrides
Test2( const Test2 &rhs ) (} // overrides
};

Thanks for pointing out the use of initializer syntax.


> Finally, I would still like to be able to get at variables from the
> enclosing scope without declaring a member for them explicitly. And if
> this is done, I still think it should have reference semantics.

I think this would amount to implicitly defining a member.
Possible, although I could live without it.

> Putting these suggestions together:
>
> double average( const std::vector<int> &vec ) {
> double sum = 0;
> std::for_each( vec.begin(), vec.end(), struct {
> void operator() ( int x ) {
> sum += x;
> }
> } );
> return sum / vec.size();
> }
>
> struct functor {
> virtual float operator()( int x ) = 0;
> virtual ~functor() {};
> };
>
> functor *get_inc_by( float y ) {
> return new struct : functor {
> float m_y = y;
> float operator()( int x ) {
> return m_y + x;
> }
> };
> }
>
> If this can be made to work, it avoids adding any new keywords. I think
> most of the semantics can be figured out from the syntax. It's not as
> concise as I would like, but that's hard to get in a manifestly typed
> system.

I like your modifications.

I note that you, too, are using inheritance. As I say, GCC rejects
struct : functor { ... }
Q: how much violence would it be to the language syntax to accept it?

Tom Payne

unread,
Jan 1, 2000, 3:00:00 AM1/1/00
to

In comp.lang.c++.moderated Tom Payne <t...@roam-thp2.cs.ucr.edu> wrote:

> There is a third approach, the C++ default, which IIRC is like the
> first approach but prohibits locally defined classes from (directly)
> referring to local variables. In such a case,

> bind1st( int(int& x,int y){return x*y;}, x )

> establishes a binding of the first kind, while

> bind1st( int(int x,int y){return x*y;}, x )

> establishes a binding of the second kind.

And , here is some code to illustrate this point.

Tom Payne

=====================================================================

// The following code shows how to bind non-parameters by making them
// parameters and then binding them using a refinement of bind1st.
// It also provides a hack that allows lambdas implemented via Andy
// Glew's statement-expression hack to be pass as parameter arguments.
// It compiles and runs correctly under with egcs 1.1.2 on Redhat 6.0.


#include <iostream>
#include <algorithm>
#include <list>
#include <utility>
#include <functional>


// A hack to deal with the fact that binary_function lacks operator().


template<class T0, class T1, class T2>

struct binary_functor : binary_function<T1,T2,T0> {
virtual T0 operator() ( T1 x, T2 y ) const = 0;
};


// A hack to upcast a reference to a lambda (of a local class) to a
// reference to a binary_functor.
template<class T1, class T2, class T3>
const binary_functor<T1,T2,T3>&
upcast( const binary_functor<T1,T2,T3>& g ){
return g;
}


// A modification (hack) of binder1st to keep lambda objects from getting
// sliced (compare to implementation on p.519 of Stroustrup's 3rd edition)
template <class BinOp>
class binder
: unary_function<
typename BinOp::second_argument_type,
typename BinOp::result_type
>
{
protected:
const BinOp& f; // Doesn't slice f, but does it dangle?
typename BinOp::first_argument_type x;
public:
binder(const BinOp& f, typename BinOp::first_argument_type x) : f(f), x(x) {}
result_type operator() (const typename BinOp::second_argument_type y) const {
return f(x,y);
}
};


// A modificaiton (hack) of bind1st to control the type of x. Also, the
// parameter order has been reversed (for syntactic asethetics).
template <class BinOp>
binder<BinOp>
bind( typename BinOp::first_argument_type x, const BinOp& f ) {
return binder<BinOp>(f,x);
}


main() {

list<int> q; // Create the list < 1, 2, 3, 4 >.
q.push_back(1);
q.push_back(2);
q.push_back(3);
q.push_back(4);

//
// To sum that list, we implement
//
// int x = 0;
// for_each( q.begin(), q.end(), bind( x, void(int& x,int y){x+=y;} ) );
//
// via the statement-expression hack.
//

int x = 0; // local variable to which the lambda's x will refer.

for_each( q.begin(), q.end(),
bind(
x,
upcast( // hack to allow lambda as parameter argument to bind
({ // GNU statement-expression (to isolate Lambda and f)
struct Lambda : binary_functor<void, int&, int> { // local class
void operator()( int& x, int y ) const { // parameterize x and y
x+=y; // function body with free variables x and y
} // x got parameterized by reference, and y by value.
} f; f; // f is local to (and the value of) the statement exp.
}) // end GNU statement-expression
) // end upcast
) // end bind
); // end for_each

cout << "The sum is " << x << "." << endl;

// Of course, we can
#define lambda upcast( ({ struct Lambda
// and
#define adbmal f; f; }) )
// and then write

for_each( q.begin(), q.end(),
bind( x, lambda : binary_functor<void, int&, int> {
void operator()( int& x, int y ) const {
x+=y;
}
} adbmal )
);

// which is almost readable. ;-)

cout << "And now the sum is " << x << "." << endl;

Andy Glew

unread,
Jan 2, 2000, 3:00:00 AM1/2/00
to
> Guys, I am surprised we are talking about non-reference binding
> at all - what if lambda wants to _modify_ non-parameter variable
> from enclosing scope ? I think this is much more serious concern
> that the speed which could only be slightly better for builtin types
> and worth for others.

Lambdas defined using reference binding to variables in the
local scope cannot be used when the local scope closes.

I.e. you cannot use such a lambda elsewhere.

IMHO this is a most serious concern. We *must* have value
binding to permit lambdas to be useful outside the current scope.

Similarly, I agree that we should have reference binding,
for the reason you mention.

Lambdas need *both* value and reference binding.
The question then becomes how to express them both.
---

Dave Abrahams

unread,
Jan 3, 2000, 3:00:00 AM1/3/00
to

Sort of on a tangent, but has anybody looked at The Binder Library
<http://www2.cs.utu.fi/BL/>? A most excellent piece of work, IMO.

-Dave

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]
[ about comp.lang.c++.moderated. First time posters: do this! ]

[ comp.std.c++ is moderated. To submit articles, try just posting with ]

Dave Abrahams

unread,
Jan 5, 2000, 3:00:00 AM1/5/00
to
You know, Python has a limited form of lambda, but turns out to be every bit
good enough. You can't access variables from enclosing scopes, but you can
get values from the enclosing scopes by binding default arguments:

lambda x, y = y_from_enclosing_scope: x + y

It's kind of an ugly hack, but you get used to it, and it's completely
adequate for most uses. I'm afraid C++ just doesn't give us what we need to
make a similar compromise.

I am utterly unworried about the fact that a bound auto variable's lifetime
would end with its scope even if the lambda function passed out of the
scope. This is absolutely C++-like in every respect and would surprise no
one, unless we insist on using the word 'lambda'. That would make people
expect it to work like lambda does in other languages.

Tom Payne

unread,
Jan 5, 2000, 3:00:00 AM1/5/00
to
In comp.lang.c++.moderated Andy Glew <gl...@cs.wisc.edu> wrote:
[...]

> Lambdas need *both* value and reference binding.
> The question then becomes how to express them both.

What wrong with parameterizing (via lambda abstraction) the variable
to be bound and then binding its parameter via (a suitable refinement
of) bind1st?

Tom Payne

f...@stlport.org

unread,
Jan 5, 2000, 3:00:00 AM1/5/00
to
I do not think C++ needs lambdas which might be used
after enclosing scope exits. I think named functions
would do just fine for those cases.

I do think having both bindings is extremely confusing.
Namely, I think having value binding is confusing, since,
for C++ programmer, it is very counterintuitive having
another copy of the object without explicitly declaring it
to be a copy.

-Boris.

In article <84hcam$h...@spool.cs.wisc.edu>,


"Andy Glew" <gl...@cs.wisc.edu> wrote:
> Lambdas defined using reference binding to variables in the
> local scope cannot be used when the local scope closes.
>
> I.e. you cannot use such a lambda elsewhere.
>
> IMHO this is a most serious concern. We *must* have value
> binding to permit lambdas to be useful outside the current scope.
>
> Similarly, I agree that we should have reference binding,
> for the reason you mention.
>

> Lambdas need *both* value and reference binding.
> The question then becomes how to express them both.

Sent via Deja.com http://www.deja.com/
Before you buy.

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]

Dave Harris

unread,
Jan 5, 2000, 3:00:00 AM1/5/00
to
gl...@cs.wisc.edu (Andy Glew) wrote:
> I just think it inconsistent, given that I can have conflicting names
> in so many other places

Not everywhere, though. For example, in:
int x = 1;


{
int x = x;
...
}

Although the syntax is allowed, I think you misunderstood what it does.
You cannot refer to the outer x within the braces, and the inner x is
being initialised to itself (so will probably not get the value 1). There
is no way to resolve the name conflict in favour of the outer x. It's not
really a problem because the inner x has very small scope and we can
rename it without upsetting a lot of code.

This is the sort of semantics I expected from lambdas. I had thought that
lambdas, being anonymous, would produce the same small scopes and so
avoiding name collisions would be easy. I am having second thoughts,
though, because of inheritance. See below.


> > Finally, I would still like to be able to get at variables from the
> > enclosing scope without declaring a member for them explicitly. And if
> > this is done, I still think it should have reference semantics.
>
> I think this would amount to implicitly defining a member.
> Possible, although I could live without it.

Yes. The main reason for having two variables with the same name is when
they are the same thing. I had thought this proposal would reduce name
clashes and the need for the enclosing_scope qualifier, because we would
not need so many explicit members. With implicit members the compiler
resolves the clash.

I'm now concerned about how these two parts interact with inheritance. In
effect, local variables hide the inherited names. Consider:

class Base {
public:
virtual int operator()( int y ) const = 0;
int x;
};

void use_lambda( const Base & );

void test() {
int x = 1;

use_lambda( struct : Base {
int old_x( x ); // Which x?
virtual int operator()( int y ) const {
return x + y + old_x; // Which x?
}
} );
}

This can be made to work. I would expect use of the x in the operator() to
refer to an implicit member int &x which referred to the local x, not
Base::x. Similarly the initialiser of old_x would actually be an argument
of the implicit constructor. Neither would use Base::x, and commenting out
Base::x would not change the meaning of the code. Commenting out the local
x would. The correct (IMHO) semantics drops out of the rewrite rule.

However, is it clear enough? The lambda can use Base variables and it may
not be obvious where they are coming from or that local variables of
test() will mask them. Requiring the members to be created explicitly
doesn't seem to help. The root seems to be the subtle difference in the
generated code between:

struct __lambda : Base {
int y;
__lambda( int &z ) : y(x) {} // (1)
__lambda( int &x ) : y(x) {} // (2)
};

in the first case, y is initialised to the inherited member. In the
second, y gets the constructor argument. I think this is fairly
error-prone in the language as it stands, and we risk lambdas propagating
the problem.

I am wondering whether enclosing_scope should be *required*, in other
words whether local variables should be out of scope inside the lambda.
Then they don't hide inherited variables. The above would be written as:

void test() {
int x = 1;

use_lambda( struct : Base {
int old_x( enclosing_scope::x );
virtual int operator()( int y ) const {
return enclosing_scope::x + y + old_x;
}
} );
}

Of course, you would be able to create a member that referred to
enclosing_scope::x explicitly if you wanted, eg to give it a shorter name.
You could even use the name 'x', which gives what you had before.

I don't know; maybe in practice conflicts with inherited names won't be
common enough to justify a new keyword and the extra verbosity. An
alternative would be to rule the case where a local variable has the same
name as an inherited one as ambiguous, requiring renaming. This is simple
yet safe.


> struct : public base_class {...};


>
> does not seem to be accepted (at least not by GCC).

It could well need another language change. I presume there's no technical
reason why it couldn't be done, although I've not tried implementing it
myself.

Is it the correct thing to do from a usability point of view? I suspect
the people who don't like C++ will say it is more obscure syntax, and that
a new keyword would be clearer.

An alternative would be to reuse "void". Thus:

struct void : public base_class {...};

Here we are saying that the struct has no name, just as
void proc( void );

says that proc takes no arguments and returns no result. It has a certain
charm, to the right sort of twisted mind :-)

Dave Harris, Nottingham, UK | "Weave a circle round him thrice,
bran...@cix.co.uk | And close your eyes with holy dread,
| For he on honey dew hath fed
http://www.bhresearch.co.uk/ | And drunk the milk of Paradise."

---

Andy Glew

unread,
Jan 11, 2000, 3:00:00 AM1/11/00