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

Lamda-aware higher order functions

66 views
Skip to first unread message

pfultz2

unread,
Nov 20, 2011, 6:22:58 PM11/20/11
to
Sometimes, it would be nice to have polymorphic lambdas in C++, not
because I want use the lambdas polymorphic, but I just want the
compiler to deduce the type for me. Well I thought maybe there could
be a way for the compiler to do this if the function made known how
the lambda should be called. Then the function could be called with a
special marco like this:

CALL(test, x) { printf("Number:%i\n";};

which would result in this:

test & [&] (decltype(test)::lambda_param<0>::type x) { printf("Number:
%i\n";};

This test class could be defined like this:

struct test_t
{
template<int N, class Dummy = void>
struct lambda_param;

template<class Dummy>
struct lambda_param<0, Dummy>
{
typedef int type;
};
template<class Yield>
void operator&(Yield yield)
{
yield(5);
}

} test;

This macro could also be defined for expression to make it shorter
also, like this:

EXPR(find_if(range), x)(x < 3);

Which would transform into this:

find_if(range) & [&] (decltype(find_if(range))::lambda_param<0>::type
x) {return x < 3;};

Now to top off with the syntatic sugar(which is not cross-platform
compatible) I could define these macros to make it shorter:

#define $ CALL
#define $$ EXPR

Now I can write things like this:

$(for_each(some_range), x)
{
printf("%i\n", x);
};
auto it = $$(find_if(some_range), x)(x < 3);

Now, of course, it seems like it would be nice for range adaptors to
be lamba aware too, but I cant seem to quite make it work. I would
like to be able to call these range adaptors like this:

range | $$(filtered, x)(x < 3);

This wont work since filtered doesnt know what range its using(and
thus what type x should be), because the way the macro is structured.
This macro essentially gets tranformed into this:

range | filtered & [&] (decltype(filtered)::lambda_param<0>::type x)
{return x < 3;};

Now I can make it work if I write like this:

$$(range | filtered, x)(x < 3);

But I hate to have to nest things like this. I was wondering if there
was some other approach I could do to improve this, or maybe this is
just as about as far as the rabbit hole goes.

Hopefully, all this made sense.

What do you think?


--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Alec Ross

unread,
Nov 22, 2011, 2:43:45 PM11/22/11
to
In message
<7bc8aa42-04a9-458c...@o13g2000vbo.googlegroups.com>,
pfultz2 <pfu...@yahoo.com> writes
Have you looked at what can be done using the Boost libs? e.g. see:

http://lists.boost.org/Archives/boost/2011/05/181267.php

--
Alec Ross

Dave Abrahams

unread,
Nov 23, 2011, 11:09:20 AM11/23/11
to
on Sun Nov 20 2011, pfultz2 <pfultz2-AT-yahoo.com> wrote:

> Sometimes, it would be nice to have polymorphic lambdas in C++, not
> because I want use the lambdas polymorphic, but I just want the
> compiler to deduce the type for me.

OK so far. The examples, though, took me forever to begin to
understand. Maybe that's because of the naming...

> Well I thought maybe there could be a way for the compiler to do this
> if the function made known how the lambda should be called. Then the
> function could be called with a special marco like this:
>
> CALL(test, x) { printf("Number:%i\n";};
>
> which would result in this:
>
> test & [&] (decltype(test)::lambda_param<0>::type x) { printf("Number:
> %i\n";};
>
> This test class could be defined like this:
>
> struct test_t
> {
> template<int N, class Dummy = void>
> struct lambda_param;
>
> template<class Dummy>
> struct lambda_param<0, Dummy>
> {
> typedef int type;
> };
> template<class Yield>
> void operator&(Yield yield)
> {
> yield(5);
> }
>
> } test;

<schnipp>

> $(for_each(some_range), x)
> {
> printf("%i\n", x);
> };
> auto it = $$(find_if(some_range), x)(x < 3);

Now if I understand correctly, "test" at the beginning of your posting
is supposed to stand for some example "algorithm object" of the same ilk
as "for_each" in the snippet above. So you're suggesting encoding the
computation of the lambda's parameter and return types into the
algorithm object itself? Makes sense...

> Now, of course, it seems like it would be nice for range adaptors to
> be lamba aware too, but I cant seem to quite make it work. I would
> like to be able to call these range adaptors like this:
>
> range | $$(filtered, x)(x < 3);
>
> This wont work since filtered doesnt know what range its using(and
> thus what type x should be), because the way the macro is structured.
> This macro essentially gets tranformed into this:
>
> range | filtered & [&] (decltype(filtered)::lambda_param<0>::type x)
> {return x < 3;};
>
> Now I can make it work if I write like this:
>
> $$(range | filtered, x)(x < 3);
>
> But I hate to have to nest things like this. I was wondering if there
> was some other approach I could do to improve this, or maybe this is
> just as about as far as the rabbit hole goes.
>
> Hopefully, all this made sense.
>
> What do you think?

I think it might be a whole lot easier to have real polymorphic lambdas
;-)

--
Dave Abrahams
BoostPro Computing
http://www.boostpro.com

pfultz2

unread,
Nov 25, 2011, 9:54:02 PM11/25/11
to
> Now if I understand correctly, "test" at the beginning of your posting
> is supposed to stand for some example "algorithm object" of the same ilk
> as "for_each" in the snippet above. So you're suggesting encoding the
> computation of the lambda's parameter and return types into the
> algorithm object itself? Makes sense...

I guess what I am trying to accomplish would be similar to ruby-style
blocks. It doesn't necessarily have to apply to std algorithms; it
could be used for callbacks and signals, also. For example, using ruby
blocks you would iterate like this:

collection.each { |x| print x }

This is because the "each" method is accepts a lambda. When the
function calls "yield", it calls the lambda block. Now, of course, we
dont have polymorphic lambdas, so the special ruby-like functions(or
lambda-aware functions, im not sure what to call them) would let the
compiler know what variable type they pass to the lambda as
parameters. So in essence, the compiler would deduce the type(but it
wouldnt be polymorphic). The way this could look would be like this:

$(for_each(some_range), x) { cout << x; };

This would call "for_each" like this:

for_each(some_range, [] (typename range_value<some_range>::type x)
{ cout << x; });

Now, how does it know that x is of the type "typename
range_value<some_range>::type"? That is because the function returns a
special "object" that says each of the types it passes into the
lambda.

Now of course this "$" macro works great for a lot of functions, but
as with almost all macros, it has its caveats. One of its caveats, is
that it cant be used with pipable range adaptors. It, of course, would
be great if I could write something like this:

//Find all the last names of people named "Joe"
some_range | $(filtered, x) { return x.first_name = "Joe" } | $
(transformed, x) { return x.last_name; };

Of course this wont work, because it is a macro, and not an operator,
so it would need to be called like this:

$($(some_range | filtered, x) { return x.first_name = "Joe"; } |
transformed, x) { return x.last_name; }

Which is harder to read and write. I dont know of anyway with using
the macros to get around this. Perhaps I dont use the pipable adaptors
for filter and transform, but instead create some special "functions"
that can use the "$" macro(lets call filter => where, and transform =>
select). I could place them inside of another lambda, maybe and write
something like this:

$(from(some_range), x) { $(where) { return x.first_name = "Joe" } | $
(select) { return x.last_name; }; }

I am not sure at all if this would work. However, its starting look a
lot like linq in C#, but fairly verbose. It would be nice to have it
compact like in C#, but im afraid the macros needed to do that would
just make it look awful, if boost supported something like this, it
would look like this:

BOOST_FROM(some_range, x, BOOST_WHERE(x.first_name = "Joe"),
BOOST_SELECT(x.last_name))

theres no way "from", "select" and "where" could use there lower case
version, it could very possibly interfere with a lot of other names.
It could be written like this maybe:

FROM(some_range, x, where(x.first_name = "Joe"), select(x.last_name))

This could be very possible, but I guess I digressed.

> I think it might be a whole lot easier to have real polymorphic lambdas
> ;-)

No doubt polymorphic lambdas would make this a lot easier, however,
this wont happen for at least another 5 years(assuming they create
another standard that soon). Im trying to write something that would
help make writing lambdas much more compact and cleaner now. This all
could be done by a library in order to make writing these "lambda-
aware" functions easier(I am not sure what the appropriate term would
be for these functions).


--
0 new messages