www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Lazy evaluation

reply OF <nospam nospammington.com> writes:
Hey. I've been writing a bit on a "true" lazy evaluation (i.e. not evaluated
until value is requested, but only evaluated once). A simple example of this
can be achieved using a delegate, like so:

template Lazy(T)
{
	class Lazy
	{
		this(T delegate() exp)
		{
			mExp = exp;
		}

		T opCall()
		{
			if (!mEvaluated)
			{
				mValue = mExp();
				mEvaluated = true;
			}
			return mValue;
		}

	private:
		bool mEvaluated = false;
		final T delegate() mExp;
		T mValue;

	}
}

Which would work fine with Lazy!(int)( { return someExpr; } ), but it would be
nice to be able to just use Lazy!(int)( someExpr ). However, it seems this can
only be achieved using lazy function arguments, but problem is, if I use lazy
function arguments, I can't really save it for later and do anything worthwhile
with it.

Consider the additional constructor

this(lazy T lazyexp)
{
	mExp = { return lazyexp; };
}

which looks fine, but isn't (it crashed with Access Violation). It hints at
lazyexp not being valid after the function has ended, which makes it quite
different from delegates. Yet the article about lazy evaluation (
http://www.digitalmars.com/d/1.0/lazy-evaluation.html ) seems to indicate lazy
is just syntetic sugar for delegates... but the behaviour is not like it at
all. It doesn't seem to be able to convert a lazy expression to an delegate in
any way (cast complains, the above, as said, crashes). Is there any way I've
missed?

In addition, the above mentioned article states "So D takes it one small, but
crucial, step further (suggested by Andrei Alexandrescu). Any expression can be
implicitly converted to a delegate that returns either void or the type of the
expression." which doesn't seem to be true either, or it would be enough with
the first constructor that just uses the normal delegate syntax. Had this
worked, it would have been fine for this case.

An other alternative I thought of was to save the lazy expression in the Lazy
object instead of the delegate, but lazy seems to be completely restricted to
function arguments. Is there any specific reason for this? I mean, "lazy int i
= j++;" in, for example, a function isn't that much weirder. It's still just
seems to be syntetic sugar for delegates, it should be able to work
consistently with the behaviour of lazy for function arguments, should it not?

Any explanations or suggestions are welcome.

Slightly unrelated, is there anything in D corresponding to C++'s implicit cast
operators (operator 'typename'(), e.g. operator int()) in D? I can't seem to
find any... 
Jun 27 2007
next sibling parent reply BCS <BCS pathlink.com> writes:
OF wrote:
 It hints at lazyexp not being valid after the function has ended, which makes
it quite different from delegates.
This is the /exact/ behavior of delegate liters and delegates formed from nested functions. They are invalid after the function call they are from returns. This is because the delegate caries a pointer to the stack frame of the surrounding function.
Jun 27 2007
next sibling parent reply Lutger <lutger.blijdestijn gmail.com> writes:
BCS wrote:
 OF wrote:
 It hints at lazyexp not being valid after the function has ended, 
 which makes it quite different from delegates.
This is the /exact/ behavior of delegate liters and delegates formed from nested functions. They are invalid after the function call they are from returns. This is because the delegate caries a pointer to the stack frame of the surrounding function.
Yeah, but only when you access some variables through this pointer right?
Jun 27 2007
next sibling parent reply BCS <BCS pathlink.com> writes:
Lutger wrote:
 BCS wrote:
 
 OF wrote:

 It hints at lazyexp not being valid after the function has ended, 
 which makes it quite different from delegates.
This is the /exact/ behavior of delegate liters and delegates formed from nested functions. They are invalid after the function call they are from returns. This is because the delegate caries a pointer to the stack frame of the surrounding function.
Yeah, but only when you access some variables through this pointer right?
I would assume so but there might be some hidden reads that could cause problems.
Jun 27 2007
parent reply Lutger <lutger.blijdestijn gmail.com> writes:
BCS wrote:
 Lutger wrote:
 BCS wrote:

 OF wrote:

 It hints at lazyexp not being valid after the function has ended, 
 which makes it quite different from delegates.
This is the /exact/ behavior of delegate liters and delegates formed from nested functions. They are invalid after the function call they are from returns. This is because the delegate caries a pointer to the stack frame of the surrounding function.
Yeah, but only when you access some variables through this pointer right?
I would assume so but there might be some hidden reads that could cause problems.
Could you elaborate? I see nothing in the spec that says it's legal or not. If there are problems with this, I think that should be clearly stated.
Jun 27 2007
parent BCS <BCS pathlink.com> writes:
Lutger wrote:
 BCS wrote:
 
 Lutger wrote:

 BCS wrote:

 OF wrote:

 It hints at lazyexp not being valid after the function has ended, 
 which makes it quite different from delegates.
This is the /exact/ behavior of delegate liters and delegates formed from nested functions. They are invalid after the function call they are from returns. This is because the delegate caries a pointer to the stack frame of the surrounding function.
Yeah, but only when you access some variables through this pointer right?
I would assume so but there might be some hidden reads that could cause problems.
Could you elaborate? I see nothing in the spec that says it's legal or not. If there are problems with this, I think that should be clearly stated.
I known of no cases where hidden read/writes happen, but unless the spec says so, I would not assume they don't happen if I could avoid it. The spec does say that a delegate to a nested function is invalid after the function returns (note nothing about if it access anything). <start rant> This is one reason where I would really like to see explicit contexts for delegate literals class C{ int i; int j; } void main(){ C c = new C; void delegate (int, int) set = c.(int k, int l){ i=k; j=l; // access any public parts of the thing }; // delegate literal without risk of stack corruption void delegate() action = null.{someGlobalAction();}; int i = 5; bool delegate(int) test = i.(int j){ return this == j; // why not int for context? }; // sizeof(int) == sizeof(void*) // now how about 32bit structs by value? struct foo { short a; char b; byte c; } static assert(sizeof(foo) == sizeof(void*)); foo dat = foo(45, 'c', 222) int delegate() go_v = dat.{return a+b+c;}; // now by reference int delegate() go_p = (&dat).{return a+b+c;}; dat.a = 0; dat.b = '\0'; dat.c = 0; assert(go_v != 0); assert(go_p == 0); }
Jun 27 2007
prev sibling parent reply Regan Heath <regan netmail.co.nz> writes:
Lutger Wrote:
 BCS wrote:
 OF wrote:
 It hints at lazyexp not being valid after the function has ended, 
 which makes it quite different from delegates.
This is the /exact/ behavior of delegate liters and delegates formed from nested functions. They are invalid after the function call they are from returns. This is because the delegate caries a pointer to the stack frame of the surrounding function.
Yeah, but only when you access some variables through this pointer right?
If you don't need to do that then you want a function as opposed to a delegate, right? Is such a thing possible in D, function literals. You might need to explicitly say 'function' in there somewhere. I've not used these a heck of a lot so I'm not sure. Regan
Jun 28 2007
parent Frits van Bommel <fvbommel REMwOVExCAPSs.nl> writes:
Regan Heath wrote:
 Is such a thing possible in D, function literals.  You might need to
explicitly say 'function' in there somewhere.  I've not used these a heck of a
lot so I'm not sure.
Yes, function literals are possible. And indeed, they have 'function' in them. In fact, put 'function' in front of a delegate literal (and remove the optional 'delegate' if present) and you have a function literal. Details: http://www.digitalmars.com/d/expression.html#FunctionLiteral (or the equivalent 1.0 page)
Jun 28 2007
prev sibling parent OF <nospam nospammington.com> writes:
BCS Wrote:

 OF wrote:
 It hints at lazyexp not being valid after the function has ended, which makes
it quite different from delegates.
This is the /exact/ behavior of delegate liters and delegates formed from nested functions. They are invalid after the function call they are from returns. This is because the delegate caries a pointer to the stack frame of the surrounding function.
Yes, you're right. It's probably the delegate created in this(lazy T exp) { mExp = { return exp(); }; } that's the problem. this(T delegate() exp) { mExp = { return exp(); }; } crashes too. That's what I get for not doing my homework... I suppose my only hope would be some kind of casting from lazy T to T delegate(), but seems I'm out of luck.
Jun 28 2007
prev sibling next sibling parent reply "Jarrett Billingsley" <kb3ctd2 yahoo.com> writes:
"OF" <nospam nospammington.com> wrote in message 
news:f5u09a$1mmq$1 digitalmars.com...
 Slightly unrelated, is there anything in D corresponding to C++'s implicit 
 cast operators (operator 'typename'(), e.g. operator int()) in D? I can't 
 seem to find any...
Nope. D doesn't have implicit casting as in C++.
Jun 27 2007
parent reply "David B. Held" <dheld codelogicconsulting.com> writes:
Jarrett Billingsley wrote:
 "OF" <nospam nospammington.com> wrote in message 
 news:f5u09a$1mmq$1 digitalmars.com...
 Slightly unrelated, is there anything in D corresponding to C++'s implicit 
 cast operators (operator 'typename'(), e.g. operator int()) in D? I can't 
 seem to find any...
Nope. D doesn't have implicit casting as in C++.
...yet. Dave
Jun 27 2007
next sibling parent Robert Fraser <fraserofthenight gmail.com> writes:
I really hope it doesn't... there are a _few_ times where it'd be nice for two
classes to be inter-operable (mostly for backwards-compatibility purposes), but
the majority of the time (like most operator overloads to me...) it's just
confusing.

David B. Held Wrote:

 Jarrett Billingsley wrote:
 "OF" <nospam nospammington.com> wrote in message 
 news:f5u09a$1mmq$1 digitalmars.com...
 Slightly unrelated, is there anything in D corresponding to C++'s implicit 
 cast operators (operator 'typename'(), e.g. operator int()) in D? I can't 
 seem to find any...
Nope. D doesn't have implicit casting as in C++.
...yet. Dave
Jun 27 2007
prev sibling parent reply "Jarrett Billingsley" <kb3ctd2 yahoo.com> writes:
"David B. Held" <dheld codelogicconsulting.com> wrote in message

 ...yet.
So just how far is Walter going to go to please C++ programmers? :P
Jun 28 2007
parent "David B. Held" <dheld codelogicconsulting.com> writes:
Jarrett Billingsley wrote:
 "David B. Held" <dheld codelogicconsulting.com> wrote in message
 
 ...yet.
So just how far is Walter going to go to please C++ programmers? :P
The problem is proxy types. Proxy types happen to be useful, and in order for them to work properly, you basically need implicit casts. Want an example of proxy types not quite working properly? Think what happens when you do ++ on a property or think of the magic that happens when you do it on a builtin hash. That doesn't mean they will be implemented just like C++'s implicit casts; but if they fix these other holes in the language, as well as enabling a new pattern, isn't that a net win? Dave
Jun 28 2007
prev sibling next sibling parent "Stewart Gordon" <smjg_1998 yahoo.com> writes:
"OF" <nospam nospammington.com> wrote in message 
news:f5u09a$1mmq$1 digitalmars.com...
 Hey.  I've been writing a bit on a "true" lazy evaluation (i.e.
 not evaluated until value is requested, but only evaluated once).
 A simple example of this can be achieved using a delegate, like so:
<snip>
 Which would work fine with Lazy!(int)( { return someExpr; } ), but
 it would be nice to be able to just use Lazy!(int)( someExpr ).
 However, it seems this can only be achieved using lazy function
 arguments, but problem is, if I use lazy function arguments, I
 can't really save it for later and do anything worthwhile with it.
<snip> Actually you can take advantage of the fact that lazy expressions can be used for variadic delegate parameters. this(T delegate()[1] exp...) { mExp = exp; } enables you to use Lazy!(int)(someExpr) I used this to make an Unlambda to D compiler, and it even made it straightforward to implement the d builtin (which David Madore thought would be very hard to compile). Now the only thing that's left is getting c working properly. Stewart.
Jun 29 2007
prev sibling parent OF <nospam nospammington.com> writes:
Stewart Gordon Wrote:

 Actually you can take advantage of the fact that lazy expressions can be 
 used for variadic delegate parameters.
Using this it works fine. Thank you.
Jun 29 2007