Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > C++ > Proposal for lazy evaluation of C++ function arguments

Reply
Thread Tools

Proposal for lazy evaluation of C++ function arguments

 
 
clintonmead@gmail.com
Guest
Posts: n/a
 
      11-14-2012
/*

(I've commented this code so it should compile just by copying/pasting the message. You'll also need boost.)

Lets say we want to create a boost::variant, with a few options. For example, we could be creating a filestream, but our options might be a dummy filestream, stdout or a real file stream. But for simplicity in this case, let's assume the options are 'int', 'std::string' and 'char'. Lets also assumewe have functions which return appropriate values of each type, like so:

*/

#include <string>

int f() { return 42; }
std::string g() { return "The meaning of life"; }
char h() { return '!'; }

/*

Now we'd like to make a variant like this:

auto x = make_variant(n, f(), g(), h());

For n = 0, x = 42, for n = 1, x = "The meaning of life" and for x = 2, n = '!'.

Also, this way, the type of the boost::variant is inferred.

However, the problem with this is that all of f(), g() and h() are evaluated. Not much of a problem in this case, but if f(), g() or h() were complicated this could be an issue.

If we want to delay evaluation, we might do this:

auto x = make_variant(n, []{return f();}, []{return g();}, []{return h();});

This looks a bit ugly now, but we can now write 'make_variant' like below:

*/

#include <boost/variant.hpp>
#include <functional>
#include <stdexcept>

template <class RETURN_T>
RETURN_T make_variant_worker(int n)
{
throw std::runtime_error("Invalid selection");
}

template <class RETURN_T, class FIRSTARG, class... ARGS>
RETURN_T make_variant_worker(int n, const FIRSTARG& first_arg, const ARGS&.... args)
{
return n == 0 ? first_arg() : make_variant_worker<RETURN_T>(n - 1, args...);
}

template <class... ARGS>
auto make_variant(int n, const ARGS&... args) -> boost::variant<decltype(args())...>
{
return make_variant_worker<boost::variant<decltype(args() )...>>(n, args....);
}

/*

We can then use the 'make_variant' function as follows:

*/

int main()
{
int n;
std::cin >> n;
auto x = make_variant(n, []{return f();}, []{return g();}, []{return h();});
std::cout << x << std::endl;
}

/*

However, the call to 'make_variant' is a bit ugly. What I'd like to be ableto do is somewhere in the 'make_variant' function put an attribute on the function arguments that implicitly wraps all the arguments with "[] { return arg; }", i.e. delays their evaluation. Hence, I could call:

make_variant(n, f(), g(), h());

Without evaluating f(), g() or h() unnecessarily.

This isn't without precedent in C++, the non-overridden "&&", "||" and "?:"all don't necessarily evaluate all of their arguments. Such a feature would allow one not only to write a 'make_variant' function without splatteringlambdas all throughout the code, but also create short circuit operators for "&&" and "||" (say, for three value logic), and functions like "if_then_else" that behave like their builtin equivalents.

I'm not sure how to make this a formal proposal, even if I did I wouldn't know how to express the details. However, I think it would be fairly simple to implement because it's effectively just syntactic sugar, particularly when the call is occurring internally in one compilation unit. I'm not sure if I'm reinventing the wheel here, if this can already be done in C++11 I'd be interested to see how, otherwise if someone wants to take this up that'smore experienced than me with these things I'd be happy with that too.

Regards,

Clinton Mead

*/
 
Reply With Quote
 
 
 
 
Victor Bazarov
Guest
Posts: n/a
 
      11-14-2012
On 11/13/2012 8:15 PM, http://www.velocityreviews.com/forums/(E-Mail Removed) wrote:
> /*
>
> (I've commented this code so it should compile just by copying/pasting the message. You'll also need boost.)
>
> Lets say we want to create a boost::variant, with a few options. For example, we could be creating a filestream, but our options might be a dummy file stream, stdout or a real file stream. But for simplicity in this case, let's assume the options are 'int', 'std::string' and 'char'. Lets also assume we have functions which return appropriate values of each type, like so:
>
> */
>
> #include <string>
>
> int f() { return 42; }
> std::string g() { return "The meaning of life"; }
> char h() { return '!'; }
>
> /*
>
> Now we'd like to make a variant like this:
>
> auto x = make_variant(n, f(), g(), h());
>
> For n = 0, x = 42, for n = 1, x = "The meaning of life" and for x = 2, n = '!'.
>
> Also, this way, the type of the boost::variant is inferred.
>
> However, the problem with this is that all of f(), g() and h() are evaluated. Not much of a problem in this case, but if f(), g() or h() were complicated this could be an issue.
>
> If we want to delay evaluation, we might do this:
>
> auto x = make_variant(n, []{return f();}, []{return g();}, []{return h();});
>
> This looks a bit ugly now, but we can now write 'make_variant' like below:
>
> */
>
> #include <boost/variant.hpp>
> #include <functional>
> #include <stdexcept>
>
> template <class RETURN_T>
> RETURN_T make_variant_worker(int n)
> {
> throw std::runtime_error("Invalid selection");
> }
>
> template <class RETURN_T, class FIRSTARG, class... ARGS>
> RETURN_T make_variant_worker(int n, const FIRSTARG& first_arg, const ARGS&... args)
> {
> return n == 0 ? first_arg() : make_variant_worker<RETURN_T>(n - 1, args...);
> }
>
> template <class... ARGS>
> auto make_variant(int n, const ARGS&... args) -> boost::variant<decltype(args())...>
> {
> return make_variant_worker<boost::variant<decltype(args() )...>>(n, args...);
> }
>
> /*
>
> We can then use the 'make_variant' function as follows:
>
> */
>
> int main()
> {
> int n;
> std::cin >> n;
> auto x = make_variant(n, []{return f();}, []{return g();}, []{return h();});
> std::cout << x << std::endl;
> }
>
> /*
>
> However, the call to 'make_variant' is a bit ugly. What I'd like to be able to do is somewhere in the 'make_variant' function put an attribute on the function arguments that implicitly wraps all the arguments with "[] { return arg; }", i.e. delays their evaluation. Hence, I could call:
>
> make_variant(n, f(), g(), h());
>
> Without evaluating f(), g() or h() unnecessarily.
>
> This isn't without precedent in C++, the non-overridden "&&", "||" and "?:" all don't necessarily evaluate all of their arguments. Such a feature would allow one not only to write a 'make_variant' function without splattering lambdas all throughout the code, but also create short circuit operators for "&&" and "||" (say, for three value logic), and functions like "if_then_else" that behave like their builtin equivalents.
>
> I'm not sure how to make this a formal proposal, even if I did I wouldn't know how to express the details. However, I think it would be fairly simple to implement because it's effectively just syntactic sugar, particularly when the call is occurring internally in one compilation unit. I'm not sure if I'm reinventing the wheel here, if this can already be done in C++11 I'd be interested to see how, otherwise if someone wants to take this up that's more experienced than me with these things I'd be happy with that too.
>
> Regards,
>
> Clinton Mead
>
> */
>


Without reading too carefully, my first reaction is, "it's not lazy
evaluation, it's short-circuit evaluation", IOW, you don't delay, you
avoid evaluating what's not needed. Second reaction, the language
already has this:

(n == 0 ? f() : n == 1 ? g() : h())

which will not evaluate f() or h() if n == 1.

There might be something to the "arithmetic ?:" construct, I am not sure
what problem you're going to be solving with it that can't be solved by
other means, so it would just be a syntactic sugar. But consider a
lambda like this:

[](int n) { switch (n) {
case 0: return f();
case 1: return g();
case 2: return h(); } throw "bad value"; };

All you need is the ability to specify the return value type of such
lambda to be 'super-auto' if the functions 'f', 'g', 'h' all return
different types.

Is that what you're talking about?

V
--
I do not respond to top-posted replies, please don't ask
 
Reply With Quote
 
 
 
 
Clinton Mead
Guest
Posts: n/a
 
      11-14-2012
On Wednesday, November 14, 2012 11:48:48 PM UTC+11, Victor Bazarov wrote:
> On 11/13/2012 8:15 PM, (E-Mail Removed) wrote:
>
> > /*

>
> >

>
> > (I've commented this code so it should compile just by copying/pasting the message. You'll also need boost.)

>
> >

>
> > Lets say we want to create a boost::variant, with a few options. For example, we could be creating a filestream, but our options might be a dummy file stream, stdout or a real file stream. But for simplicity in this case,let's assume the options are 'int', 'std::string' and 'char'. Lets also assume we have functions which return appropriate values of each type, like so:

>
> >

>
> > */

>
> >

>
> > #include <string>

>
> >

>
> > int f() { return 42; }

>
> > std::string g() { return "The meaning of life"; }

>
> > char h() { return '!'; }

>
> >

>
> > /*

>
> >

>
> > Now we'd like to make a variant like this:

>
> >

>
> > auto x = make_variant(n, f(), g(), h());

>
> >

>
> > For n = 0, x = 42, for n = 1, x = "The meaning of life" and forx = 2, n = '!'.

>
> >

>
> > Also, this way, the type of the boost::variant is inferred.

>
> >

>
> > However, the problem with this is that all of f(), g() and h() are evaluated. Not much of a problem in this case, but if f(), g() or h() were complicated this could be an issue.

>
> >

>
> > If we want to delay evaluation, we might do this:

>
> >

>
> > auto x = make_variant(n, []{return f();}, []{return g();}, []{return h();});

>
> >

>
> > This looks a bit ugly now, but we can now write 'make_variant' like below:

>
> >

>
> > */

>
> >

>
> > #include <boost/variant.hpp>

>
> > #include <functional>

>
> > #include <stdexcept>

>
> >

>
> > template <class RETURN_T>

>
> > RETURN_T make_variant_worker(int n)

>
> > {

>
> > throw std::runtime_error("Invalid selection");

>
> > }

>
> >

>
> > template <class RETURN_T, class FIRSTARG, class... ARGS>

>
> > RETURN_T make_variant_worker(int n, const FIRSTARG& first_arg, const ARGS&... args)

>
> > {

>
> > return n == 0 ? first_arg() : make_variant_worker<RETURN_T>(n - 1, args...);

>
> > }

>
> >

>
> > template <class... ARGS>

>
> > auto make_variant(int n, const ARGS&... args) -> boost::variant<decltype(args())...>

>
> > {

>
> > return make_variant_worker<boost::variant<decltype(args() )...>>(n, args...);

>
> > }

>
> >

>
> > /*

>
> >

>
> > We can then use the 'make_variant' function as follows:

>
> >

>
> > */

>
> >

>
> > int main()

>
> > {

>
> > int n;

>
> > std::cin >> n;

>
> > auto x = make_variant(n, []{return f();}, []{return g();}, []{return h();});

>
> > std::cout << x << std::endl;

>
> > }

>
> >

>
> > /*

>
> >

>
> > However, the call to 'make_variant' is a bit ugly. What I'd like to be able to do is somewhere in the 'make_variant' function put an attribute on the function arguments that implicitly wraps all the arguments with "[] { return arg; }", i.e. delays their evaluation. Hence, I could call:

>
> >

>
> > make_variant(n, f(), g(), h());

>
> >

>
> > Without evaluating f(), g() or h() unnecessarily.

>
> >

>
> > This isn't without precedent in C++, the non-overridden "&&", "||" and "?:" all don't necessarily evaluate all of their arguments. Such a feature would allow one not only to write a 'make_variant' function without splattering lambdas all throughout the code, but also create short circuit operators for "&&" and "||" (say, for three value logic), and functions like "if_then_else" that behave like their builtin equivalents.

>
> >

>
> > I'm not sure how to make this a formal proposal, even if I did I wouldn't know how to express the details. However, I think it would be fairly simple to implement because it's effectively just syntactic sugar, particularly when the call is occurring internally in one compilation unit. I'm not sure if I'm reinventing the wheel here, if this can already be done in C++11 I'd be interested to see how, otherwise if someone wants to take this up that's more experienced than me with these things I'd be happy with that too.

>
> >

>
> > Regards,

>
> >

>
> > Clinton Mead

>
> >

>
> > */

>
> >

>
>
>
> Without reading too carefully, my first reaction is, "it's not lazy
>
> evaluation, it's short-circuit evaluation", IOW, you don't delay, you
>
> avoid evaluating what's not needed. Second reaction, the language
>
> already has this:
>
>
>
> (n == 0 ? f() : n == 1 ? g() : h())
>


I'm pretty sure this doesn't work in the example I provided (or the real life situation which inspired this) as f(), g() and h() are different types.

>
> which will not evaluate f() or h() if n == 1.
>
>
>
> There might be something to the "arithmetic ?:" construct, I am not sure
>
> what problem you're going to be solving with it that can't be solved by
>
> other means, so it would just be a syntactic sugar. But consider a
>
> lambda like this:
>
>
>
> [](int n) { switch (n) {
>
> case 0: return f();
>
> case 1: return g();
>
> case 2: return h(); } throw "bad value"; };
>
>
>
> All you need is the ability to specify the return value type of such
>
> lambda to be 'super-auto' if the functions 'f', 'g', 'h' all return
>
> different types.
>
>
>
> Is that what you're talking about?
>


No, not really. boost::variant is kind of like a "super-auto", but I'm justusing boost::variant as a way to illustrate my proposal, which could be used in other situations as well where all the types are the same, such as a TriBool class. i.e. If a TriBool class had values TriTrue, TriFalse and TriUnknown, and we had:

TriBool f() { return TriTrue; }
TriBool g() { throw "OH NO"; }

f() || g() should return TriTrue, not throw.

We could write

TriBool operator||(TriBool x, std::functional<TriBool()> f_y)
{
if (x == TriTrue) { return TriTrue; }
else
{
TriBool y = f_y();
if (y == TriTrue) { return TriTrue; }
else if (x == TriFalse && y == TriFalse) { return TriFalse; }
else { return TriUnknown; }
}
}

Then we could do the following:

f() || [] { return g(); }

And g() would only be evaluated if f() was not true.

I'd like to be able to just write:

f() || g()

in the client code, like one does for non-overloaded operators. Wrapping inthe lambda would be implicit due to some attribute, just like it's implicit the second argument of a non-overloaded || or && is not evaluated until the first is examined.

So there's two examples where this proposal would be useful, firstly constructing a boost::variant, but also short circuit non-boolean logic. I'm surethere are plenty other examples though.

Clinton

>
> V
>

 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
lazy evaluation is sometimes too lazy... help please. Ken Pu Python 3 01-16-2009 11:23 AM
Re: lazy evaluation is sometimes too lazy... help please. Boris Borcic Python 0 01-16-2009 10:46 AM
Re: lazy evaluation is sometimes too lazy... help please. Boris Borcic Python 0 01-16-2009 10:37 AM
hello! first post to clr. I'm asking about an attempt at a lazy rubysolution to computing fibonacci numbers for a project euler problem. seems tobe a bug in lazy ruby... tphyahoo Ruby 6 08-08-2008 08:15 PM
Another method of lazy, cached evaluation. simonwittber@gmail.com Python 3 01-19-2006 04:11 AM



Advertisments