Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Lambda esxpressions (i.e. anonymous functions) in C++

65 views
Skip to first unread message

Pedro Blanco

unread,
Aug 20, 1999, 3:00:00 AM8/20/99
to
I bet that more than a C/C++'er, especially when confronted to STL's
functionals and functors, to such long-lasting marvels as LISP, and to
languages such as Haskell, wonders why hasn't anybody given C/C++ the notion
of lambda expressions.

I can go back to the much simpler C in order to explain this possibility, by
way of an example: the usual sorting function accepts a pointer to a
function responsible for comparing two generic (in C terms) values. I don't
remember the exact prototype, but it's close to:

| void sort( void *array,
| size_t length,
| size_t size,
| int (*compare)( const void *, const void *) );

The usual pattern for using such a function might not be too different from:

| static int myCompare( const void *a, const void *b )
| {
| /* somehow return -1 if "a < b", 0 if "a == b", 1 if "a > b" */
| }
|
| /* and then, at some point: */
|
| sort( myArray, itsLength, elemSize, myCompare );

The point is this: the language is so limited that forces me to define a
function I am using only once. It's as if I couldn't use immediates, but I
always had to use definitions instead. Imagine that, instead of

| int a = b + 3;

I had to write somthing like

| static int three { 3 }
|
| /* and later: */
|
| int a = b + three;

I think the problem boils down to this:

| in C/C++, *a statement is not an expression*

And that's a pitty, because K&R C was very close to achieving this. Let me
introduce a different flavor of C. Let's say you don't have the ";"
terminator, as used in:

| b = 2*a;
| c = sin(b); /* both statements have no value as a whole */

but you only had the "," separator. Then, any compound statement would have
a value:

| b = 2*a,
| c = sin(b) /* the whole thing has a value, that of {d} */

Now, let's do away with "{" ans "}" too. Then, you use "(" and ")" to group
simple statements into compound ones:

| ( b = 2*a, c = sin(b) ) /* still the same value, right? */

So far we are not adding any new semantics to C, just removing stuff, so I
hope you are still with me. As a next step, we remove, of all things,
"return". A function has no more "return value", but just "value", as any
other (simple or compound) statement:

| float sinOfDouble( float a ) ( float b = 2*a, float c = sin(b) )
| /* mind the "("s instead of "{"s */

In the process, I introduced (the first new thing) "variable declaration as
expression". I bet it's easy to see that a declaration can have a value too
(even if it's an unpredictable in the case of uninitialized declarations).

In case you wonder what would happen to "if .. then .. else", well, what the
heck, let's eliminate it too; C's conditional expression has always been
good enough. So, instead of

| int max( int a, int b )
| {
| if( a > b )
| return a;
| else
| return b;
| }

we say

| int max( int a, int b ) ( a > b ? a : b )

Almost anybody I know agrees that the conditional expression could have a
nicer sintax, but that would be easy to fix (for instance, bringing back the
"if then else" keywords). Now, we are ready to wipe out function definitions
too, so, instead of the definition for "sinOfDouble" above, I would write:

| float sinOfDouble( float a ) = ( float b = 2*a, float c = sin(b) )

the only difference being the "=". But an important one, because now I'm
only declaring a variable (of type "float(*)(float)") and initializing it.
Finally, let me introduce a keyword, "function", say (the second --and
last-- novelty). The declaration-plus-initialization above would become:

| float sinOfDouble = function( float a )
| ( float b = 2*a, float c = sin(b) )

That's it! We can finally have anonymous functions! Back to the use of sort
above, we can now say:

| sort( myArray,
| itsLength,
| elemSize,
| function(const void *a, const void *b)( /* code here */ ) )

If, instead of "function", you say "lambda", that's what LISPers say, but
otherwise there's not much of a difference. There are, of course, open
issues, such as what to do with "do..while" and the like, but I'm sure there
are solutions (and, in the meantime, we can [guess!] get rid of them too and
survive using recursion and tail-recursion optmimizing compilers).

Also, now that we can have functions as expressions and assign them to
variables, another subtlety is the binding of free ("external" in C
parlance) variables inside functions -- static, dynamic, or what. Of course,
we would use the nastiest semantics provided it gave the fastest execution,
which is what C is all about, isn't it?

[Note: I said above that all this is closer to K&R C, as opposed to ANSI C,
because, since any expression has a value, there's no room for "void",
especially as a return type for functions; expressions as here defined,
including functions, have *no choice* but to have a value]

There are important ideas that come up if one pictures a C++ that came from
such a C. For example, subclasses that in fact should be simple instances,
if only one could express the fact. Or method update. Once statements are
expressions, code and data become closer to each other, and the language
becomes more powerful.


Comments?

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

John Harrison

unread,
Aug 22, 1999, 3:00:00 AM8/22/99
to
The trouble with all this is that people like ornate syntax, they find
it reassuring and therefore easier to understand. The simplest language
I know is Scheme but that doesn't mean its easy.

jk

unread,
Aug 22, 1999, 3:00:00 AM8/22/99
to
On 20 Aug 1999 22:58:34 -0400, "Pedro Blanco" <pbl...@jet.es> wrote:

[major snip]
|Comments?

Neat! A very nice way for C/C++ to go rather than additional of this templatemania
that tend to obscure C++ (although I wouldn't want to be without it).

One of the issues of C is of course that is quite easy to learn considering
most source code built with the current syntax (C++ really doesn't fit that anymore).
I'm not sure C source code with the lambda concept embedded will sustain this.

Maybe someone can rewrite the whole thing and call it Lambda C (LC/LC++)?

/jk

Martin Fabian

unread,
Aug 23, 1999, 3:00:00 AM8/23/99
to
Pedro Blanco wrote:
<a nice intro on lambda calculus, thanks>

Isn't all this made available in C++ by functors and templates, and in a
typesafe manner as well?

class SinOfDouble
{
float val; // in case it's needed later
public:
float operator()(float a) { return val = sin(2*a); }
};


--
Martin Fabian http://www.s2.chalmers.se/~fabian/
--
"Cheer up. It may never happen" (Edina Monsoon)

/* Remove NOSPAM from reply-to address to mail me */

Andrew R. Thomas-Cramer

unread,
Aug 23, 1999, 3:00:00 AM8/23/99
to

One of the benefits of lambda functions can be increased readability, since
tiny, single-use functions can be defined at the point of use. (Of course,
they can also decrease readability, testability and re-usability.)

In C++, this benefit could be approached by defining the function _just
before_ the point of use, in a local class.

Pedro Blanco wrote in message <007e01beeaaf$5c233460$7d9c37c3@mindanao>...


>I bet that more than a C/C++'er, especially when confronted to STL's
>functionals and functors, to such long-lasting marvels as LISP, and to
>languages such as Haskell, wonders why hasn't anybody given C/C++ the
notion
>of lambda expressions.
>

Siemel B. Naran

unread,
Aug 24, 1999, 3:00:00 AM8/24/99
to
On 23 Aug 1999 14:36:26 -0400, Martin Fabian

>Isn't all this made available in C++ by functors and templates, and in a
>typesafe manner as well?
>
>class SinOfDouble
>{
> float val; // in case it's needed later
>public:
> float operator()(float a) { return val = sin(2*a); }
>};

Now if local classes had internal linkage, then we could use these local
classes in calls to template functions. This would be nice.
void f(const Container& c) {
class SinOfDouble { ... };
std::for_each(c.begin(),c.end(),SinOfDouble());
}
void f2() { /* don't know SinOfDouble */ }

It's not absolutely necessary because we can use classes in unnamed
namespaces, although this doesn't give us as much locality of reference.
That is, the class is available to many functions, not just one.
namespace { class SinOfDouble { ... }; }
void f(const Container& c) {
std::for_each(c.begin(),c.end(),SinOfDouble());
}
void f2() { /* can use SinOfDouble */ }

It would be nice if local classes could access the local context, just
like lambda functions in Scheme. But working out this idea is a major
challenge.

--
----------------------------------
Siemel B. Naran (sbn...@uiuc.edu)
----------------------------------

Robin Rosenberg

unread,
Aug 24, 1999, 3:00:00 AM8/24/99
to

>>>>>>>>>>>>>>>>>> Ursprüngliche Nachricht <<<<<<<<<<<<<<<<<<

Am 8/21/99, 3:58:34 AM, schrieb "Pedro Blanco" <pbl...@jet.es> zum
Thema Lambda esxpressions (i.e. anonymous functions) in C++:


> I bet that more than a C/C++'er, especially when confronted to STL's
> functionals and functors, to such long-lasting marvels as LISP, and to
> languages such as Haskell, wonders why hasn't anybody given C/C++ the
notion
> of lambda expressions.

This is experimental. Assume the following command using the files
below:
The whole LAMBDA expression must be on lines of its own. The
extraction is
based on counting braces.

Comments are welcome. I'm not using it myself, at least not yet.

./lcpp.pl x.lcpp >x.cpp && g++ x.cpp && ./a.out

------x.lcpp--------
#include <iostream>
#include <vector>
#include <algorithm>
#include <iterator>

int main()
{
vector<int> v;
v.push_back(1);
v.push_back(3);
v.push_back(10);
v.push_back(0);

cout << "unsorted:";
copy(v.begin(), v.end(), ostream_iterator<int>(cout," "));
cout << endl;

sort(v.begin(),
v.end(),
bool LAMBDA(int a,int b) { return a<b; }
);

cout << "sort1:";
copy(v.begin(), v.end(), ostream_iterator<int>(cout," "));
cout << endl;

sort(v.begin(),
v.end(),
bool LAMBDA(int a,int b) {
if (a>b) {
return true;
} else
return false;
}
);
cout << "sort2:";
copy(v.begin(), v.end(), ostream_iterator<int>(cout," "));
cout << endl;
}
------lcpp.pl-------
#!/usr/bin/perl

/*
* Simple hacked preprocessor for LAMBA (anonymous functions
*
* Copyright (C) 1999, Rosenberg IT AB, Sweden
*
* Version: PA1
*
* Disclaimer: This is experimental. Use and change at your own risk
*/

use File::Basename;

@lambda=();
for $file (@ARGV) {
open IN,"<$file";
$line=0;
$funnum=0;
$lastincludeline=1;
@out=();
push @out,"# $line \"$file\"\n";
while(<IN>) {
++$line;
if (m/\#\s+include/) {
$lastincludeline=$line;
}
if (m/^(\s+)(.*)LAMBDA(.*)/) {
$ls=tr/{/{/;
$rs=tr/}/}/;
++$funnum;
push @out,"# $line \"$file\"\n";
push @out,"$1lambda_$funnum\n";
push @out,"# $line \"$file\"\n";
push @lambda,"# ".($line-1)." \"$file\"\n";
push @lambda,"$2\nlambda_$funnum$3\n";
while ($ls gt $rs) {
$_=<IN>;
$ls+=tr/{/{/;
$rs+=tr/}/}/;
push @lambda,$_;
}
} else {
push @out,$_;
}
}
$genf="lamda".basename($file).".h";
$line=0;
for (@out) {
++$line;
print $_;
if ($line eq $lastincludeline+1) {
print "#include \"$genf\"\n";
}
}
close IN;
print STDERR "creating $genf\n";
open OUT,">$genf";
print OUT @lambda;
close OUT;
}

-- robin
Rosenberg IT AB

Sean Kelly

unread,
Aug 24, 1999, 3:00:00 AM8/24/99
to
I can see what you're suggesting and it's quite interesting. It seems to me
that the benefit is that one-use functions can be defined at the point
they're used, though that's not much different than declaring the function
right before the function it's used in:

double oneUseMax( double x, double y ) { x > y ? x : y; }

void myfunc( x, oneUseMax( y ) ) {...}

or even better:

inline double oneUseMax( double x, double y ) { x > y ? x : y; }

void myfunc( x, oneUseMax( y, z ) ) {...}


It seems like lambda functions might have more application with C than C++
though, as it's more function-oriented than object-oriented in its
application. I can't envision many scenarios in C++ where I'd want to
actually use lambda functions, though they're terrific in LISP.

Sean

Paul D. DeRocco

unread,
Aug 27, 1999, 3:00:00 AM8/27/99
to
Sean Kelly wrote:
>
> It seems like lambda functions might have more application with C
> than C++ though, as it's more function-oriented than object-oriented
> in its application. I can't envision many scenarios in C++ where I'd
> want to actually use lambda functions, though they're terrific in
> LISP.

They would be useful anywhere you use a functor, e.g., for_each,
find_if, etc. The bind1st, bind2nd, etc. functions are cute and all, but
they merely use a C++-ism to accomplish what could more clearly be
expressed with a lambda construct. And binders are special-purpose,
turning a two-argument function into a one-argument one, while lambda
constructs are completely general.

--

Ciao, Paul D. DeRocco
Paul mailto:pder...@ix.netcom.com

Wolfgang Grieskamp

unread,
Aug 27, 1999, 3:00:00 AM8/27/99
to
sbn...@uiuc.edu (Siemel B. Naran) writes:

> On 23 Aug 1999 14:36:26 -0400, Martin Fabian
>
> >Isn't all this made available in C++ by functors and templates, and in a
> >typesafe manner as well?
> >
> >class SinOfDouble
> >{
> > float val; // in case it's needed later
> >public:
> > float operator()(float a) { return val = sin(2*a); }
> >};
>
> Now if local classes had internal linkage, then we could use these local
> classes in calls to template functions. This would be nice.
> void f(const Container& c) {
> class SinOfDouble { ... };
> std::for_each(c.begin(),c.end(),SinOfDouble());
> }
> void f2() { /* don't know SinOfDouble */ }

It would be even nicer if there would be really "inner" classes as in
Java which can access the automatic variables of their context:

void f(const Container& c, float a) {
class Add { float operator()(float b){ return b+a; } };
std::for_each(c.begin(),c.end(),Add());
}

The next step then would be to allow the creation of objects of
anonymous classes in expressions:

void f(const Container& c, float a) {
std::for_each(c.begin(),c.end(),
class { float operator()(float b){ return b+a; } }
);
}

A shortcut for defining classes with operator() immediality leads
to lambda's:

void f(const Container& c, float a) {
std::for_each(c.begin(),c.end(),
lambda (float b){ return b+a; }
);
}

(Unfortunately, the Java guys didn't builded that into their language.
They also don't have operator()).


The translation of this "auto" classes would be straighforward
in C++: just add the context parameters such as "float a" as additional
&-parameters to yield a normal static class (called lambda-lifting
in the FP scene, and the technique used by SUN to implement inner
classes in 1.1 without modifying the JVM).


A problem with this approach is that the so-constructed objects became
undefined if they leave the scope of their creation, since they refer
to automatic variables. These are _not_ closures, a concept which is
perhaps not compatible with C++ (garbage collection is f.i. required).
However, for STL programming closures are not actually required,
since the function object's are usually only propagated downwards
the call tree.

Regards,
Wolfgang

--
w...@cs.tu-berlin.de http://uebb.cs.tu-berlin.de/~wg/

Dave Gomboc

unread,
Aug 27, 1999, 3:00:00 AM8/27/99
to
In article <007e01beeaaf$5c233460$7d9c37c3@mindanao>,

"Pedro Blanco" <pbl...@jet.es> wrote:
> I bet that more than a C/C++'er, especially when confronted to STL's
> functionals and functors, to such long-lasting marvels as LISP, and
> to languages such as Haskell, wonders why hasn't anybody given C/C++
> the notion of lambda expressions.

[snip]
> Comments?

Can we curry arbitrary functions too? That would make it really
cool. :-)

Dave

--
Dave Gomboc
da_veATc_sPERIODualb_ertaDOTc_a
(remove underscores and replace MAJUSCULES with punctuation to get email


Sent via Deja.com http://www.deja.com/
Share what you know. Learn what you don't.

Siemel B. Naran

unread,
Aug 28, 1999, 3:00:00 AM8/28/99
to
On 27 Aug 1999 19:59:23 -0400, Wolfgang Grieskamp <w...@cs.tu-berlin.de> wrote:

>It would be even nicer if there would be really "inner" classes as in
>Java which can access the automatic variables of their context:

Good idea, but there's an overhead on people who don't want to use the
feature, or people who need to use the feature only a little. It is
conceivable that we don't need the local class to have access to the
local context. Or maybe the local class needs access to only 3 local
variables.


>The next step then would be to allow the creation of objects of
>anonymous classes in expressions:
>
> void f(const Container& c, float a) {
> std::for_each(c.begin(),c.end(),
> class { float operator()(float b){ return b+a; } }
> );
> }

IMHO, it's not worth it. For classes that do complicated things,
we'd probably want to write out the class definition in full, just
to make it easier to read. For classes that do simple things, like
the one above, we can use the builder objects in <functional>.

using std::plus;
using std::bind2nd;


void f(const Container& c, float a) {
std::for_each(c.begin(),c.end(),

bind2nd(plus<float>(),a)
);
}

>A shortcut for defining classes with operator() immediality leads
>to lambda's:

>The translation of this "auto" classes would be straighforward
>in C++: just add the context parameters such as "float a" as additional
>&-parameters to yield a normal static class (called lambda-lifting
>in the FP scene, and the technique used by SUN to implement inner
>classes in 1.1 without modifying the JVM).

But now sizeof(class) increases. I often write little classes that
don't need access to any of the local variables. I want these
classes to be as little as possible. Sometimes, I need to grant
access to a few local variables.

Besides, is the implicit data member of the local class "float a"
or "float& a" or "const float& a"?


>A problem with this approach is that the so-constructed objects became
>undefined if they leave the scope of their creation, since they refer
>to automatic variables. These are _not_ closures, a concept which is
>perhaps not compatible with C++ (garbage collection is f.i. required).
>However, for STL programming closures are not actually required,
>since the function object's are usually only propagated downwards
>the call tree.

--

----------------------------------
Siemel B. Naran (sbn...@uiuc.edu)
----------------------------------

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

Wolfgang Grieskamp

unread,
Aug 30, 1999, 3:00:00 AM8/30/99
to
sbn...@uiuc.edu (Siemel B. Naran) writes:

> On 27 Aug 1999 19:59:23 -0400, Wolfgang Grieskamp <w...@cs.tu-berlin.de>
wrote:
>
> >It would be even nicer if there would be really "inner" classes as in
> >Java which can access the automatic variables of their context:
>
> Good idea, but there's an overhead on people who don't want to use the
> feature, or people who need to use the feature only a little. It is
> conceivable that we don't need the local class to have access to the
> local context. Or maybe the local class needs access to only 3 local
> variables.

But what variables from the context are used can be easily
checked by the compiler, so there is no overhead if
no context variables are used.

I think it is instructive that I illustrate a possible
compilation. Consider the version using the
desired lambda:

void f(const Container& c, float a) {

float other;
transform(c.begin(),c.end(),
float lambda(float b){ return b+a; } }
);
}

This would translate to:

class Anon {
float &a;
Anon(float &_a) : a(_a) {}
operator()(float b) { return b+a; }
};


void f(const Container& c, float a) {

float other;
transform(c.begin(),c.end(), Anon(a));
}

Thus the variable "other" is not passed to the constructor
of the anonymous class, since it is not used by its code.


> >The next step then would be to allow the creation of objects of
> >anonymous classes in expressions:
> >
> > void f(const Container& c, float a) {
> > std::for_each(c.begin(),c.end(),
> > class { float operator()(float b){ return b+a; } }
> > );
> > }
>
> IMHO, it's not worth it. For classes that do complicated things,
> we'd probably want to write out the class definition in full, just
> to make it easier to read.

I agree, and introduced this step just for orthogonality of explaining
lambads by inner classes.


> For classes that do simple things, like
> the one above, we can use the builder objects in <functional>.
>
> using std::plus;
> using std::bind2nd;
> void f(const Container& c, float a) {
> std::for_each(c.begin(),c.end(),
> bind2nd(plus<float>(),a)
> );
> }

I've done a lot of practical programming with higher-order
functions. The libraries I've been used provided the usual functional
combinators (currying functions as "bind2nd", K, I, etc.). I seldomly
used them, since explicite lambda expression are more readable most
the time.

Moreover, if there are lambdas, then programmers start to do things
which they wouldn't do with combinators, but which increase
readibility and even performance. Consider

FloatContainer::const_iterator beg = cont.begin();
FloatContainer::const_iterator end = cont.end();
float sum = 0;
while (beg != end){
sum += *beg++;
}
return sum;

against

float sum = 0;
for_each(cont.begin(), cont.end(),
void lambda (float a) { sum += a; })
return sum;


There shouldn't be any performace penalty with the later
version assuming the usual template specialization optimizations.
In fact, the later version may run more faster then the
first one, since for_each can use an iteration method customized
for the container.

Regards

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

Christopher Eltschka

unread,
Aug 30, 1999, 3:00:00 AM8/30/99
to
"Siemel B. Naran" wrote:
>
> On 27 Aug 1999 19:59:23 -0400, Wolfgang Grieskamp <w...@cs.tu-berlin.de> wrote:
>
> >It would be even nicer if there would be really "inner" classes as in
> >Java which can access the automatic variables of their context:
>
> Good idea, but there's an overhead on people who don't want to use the
> feature, or people who need to use the feature only a little. It is
> conceivable that we don't need the local class to have access to the
> local context. Or maybe the local class needs access to only 3 local
> variables.

Since for local classes _all_ member functions must be inline,
the compiler just knows if, and which, inner variables are
accessed. It could therefore decide to add the extra overhead
only if inner variables are really accessed, and to choose the
best method according to thew number of accessed arguments
(f.ex, if only a few arguments are used, it might just add
hidden references to the class; if more variables are used,
it could add a frame pointer to the class).

[...]

> >The translation of this "auto" classes would be straighforward
> >in C++: just add the context parameters such as "float a" as additional
> >&-parameters to yield a normal static class (called lambda-lifting
> >in the FP scene, and the technique used by SUN to implement inner
> >classes in 1.1 without modifying the JVM).
>
> But now sizeof(class) increases. I often write little classes that
> don't need access to any of the local variables. I want these
> classes to be as little as possible. Sometimes, I need to grant
> access to a few local variables.

No problem for the compiler, as the _complete_
definition of the class member functions is visible
(well, if it inherits a global class, there may be
invisible member function definitions, but those cannot
access local variables anyway).

>
> Besides, is the implicit data member of the local class "float a"
> or "float& a" or "const float& a"?

float&, of course. As usual, the as-if rule applies. Since all
access points of the variable are visible, the compiler
should be able to aggressively optimize - even up to completely
optimizing away the whole class.

[...]

Robert Klemme

unread,
Aug 30, 1999, 3:00:00 AM8/30/99
to
Dave Gomboc schrieb:

>
> In article <007e01beeaaf$5c233460$7d9c37c3@mindanao>,
> "Pedro Blanco" <pbl...@jet.es> wrote:
> > I bet that more than a C/C++'er, especially when confronted to STL's
> > functionals and functors, to such long-lasting marvels as LISP, and
> > to languages such as Haskell, wonders why hasn't anybody given C/C++
> > the notion of lambda expressions.
>
> [snip]
> > Comments?
>
> Can we curry arbitrary functions too? That would make it really
> cool. :-)

yeah, and i'd like parallelism and multithreading in the language,
too. and maybe a coffee cookin'...

sorry, i just couldn't resist: why use an enhanced version of c++ for
functional programming and not a functional language in the first
place? don't try to squeeze it all in.

bye

robert

--
Robert Klemme
Internet Consultant/Berater
-------------------------------------------------------------
UNITY AG ~ Riemekestraße 160 ~ D-33106 Paderborn
http://www.unity.de ~ E-Mail: mailto:kle...@unity.de
Telefon: 0 52 51 / 6 90 90-207 ~ Fax: 0 52 51 / 6 90 90-199
-------------------------------------------------------------

Siemel B. Naran

unread,
Aug 31, 1999, 3:00:00 AM8/31/99
to
On 30 Aug 1999 13:45:00 -0400, Christopher Eltschka
>"Siemel B. Naran" wrote:

>> Good idea, but there's an overhead on people who don't want to use the
>> feature, or people who need to use the feature only a little. It is
>> conceivable that we don't need the local class to have access to the
>> local context. Or maybe the local class needs access to only 3 local
>> variables.
>
>Since for local classes _all_ member functions must be inline,
>the compiler just knows if, and which, inner variables are
>accessed. It could therefore decide to add the extra overhead
>only if inner variables are really accessed, and to choose the
>best method according to thew number of accessed arguments
>(f.ex, if only a few arguments are used, it might just add
>hidden references to the class; if more variables are used,
>it could add a frame pointer to the class).

The compiler may make the functions non-inline at its discretion.

Moreover, this appears to be an optimization that the compiler must
either always do or never do. Consider:

void f() { int o; struct S { int p; }; cout << sizeof(S) << '\n'; }

If the compiler doesn't do the optimization, class f()::S will have
sizeof==2*sizeof(int), and with sizeof(int)==4, we'd see "8".

If the compiler does do the optimization, class f()::S will have
sizeof==sizeof(int), and with sizeof(int)==4, we'd see "4".

Common sense tells me that the behaviour of the program should be
same with or without the optimization. That is, either we always
see "4" or we always see "8".

Now the optimization is possible, but it's really hard to do
because it requires context specific analysis. Let's wait for
compiler writers to do the return value optimization before we
start doing this :).

--
---------------------------------
siemel b naran (sbn...@uiuc.edu)
---------------------------------

Siemel B. Naran

unread,
Aug 31, 1999, 3:00:00 AM8/31/99
to
On 30 Aug 1999 12:13:10 -0400, Wolfgang Grieskamp <w...@cs.tu-berlin.de> wrote:
>sbn...@uiuc.edu (Siemel B. Naran) writes:

>> Good idea, but there's an overhead on people who don't want to use the
>> feature, or people who need to use the feature only a little. It is
>> conceivable that we don't need the local class to have access to the
>> local context. Or maybe the local class needs access to only 3 local
>> variables.

>But what variables from the context are used can be easily


>checked by the compiler, so there is no overhead if
>no context variables are used.

Good idea. Problem now is that you are requiring the compiler to
the most complex of optimizations -- context-specific optimization.


> void f(const Container& c, float a) {
> float other;
> transform(c.begin(),c.end(),
> float lambda(float b){ return b+a; } }
> );
> }

I see what you mean.
But this seems like it would cause lots of parse errors because there
doesn't seem to be a BNF grammar to fit it.
How about
transform(c.begin(),c.end(),
class() float (float b){ return b+a; }
);


>This would translate to:
>
> class Anon {
> float &a;
> Anon(float &_a) : a(_a) {}
> operator()(float b) { return b+a; }
> };
> void f(const Container& c, float a) {
> float other;
> transform(c.begin(),c.end(), Anon(a));
> }
>
>Thus the variable "other" is not passed to the constructor
>of the anonymous class, since it is not used by its code.

Fine. But just make the operator() const.

--
---------------------------------
siemel b naran (sbn...@uiuc.edu)
---------------------------------

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

j.striegnitz

unread,
Aug 31, 1999, 3:00:00 AM8/31/99
to
Dave Gomboc wrote:
>
> In article <007e01beeaaf$5c233460$7d9c37c3@mindanao>,
> "Pedro Blanco" <pbl...@jet.es> wrote:
> > I bet that more than a C/C++'er, especially when confronted to STL's
> > functionals and functors, to such long-lasting marvels as LISP, and
> > to languages such as Haskell, wonders why hasn't anybody given C/C++
> > the notion of lambda expressions.
>
> [snip]
> > Comments?
>
> Can we curry arbitrary functions too? That would make it really
> cool. :-)

Yes, we can and only need to rely on existing C++ features.

But, if we want to clone the behaviour of functions in a functional
sense,
we have to introduce some limitation: in functional programming
langauges
(those that I know) there isn't the possibility to pass arguments by
reference. Therefore, we must decide if functions passed to a
curry-operator
are allowed to change their arguments or not.

I just finished work on an appropiate "library". This library introduces
a
new function named "curry" (I call it operator). The follwing snippet
shows
its use:

[snip.........................................]
int sum(const int a,const int b,int c) {
return a+b+c;
}

int add(const int,int) {
return a+b;
}

template <typename F>
int test2(F f) {
return f(2,3);
}
....
cout << curry(sum)(1)(2)(3) << endl; // prints "6"
cout << test2(curry(sum)(2)) << endl; // prints "7"
....
cout << for_each(a.begin(),a.end(),curry(sum)(1)(2));
....
cout << for_each(a.begin(),a.end(),curry(add)(3)));
cout << for_each(a.begin(),a.end(),bind1st(plus<int>,3)));
[snap..........................................]

Soon I will have finished a paper describing all the underlying details.
If more interested, send eMail.

Ciao,
Joerg
--
==========================================================================
Dipl.-Inform. Joerg Striegnitz
Research Center Juelich, Central Institute for Applied Mathematics
(ZAM)
D-52425 Juelich / Germany
eMail.: J.Stri...@fz-juelich.de
Tel.: +49-2461-612054 Fax.: +49-2461-616656
==========================================================================

Biju Thomas

unread,
Sep 1, 1999, 3:00:00 AM9/1/99
to
Siemel B. Naran wrote:
>
> Now the optimization is possible, but it's really hard to do
> because it requires context specific analysis. Let's wait for
> compiler writers to do the return value optimization before we
> start doing this :).

Doesn't most compilers do that already? I remember reading in 'More
Effective C++' that most compilers do RVO and that is why it has such
special name, years back. (I believe words from authoritative figures,
when it is convenient to do so :-)

--
Biju Thomas

Dave Gomboc

unread,
Sep 1, 1999, 3:00:00 AM9/1/99
to
{Your article is basically on-topic and would be approved if it didn't
quote the whole original article. Please remove stuff you are not
referencing in your answer (eg. the signature and the moderation banner
but probably other stuff, too) and repost your article. Thank you. -mod}

In article <37CA940F...@unity.de>,
Robert Klemme <kle...@unity.de> wrote:
> Dave Gomboc schrieb:


> >
> > In article <007e01beeaaf$5c233460$7d9c37c3@mindanao>,
> > "Pedro Blanco" <pbl...@jet.es> wrote:

> > > I bet that more than a C/C++'er, especially when confronted to
STL's
> > > functionals and functors, to such long-lasting marvels as LISP,
and
> > > to languages such as Haskell, wonders why hasn't anybody given
C/C++
> > > the notion of lambda expressions.
> >

> > [snip]
> > > Comments?
> >
> > Can we curry arbitrary functions too? That would make it really
> > cool. :-)
>

> yeah, and i'd like parallelism and multithreading in the language,
> too. and maybe a coffee cookin'...
>
> sorry, i just couldn't resist: why use an enhanced version of c++ for
> functional programming and not a functional language in the first
> place? don't try to squeeze it all in.
>
> bye
>
> robert

One reason might be that you want an (e.g. Haskell) compiler to compile
to C++?

Dave

> --
> Robert Klemme
> Internet Consultant/Berater
> -------------------------------------------------------------
> UNITY AG ~ Riemekestraße 160 ~ D-33106 Paderborn
> http://www.unity.de ~ E-Mail: mailto:kle...@unity.de
> Telefon: 0 52 51 / 6 90 90-207 ~ Fax: 0 52 51 / 6 90 90-199
> -------------------------------------------------------------
>

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

--


Dave Gomboc
da_veATc_sPERIODualb_ertaDOTc_a
(remove underscores and replace MAJUSCULES with punctuation to get

email address)


Sent via Deja.com http://www.deja.com/
Share what you know. Learn what you don't.

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

Paul D. DeRocco

unread,
Sep 1, 1999, 3:00:00 AM9/1/99
to
Robert Klemme wrote:
>
> why use an enhanced version of c++ for
> functional programming and not a functional language in the first
> place? don't try to squeeze it all in.

STL promotes a programming style that makes one wish for this ability quite
often. I wish for it all the time.

--

Ciao, Paul D. DeRocco
Paul mailto:pder...@ix.netcom.com

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

Wolfgang Grieskamp

unread,
Sep 1, 1999, 3:00:00 AM9/1/99
to
sbn...@uiuc.edu (Siemel B. Naran) writes:

> The compiler may make the functions non-inline at its discretion.
>
> Moreover, this appears to be an optimization that the compiler must
> either always do or never do. Consider:
>
> void f() { int o; struct S { int p; }; cout << sizeof(S) << '\n'; }
>

> [...]


>
> Common sense tells me that the behaviour of the program should be
> same with or without the optimization. That is, either we always
> see "4" or we always see "8".

You are right. But solutions might exist:

1. the compiler does always lift context variables
in a deterministic but unspecified way. (see below)

2. only anonymous (inner) classes are allowed, so sizeof(S)
isn't valid (or does C++ also allows sizeof(expr)? Then this
won't work.)

3. sizeof(S) isn't valid for these specific S's, like sizeof(T)
of an incomplete type T isn't valid.

Solution (1) assumes that the way the compiler lifts context variables
is invariant according to optimization settings (it has actually
nothing to do with optimizations.) But it gives freedom for different
compilers to use different strategies. Letting such freedom is a usual
phenomen in the standard.

> Now the optimization is possible, but it's really hard to do
> because it requires context specific analysis.

I think this is a myth. I see no reason why C++ compilers shouldn't
easily do the same kind of transformation as much more simple Java
compilers can do ;) (and FL compilers done for over a decade.) The
scoping rules of variables aren't more complicated as in other
languages.


> Let's wait for
> compiler writers to do the return value optimization before we
> start doing this :).

What's this?


Regards
Wolfgang

--
w...@cs.tu-berlin.de http://uebb.cs.tu-berlin.de/~wg/

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

Wolfgang Grieskamp

unread,
Sep 1, 1999, 3:00:00 AM9/1/99
to
Robert Klemme <kle...@unity.de> writes:

> [...]
> sorry, i just couldn't resist: why use an enhanced version of c++ for


> functional programming and not a functional language in the first
> place?

Its a consequence of the STL. The STL without anonymous inner
classes/lambda abstraction is only half of the STL ;)

Regards,
Wolfgang

--
w...@cs.tu-berlin.de http://uebb.cs.tu-berlin.de/~wg/

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

Pontus Gagge

unread,
Sep 8, 1999, 3:00:00 AM9/8/99
to

>Can we curry arbitrary functions too? That would make it really
>cool. :-)
>
>Dave

Well, not arbitrary functions, but at least STL-compliant binary
functors...
(Sorry, couldn't resist!)

/Pontus Gagge


//////////////////////////////////////////////////////////////////////
// Currying functor. Usage example: curry(std::plus)(3)(4) yields 7.

template <class BinOp>
class curry : public std::unary_function<typename
BinOp::first_argument_type, std::binder1st<BinOp,
BinOp::first_argument_type> >
{
typedef std::binder1st<BinOp, BinOp::first_argument_type>
result_closure;
BinOp m_op;
public:
curry(const BinOp &op)
: m_op(op)
{}

result_closure
operator()(const typename BinOp::first_argument_type x)
{ return result_closure(m_op, x); }
}; // curry<BinOp>

0 new messages