September 01, 2006
Chris Nicholson-Sauls wrote:
> 
> 
> Ivan Senji wrote:
>> Walter Bright wrote:
>>
>>> The implicit conversion to delegate just broke too much. Instead, I'm trying out Tom S.'s suggestion of using a 'lazy' parameter storage class.
>>>
>>> http://www.digitalmars.com/d/changelog.html
>>
>>
>> Hmm, I am afraid I have to admit I'm a little bit puzzled by this lazy thing.
>>
>> Let's say we have:
>>
>> void dotimes(int count, lazy void exp)
>> {
>>   for (int i = 0; i < count; i++)
>>   {
>>     exp();
>>   }
>> }
>>
>> and I can call it like this:
>>
>> int x;
>> dotimes(5, x++);
>>
>> And it works as expected and x end up being 5.
>>
>> But shouldn't I also be allowed to pass in a delegate of the appropriate type
>>
>> dotimes(5, {writefln(x); x++;});
>>
>> A funny thing happens: nothing. The loop in dotimes does get executed 5 times but delegate passed in isn't called?
>>
>>
> 
> This is because anonymous delegates are themselves expressions, and so your 'exp()' just evaluates to the delegate itself -- it does not invoke it.  However, to achieve what you were trying to do, just make use of parentheses and the comma operator.  Also, you may provide an overload that takes a lazy delegate.  Here is my own test program, which worked without issue:

Hmm, there is something about this lazy stuff that is troubling me and I'm not exactly sure what it is and if I can explain it:

so it is x++ vs {writefln(x); x++;}

The first one is an expression that looks like it is executed at call site but it isn't because that parameter is declared to be lazy. It is executed inside dotimes. That is ok.

The second is a block of statements, actually it is a void delegate. It isn't executed at call site so it also will not be executed in dotimes.
To make it be executed in dotimes I have to make it into something that looks like it is being executed: for example: {writefln(x); x++;}()

And this is when thing begin to suck (unless I am missing something?).

So if I had
dotimes(5, x++);

and now if I want it to do 5 times somethin a bit more than just x++ i cannot just write:
dotimes(5, {writefln(); x++;});

And now a completly different function is called (one taking void delegate() as argument), to get it to call the lazy function I have to change to type from void delegate() to void by pretending to be calling the delegate.

So is there a point to having lazy, I could only have written dotimes that takes void delegate() an write this:

dotimes(5, {x++;});  and   dotimes(5, {writefln(); x++;} );

vs

dotimes(5, x++);     and   dotimes(5, {writefln(); x++;}() );

And now (while writing) I finally figure out what was troubling me: each of these approaches has a pretty case and an ugly case, the two pretty cases being:

dotimes(5, x++);  and   dotimes(5, {writefln(); x++;} );

But to get that to work 2 different methods are needed.

Don't feel the need to reply to this incoherent post :D , it is just some of my thoughts about lazy.

Although now I understand what is happening and why, this is still troubling me.
September 01, 2006
Ivan Senji wrote:
> Chris Nicholson-Sauls wrote:
>>
>>
>> Ivan Senji wrote:
>>> Walter Bright wrote:
>>>
>>>> The implicit conversion to delegate just broke too much. Instead, I'm trying out Tom S.'s suggestion of using a 'lazy' parameter storage class.
>>>>
>>>> http://www.digitalmars.com/d/changelog.html
>>>
>>>
>>> Hmm, I am afraid I have to admit I'm a little bit puzzled by this lazy thing.
>>>
>>> Let's say we have:
>>>
>>> void dotimes(int count, lazy void exp)
>>> {
>>>   for (int i = 0; i < count; i++)
>>>   {
>>>     exp();
>>>   }
>>> }
>>>
>>> and I can call it like this:
>>>
>>> int x;
>>> dotimes(5, x++);
>>>
>>> And it works as expected and x end up being 5.
>>>
>>> But shouldn't I also be allowed to pass in a delegate of the appropriate type
>>>
>>> dotimes(5, {writefln(x); x++;});
>>>
>>> A funny thing happens: nothing. The loop in dotimes does get executed 5 times but delegate passed in isn't called?
>>>
>>>
>>
>> This is because anonymous delegates are themselves expressions, and so your 'exp()' just evaluates to the delegate itself -- it does not invoke it.  However, to achieve what you were trying to do, just make use of parentheses and the comma operator.  Also, you may provide an overload that takes a lazy delegate.  Here is my own test program, which worked without issue:
> 
> Hmm, there is something about this lazy stuff that is troubling me and I'm not exactly sure what it is and if I can explain it:
> 
> so it is x++ vs {writefln(x); x++;}
> 
> The first one is an expression that looks like it is executed at call site but it isn't because that parameter is declared to be lazy. It is executed inside dotimes. That is ok.
> 
> The second is a block of statements, actually it is a void delegate. It isn't executed at call site so it also will not be executed in dotimes.
> To make it be executed in dotimes I have to make it into something that looks like it is being executed: for example: {writefln(x); x++;}()
> 
> And this is when thing begin to suck (unless I am missing something?).
> 
> So if I had
> dotimes(5, x++);
> 
> and now if I want it to do 5 times somethin a bit more than just x++ i cannot just write:
> dotimes(5, {writefln(); x++;});
> 
> And now a completly different function is called (one taking void delegate() as argument), to get it to call the lazy function I have to change to type from void delegate() to void by pretending to be calling the delegate.
> 
> So is there a point to having lazy, I could only have written dotimes that takes void delegate() an write this:
> 
> dotimes(5, {x++;});  and   dotimes(5, {writefln(); x++;} );
> 
> vs
> 
> dotimes(5, x++);     and   dotimes(5, {writefln(); x++;}() );
> 
> And now (while writing) I finally figure out what was troubling me: each of these approaches has a pretty case and an ugly case, the two pretty cases being:
> 
> dotimes(5, x++);  and   dotimes(5, {writefln(); x++;} );

Actually I just figured out i can have both pretty cases :) Hooray, just need to change dotimes to:

void dotimes(int count, void delegate()[] exp...)
{
  for (int i = 0; i < count; i++)
  {
    foreach(dg; exp)
      dg();
  }
}

With the added benefit of being able to do the craziest things:

dotimes(5, f++, writefln(f), f*=2);

being equal to:

dotimes(5, {f++; writefln(f); f*=2;});

This isn't bad afterall, but I've lost lazy on the way. (Note to self: figure out where to use lazy)
September 01, 2006
Ivan Senji wrote:
> Chris Nicholson-Sauls wrote:
>>
>>
>> Ivan Senji wrote:
>>> Walter Bright wrote:
>>>
>>>> The implicit conversion to delegate just broke too much. Instead, I'm trying out Tom S.'s suggestion of using a 'lazy' parameter storage class.
>>>>
>>>> http://www.digitalmars.com/d/changelog.html
>>>
>>>
>>> Hmm, I am afraid I have to admit I'm a little bit puzzled by this lazy thing.
>>>
>>> Let's say we have:
>>>
>>> void dotimes(int count, lazy void exp)
>>> {
>>>   for (int i = 0; i < count; i++)
>>>   {
>>>     exp();
>>>   }
>>> }
>>>
>>> and I can call it like this:
>>>
>>> int x;
>>> dotimes(5, x++);
>>>
>>> And it works as expected and x end up being 5.
>>>
>>> But shouldn't I also be allowed to pass in a delegate of the appropriate type
>>>
>>> dotimes(5, {writefln(x); x++;});
>>>
>>> A funny thing happens: nothing. The loop in dotimes does get executed 5 times but delegate passed in isn't called?
>>>
>>>
>>
>> This is because anonymous delegates are themselves expressions, and so your 'exp()' just evaluates to the delegate itself -- it does not invoke it.  However, to achieve what you were trying to do, just make use of parentheses and the comma operator.  Also, you may provide an overload that takes a lazy delegate.  Here is my own test program, which worked without issue:
> 
> Hmm, there is something about this lazy stuff that is troubling me and I'm not exactly sure what it is and if I can explain it:
> 
> so it is x++ vs {writefln(x); x++;}
> 
> The first one is an expression that looks like it is executed at call site but it isn't because that parameter is declared to be lazy. It is executed inside dotimes. That is ok.
> 
> The second is a block of statements, actually it is a void delegate. It isn't executed at call site so it also will not be executed in dotimes.
> To make it be executed in dotimes I have to make it into something that looks like it is being executed: for example: {writefln(x); x++;}()
> 
> And this is when thing begin to suck (unless I am missing something?).
> 
> So if I had
> dotimes(5, x++);
> 
> and now if I want it to do 5 times somethin a bit more than just x++ i cannot just write:
> dotimes(5, {writefln(); x++;});
> 
> And now a completly different function is called (one taking void delegate() as argument), to get it to call the lazy function I have to change to type from void delegate() to void by pretending to be calling the delegate.
> 
> So is there a point to having lazy, I could only have written dotimes that takes void delegate() an write this:
> 
> dotimes(5, {x++;});  and   dotimes(5, {writefln(); x++;} );
> 
> vs
> 
> dotimes(5, x++);     and   dotimes(5, {writefln(); x++;}() );
> 
> And now (while writing) I finally figure out what was troubling me: each of these approaches has a pretty case and an ugly case, the two pretty cases being:
> 
> dotimes(5, x++);  and   dotimes(5, {writefln(); x++;} );
> 
> But to get that to work 2 different methods are needed.

It may be that delegates should be implicitly convertible to lazy expressions with the same signature.  Thus:

    void fn( lazy int x );

Should accept both:

    fn( i++ );

and

    fn( { return i++; } );

Currently, they only accept the former.


Sean
September 01, 2006
Sean Kelly wrote:
> Ivan Senji wrote:
>> Chris Nicholson-Sauls wrote:
>>>
>>>
>>> Ivan Senji wrote:
>>>> Walter Bright wrote:
>>>>
>>>>> The implicit conversion to delegate just broke too much. Instead, I'm trying out Tom S.'s suggestion of using a 'lazy' parameter storage class.
>>>>>
>>>>> http://www.digitalmars.com/d/changelog.html
>>>>
>>>>
>>>> Hmm, I am afraid I have to admit I'm a little bit puzzled by this lazy thing.
>>>>
>>>> Let's say we have:
>>>>
>>>> void dotimes(int count, lazy void exp)
>>>> {
>>>>   for (int i = 0; i < count; i++)
>>>>   {
>>>>     exp();
>>>>   }
>>>> }
>>>>
>>>> and I can call it like this:
>>>>
>>>> int x;
>>>> dotimes(5, x++);
>>>>
>>>> And it works as expected and x end up being 5.
>>>>
>>>> But shouldn't I also be allowed to pass in a delegate of the appropriate type
>>>>
>>>> dotimes(5, {writefln(x); x++;});
>>>>
>>>> A funny thing happens: nothing. The loop in dotimes does get executed 5 times but delegate passed in isn't called?
>>>>
>>>>
>>>
>>> This is because anonymous delegates are themselves expressions, and so your 'exp()' just evaluates to the delegate itself -- it does not invoke it.  However, to achieve what you were trying to do, just make use of parentheses and the comma operator.  Also, you may provide an overload that takes a lazy delegate.  Here is my own test program, which worked without issue:
>>
>> Hmm, there is something about this lazy stuff that is troubling me and I'm not exactly sure what it is and if I can explain it:
>>
>> so it is x++ vs {writefln(x); x++;}
>>
>> The first one is an expression that looks like it is executed at call site but it isn't because that parameter is declared to be lazy. It is executed inside dotimes. That is ok.
>>
>> The second is a block of statements, actually it is a void delegate. It isn't executed at call site so it also will not be executed in dotimes.
>> To make it be executed in dotimes I have to make it into something that looks like it is being executed: for example: {writefln(x); x++;}()
>>
>> And this is when thing begin to suck (unless I am missing something?).
>>
>> So if I had
>> dotimes(5, x++);
>>
>> and now if I want it to do 5 times somethin a bit more than just x++ i cannot just write:
>> dotimes(5, {writefln(); x++;});
>>
>> And now a completly different function is called (one taking void delegate() as argument), to get it to call the lazy function I have to change to type from void delegate() to void by pretending to be calling the delegate.
>>
>> So is there a point to having lazy, I could only have written dotimes that takes void delegate() an write this:
>>
>> dotimes(5, {x++;});  and   dotimes(5, {writefln(); x++;} );
>>
>> vs
>>
>> dotimes(5, x++);     and   dotimes(5, {writefln(); x++;}() );
>>
>> And now (while writing) I finally figure out what was troubling me: each of these approaches has a pretty case and an ugly case, the two pretty cases being:
>>
>> dotimes(5, x++);  and   dotimes(5, {writefln(); x++;} );
>>
>> But to get that to work 2 different methods are needed.
> 
> It may be that delegates should be implicitly convertible to lazy expressions with the same signature.  Thus:

Maybe, that is what I expected it would do in the first place (it's too late to think now :))

> 
>     void fn( lazy int x );
> 
> Should accept both:
> 
>     fn( i++ );
> 
> and
> 
>     fn( { return i++; } );
> 
> Currently, they only accept the former.

Except in a strange (but documented) feature that variadic arguments are implicitly converted to delegates (used in my reply to myself).
September 02, 2006
On Fri, 01 Sep 2006 10:59:18 -0700, Walter Bright wrote:

>> suggestion that in/out/inout be specified at the call side as well.
> 
> Yes, it would be the same. But I think the interface should be set by the definition, not the use.

Yes we agree. But the purpose of this suggestion is not to 'set the definition' but to 'document the usage' of the definition. The purpose is to help both the compiler and human reader to validate the intention of the coder. The definition would still be set when declaring the function but if the compiler optionally allowed parameter storage class keywords at the point of invocation, you could help ensure that the coder is doing what she intended to do. It is a form of DbC.

> That's the usual practice with programming languages, and I've never seen it raised as an issue before.

So what? You are in the wonderful position of creating a useful and superb tool that can be very special. You have been a trail blazer and can continue to be one.

-- 
Derek Parnell
Melbourne, Australia
"Down with mediocrity!"
September 02, 2006
BCS wrote:

> Oskar Linde wrote:
>> Jarrett Billingsley wrote:
>> 
>> 
>>>"Kirk McDonald" <kirklin.mcdonald@gmail.com> wrote in message news:ed7ddg$6r2$1@digitaldaemon.com...
>>>
>>>
>>>>Nice. I'll have to play with the improved IFTI support.
>>>>
>>>
>>>Hm.  I missed this new feature.  What does it include?  There's not even a link to a conversation, and I don't remember any threads about this.
>> 
>> 
>> It is really just a small patch that enables implicit function template instantiation for member function and operator templates. It should work identically to how IFTI works for free functions.
>> 
>> It should open up quite a few door. Here is a quick demo I hacked together of how compile time dimensionality checking can be implemented:
>> 
>> http://www.csc.kth.se/~ol/physical.d
>> 
>> /Oskar
>> 
>> 
> 
> 
> Hot, Dog!!!
> 
> 
> This is a project I was hoping to get to a LONG time ago. (The d code side, not the getting-D-to-do-this side)
> 
> 
> Best I came up with was a Runtime version.
> 
> http://www.webpages.uidaho.edu/~shro8822/unit.d
> 
> Mind if I try to graft in a "few" ;) more units and some unittest?

Certainly not. Feel free to go ahead. :)

But as I said, this is just a quick hack. Some improvements would be a generic Quantity supporting an arbitrary number of dimensions and rational exponents.

/Oskar
September 02, 2006
Walter Bright wrote:

> Derek Parnell wrote:
>> Damn! I didn't know that. What's the rationale for such a seemingly arbitrary restriction?
> 
> No virtual functions for structs!

If one considered toString() important enough it could be added to the TypeInfo for structs, just like toHash, opEquals and opCmp are today. It would make certain sense to support all the methods of Object in this way.

/Oskar
September 02, 2006
Ivan Senji wrote:
> Chris Nicholson-Sauls wrote:
> 
>>
>>
>> Ivan Senji wrote:
>>
>>> Walter Bright wrote:
>>>
>>>> The implicit conversion to delegate just broke too much. Instead, I'm trying out Tom S.'s suggestion of using a 'lazy' parameter storage class.
>>>>
>>>> http://www.digitalmars.com/d/changelog.html
>>>
>>>
>>>
>>> Hmm, I am afraid I have to admit I'm a little bit puzzled by this lazy thing.
>>>
>>> Let's say we have:
>>>
>>> void dotimes(int count, lazy void exp)
>>> {
>>>   for (int i = 0; i < count; i++)
>>>   {
>>>     exp();
>>>   }
>>> }
>>>
>>> and I can call it like this:
>>>
>>> int x;
>>> dotimes(5, x++);
>>>
>>> And it works as expected and x end up being 5.
>>>
>>> But shouldn't I also be allowed to pass in a delegate of the appropriate type
>>>
>>> dotimes(5, {writefln(x); x++;});
>>>
>>> A funny thing happens: nothing. The loop in dotimes does get executed 5 times but delegate passed in isn't called?
>>>
>>>
>>
>> This is because anonymous delegates are themselves expressions, and so your 'exp()' just evaluates to the delegate itself -- it does not invoke it.  However, to achieve what you were trying to do, just make use of parentheses and the comma operator.  Also, you may provide an overload that takes a lazy delegate.  Here is my own test program, which worked without issue:
> 
> 
> Hmm, there is something about this lazy stuff that is troubling me and I'm not exactly sure what it is and if I can explain it:
> 
> so it is x++ vs {writefln(x); x++;}
> 
> The first one is an expression that looks like it is executed at call site but it isn't because that parameter is declared to be lazy. It is executed inside dotimes. That is ok.
> 
> The second is a block of statements, actually it is a void delegate. It isn't executed at call site so it also will not be executed in dotimes.
> To make it be executed in dotimes I have to make it into something that looks like it is being executed: for example: {writefln(x); x++;}()
> 
> And this is when thing begin to suck (unless I am missing something?).
> 
> So if I had
> dotimes(5, x++);
> 
> and now if I want it to do 5 times somethin a bit more than just x++ i cannot just write:
> dotimes(5, {writefln(); x++;});
> 
> And now a completly different function is called (one taking void delegate() as argument), to get it to call the lazy function I have to change to type from void delegate() to void by pretending to be calling the delegate.
> 
> So is there a point to having lazy, I could only have written dotimes that takes void delegate() an write this:
> 
> dotimes(5, {x++;});  and   dotimes(5, {writefln(); x++;} );
> 
> vs
> 
> dotimes(5, x++);     and   dotimes(5, {writefln(); x++;}() );
> 
> And now (while writing) I finally figure out what was troubling me: each of these approaches has a pretty case and an ugly case, the two pretty cases being:
> 
> dotimes(5, x++);  and   dotimes(5, {writefln(); x++;} );
> 
> But to get that to work 2 different methods are needed.
> 
> Don't feel the need to reply to this incoherent post :D , it is just some of my thoughts about lazy.
> 
> Although now I understand what is happening and why, this is still troubling me.

Or... just put parentheses around it instead of braces, and use commas instead of semicolons, like I did.  That works without having to do any extra work at all.  (Although your typesafe variadic delegate parameter trick is pretty interesting, too.)

dotimes(5, x++);
dotimes(5, (writefln(x), x++));

-- Chris Nicholson-Sauls
September 02, 2006
Derek Parnell wrote:
> On Fri, 01 Sep 2006 10:59:18 -0700, Walter Bright wrote:
> 
>>> suggestion that in/out/inout be specified at the call side as well.
>> Yes, it would be the same. But I think the interface should be set by the definition, not the use. 
> 
> Yes we agree. But the purpose of this suggestion is not to 'set the
> definition' but to 'document the usage' of the definition. The purpose is
> to help both the compiler and human reader to validate the intention of the
> coder. The definition would still be set when declaring the function but if
> the compiler optionally allowed parameter storage class keywords at the
> point of invocation, you could help ensure that the coder is doing what she
> intended to do. It is a form of DbC.

Setting the behavior at both the point of definition and point of use may result in fewer programming bugs, it does so at the risk of making modifications harder and annoying programmers.

>> That's the usual practice with programming languages, and I've never seen it raised as an issue before.
> 
> So what? You are in the wonderful position of creating a useful and superb
> tool that can be very special. You have been a trail blazer and can
> continue to be one.

If people don't like adding { } around the lazy expressions, they aren't going to like putting 'lazy' in front of them, either. Every time I've seen a feature like this, the complexity was at the definition point, *not* the use point.
September 02, 2006
Oskar Linde wrote:
> Walter Bright wrote:
> 
>> Derek Parnell wrote:
>>> Damn! I didn't know that. What's the rationale for such a seemingly
>>> arbitrary restriction?
>> No virtual functions for structs!
> 
> If one considered toString() important enough it could be added to the
> TypeInfo for structs, just like toHash, opEquals and opCmp are today. It
> would make certain sense to support all the methods of Object in this way.

That makes sense.