November 04, 2007
downs Wrote:

> PS:
> module test17;
> import std.stdio;
> 
> void extwhile(lazy bool cond, void delegate() Body, void delegate()
> Finally, void delegate() Else) {
>   if (!cond()) Else();
>   else {
>     do Body(); while (cond());
>     Finally();
>   }
> }
> 
> void main() {
>   bool colliding=true;
>   int counter=0;
>   extwhile(colliding,
>     { writefln("Still colliding"); counter++; if (counter==3)
> colliding=false; },
>     { writefln("Done colliding"); },
>     { writefln("Never collided"); }
>   );
> }

Heh, closures are already spreading their intoxicating power.
November 04, 2007
Robert Fraser Wrote:

> downs Wrote:
> 
> > PS:
> > module test17;
> > import std.stdio;
> > 
> > void extwhile(lazy bool cond, void delegate() Body, void delegate()
> > Finally, void delegate() Else) {
> >   if (!cond()) Else();
> >   else {
> >     do Body(); while (cond());
> >     Finally();
> >   }
> > }
> > 
> > void main() {
> >   bool colliding=true;
> >   int counter=0;
> >   extwhile(colliding,
> >     { writefln("Still colliding"); counter++; if (counter==3)
> > colliding=false; },
> >     { writefln("Done colliding"); },
> >     { writefln("Never collided"); }
> >   );
> > }
> 
> Heh, closures are already spreading their intoxicating power.

Wow. I didn't know you could do that, even with closures.
November 04, 2007
Robert Fraser wrote:
> downs Wrote:
> 
>> PS:
>> module test17;
>> import std.stdio;
>>
>> void extwhile(lazy bool cond, void delegate() Body, void delegate()
>> Finally, void delegate() Else) {
>>   if (!cond()) Else();
>>   else {
>>     do Body(); while (cond());
>>     Finally();
>>   }
>> }
>>
>> void main() {
>>   bool colliding=true;
>>   int counter=0;
>>   extwhile(colliding,
>>     { writefln("Still colliding"); counter++; if (counter==3)
>> colliding=false; },
>>     { writefln("Done colliding"); },
>>     { writefln("Never collided"); }
>>   );
>> }
> 
> Heh, closures are already spreading their intoxicating power.

It doesn't actually use closures. Completely D 1.0 :)
Those { } things are short-hand for () { }, i.e. completely normal
delegate literals.
 --downs
November 04, 2007
Marco Aurélio Wrote:

> Charles D Hixson Wrote:
> 
> > No.  Finally should be the label on a block of code that will be executed *WHATEVER* happens in the preceding loop, including the raising of an exception.
> 
> Hmm Yeah, now that I think about it, having it on that way would make it inconsistent with the try-catch-finally behavior... Maybe adding another keyword? or something like:
> 
> for(int i = 0; i < 30; i++)
> {
>     if (something)
>         break;
> } catch (break) {
>     Foo();
> }

That's redundant. Its the same as

try {
  for(int i = 0; i < 30; i++)
  {
     if (something)
        throw breakException;
   }
 } catch (breakException) {
     Foo();
 }

Are you sure there's really a problem here? How about posting something 'evil'. If no-one in the group can think of a good refactoring
then you may have a case.  I suspect structured programming has been around too long to benefit much from anything new. That said, foreach was an awful long time coming so you may have a case.

Regards,

Bruce.
November 04, 2007
Marco Aurélio Wrote:

> Daniel Keep Wrote:
> > else would also be nice.
> 
> This would be specially usefull for implementing collisions on 2D games with bitmap-based coldefs, something along the lines of:
> 
> while(object.isCollidingWithGround())
> {
>      object.moveUp();
> } finally {
>      object.stop();
> } else {
>      object.applyGravity();
> }
> 
> While the object is colliding with the ground, move it up... Then >stop it.. and if he wasn't colliding with the ground in the first place, >apply gravity.

What's wrong with:

if  (!object.isCollidingWithGround())
      object.applyGravity();
else do
{
     object.moveUp();
} while (object.isCollidingWithGround());
object.stop();

You've got one extra condition to write but its not really a big deal.
November 04, 2007
BCS Wrote:

> Reply to Daniel,
> 
> > downs wrote:
> > 
> >> Marco Aurélio wrote:
> >> 
> >>> 2 - for .. finally, while .. finally:
> >>> 
> >>> This would allow having something like:
> >>> 
> >>> while(someCondition)
> >>> {
> >>> DoSomething();
> >>> } finally {
> >>> DoOtherThing();
> >>> }
> >>> The "finally" block would be called at the end of the repetition,
> >>> only if no "break" was used. This may not seem useful at first, but
> >>> I think can reduce the number of flags needed to implement various
> >>> algorithms, making the code faster and more elegant. I'm not sure if
> >>> this is already possible with scope guards.
> >>> 
> >> I like that. :) vote +1
> >> --downs
> > else would also be nice.
> > 
> > foreach( foo ; bar )
> > DoSomethingWith(foo);
> > finally
> > DoSomethingAfterwards();
> > else
> > DoSomethingElseSinceBarIsEmpty();
> > But maybe that's just me.
> > 
> > -- Daniel
> > 
> 
> 
> I'd rather an extension of the scope syntax
> 
> while(cond)
> {
>    scope(last) DoOnCondFailed();
>    scope(break) DoOnBreak(); // or any explicet quit
>    scope(skip) DoIfCondNeverPasses();
>    scope(first) goto SkipSomeStuff;
>    ...
> }
>

Lets take these one at a time:-

scope(last):  - totally useless assuming I understood the meaning

while(cond)
{

}
DoOnCondFailed();

scope(skip):  - saves you one conditional - not very useful

if (!cond)
   DoIfCondNeverPasses();
else do
{
   ...
}
while(cond);

scope(break): - saves you a function call or two.

while(cond)
{
  ...
  if (cond2) DoOnBreak(); break;
  ...
  if (cond3) DoOnBreak(); break;
  ...
}

scope(first):

if (cond)
{
  DoFirstTimeOnly();
}
while(cond)
{
  ...
}

goto is evil so you want the opposite too

scope(notonfirst):

while(cond)
{
   static bool firstTime = false;
   if (firstTime==false) { firstTime = true; body1(); }
   body2();
}

For this case there is slight justification for something. but that something is a notfirst() functor.

class notfirst
{
private:
   bool first;
   delegate doOnFirst;
   delegate doOnSubsequent;
public:
   notfirst(delegate doOnFirst_,
              delegate doOnSubsequent_):
      first(true)
      doOnFirst(doOnFirst_),
      doOnSubsequent(doOnSubsequent_)
   {}

   void run() {
      if (first)
      {
         doOnFirst();
         first = false;
      }
      else doOnSubsequent();
   }
};

while(cond)
{
   doOnFirst(body1(); body2());
}


What is this equivalent to in the worst case scenario that you need all four?

if (!cond)
   DoIfCondNeverPasses();
else
{
  DoFirstTimeOnly();
  do
  {
     doOnFirst(body1();
     {
         ...
         if (cond2) DoOnBreak(); break;
         ...
         if (cond3) DoOnBreak(); break;
         ...
      }
  }
  while(cond);
}
DoOnCondFailed();

On balance this doesn't seem like a necessary or really useful syntax improvement to me.

Regards,

Bruce.
to me.
November 05, 2007
Reply to Bruce,

> BCS Wrote:
> 
>> I'd rather an extension of the scope syntax
>> 
>> while(cond)
>> {
>> scope(last) DoOnCondFailed();
>> scope(break) DoOnBreak(); // or any explicet quit
>> scope(skip) DoIfCondNeverPasses();
>> scope(first) goto SkipSomeStuff;
>> ...
>> }
> Lets take these one at a time:-
> 
> scope(last):  - totally useless assuming I understood the meaning
> 
> while(cond) {
> }
> DoOnCondFailed();

for(int i = 5; i>0 i--)
{
  scope(last) ThisNeverRuns();
  break;
}

> scope(skip):  - saves you one conditional - not very useful
> 
> if (!cond)
> DoIfCondNeverPasses();
> else do
> {
> ...
> }
> while(cond);
> 

mine looks better (IMHO)

> scope(break): - saves you a function call or two.
> 
> while(cond)
> {
> ...
> if (cond2) DoOnBreak(); break;
> ...
> if (cond3) DoOnBreak(); break;
> ...
> }

you prove half of my point and don't address the other half

while(cond)
{
...
if (cond2)
 DoOnBreak();
break;
...   // this never runs
if (cond3)
 DoOnBreak();

break;
...
}

If you didn't get it correct in this case, what are the chances of making an error in a more complicated case

The other part is that it is very easy to forget to add the DoOnBreak to one of the breaks (you try finding them all in old code) or adding it to every new one (Now what needs to be done on break this time).

Also, it will work with mixin(string) when you can't get to the string.

> scope(first):
> 
> if (cond)
> {
> DoFirstTimeOnly();
> }
> while(cond)
> {
> ...
> }
> goto is evil so you want the opposite too
> 
> scope(notonfirst):
> 

good idea.

> while(cond)
> {
> static bool firstTime = false;
> if (firstTime==false) { firstTime = true; body1(); }
> body2();
> }
> For this case there is slight justification for something. but that
> something is a notfirst() functor.
> 
> class notfirst
> {
> private:
> bool first;
> delegate doOnFirst;
> delegate doOnSubsequent;
> public:
> notfirst(delegate doOnFirst_,
> delegate doOnSubsequent_):
> first(true)
> doOnFirst(doOnFirst_),
> doOnSubsequent(doOnSubsequent_)
> {}
> void run() {
> if (first)
> {
> doOnFirst();
> first = false;
> }
> else doOnSubsequent();
> }
> };
> while(cond)
> {
> doOnFirst(body1(); body2());
> }

My eyes!!! I can't think of anything good to say about that solution.

> What is this equivalent to in the worst case scenario that you need
> all four?
> 
> if (!cond)
> DoIfCondNeverPasses();
> else
> {
> DoFirstTimeOnly();
> do
> {
> doOnFirst(body1();
> {
> ...
> if (cond2) DoOnBreak(); break;
> ...
> if (cond3) DoOnBreak(); break;
> ...
> }
> }
> while(cond);
> }
> DoOnCondFailed();
> 
> On balance this doesn't seem like a necessary or really useful syntax
> improvement to me.
> 

the same can be said for scope(failure/success/exit). It's all sugar. I think the scope(*) solution looks better and is easier to read an maintain in _all_ the cases you list.

In all these cases the compiler can trivially implement them with copy/paste and by rearranging jumps.

> Regards,
> 
> Bruce.
> to me.



November 05, 2007
Reply to Bruce,

> Marco Aurélio Wrote:
> 
>> Charles D Hixson Wrote:
>> 
>>> No.  Finally should be the label on a block of code that will be
>>> executed *WHATEVER* happens in the preceding loop, including the
>>> raising of an exception.
>>> 
>> Hmm Yeah, now that I think about it, having it on that way would make
>> it inconsistent with the try-catch-finally behavior... Maybe adding
>> another keyword? or something like:
>> 
>> for(int i = 0; i < 30; i++)
>> {
>> if (something)
>> break;
>> } catch (break) {
>> Foo();
>> }
> That's redundant. Its the same as
> 
> try {
> for(int i = 0; i < 30; i++)
> {
> if (something)
> throw breakException;
> }
> } catch (breakException) {
> Foo();
> }

Tell me that is a joke. If you don't see the problems with that then....

Do you have any idea how mush potential for overhead there is in that? The other solution has one jump, that has a memory allocation (and a free at some point) a bit of stack un winding, Maybe a RTTI work and who only known what else. Plus it will (incorrectly) trigger any intervening scope(failure) and sooner or later you will need to start fabricating types to keep track of what loop the break is for.

> Are you sure there's really a problem here? How about posting
> something 'evil'. If no-one in the group can think of a good
> refactoring
> 
> then you may have a case.  I suspect structured programming has been
> around too long to benefit much from anything new. That said, foreach
> was an awful long time coming so you may have a case.
> 
> Regards,
> 
> Bruce.
> 


November 05, 2007
BCS Wrote:

> Reply to Bruce,
> 
> > Marco Aurélio Wrote:
> > 
> >> Charles D Hixson Wrote:
> >> 
> >>> No.  Finally should be the label on a block of code that will be executed *WHATEVER* happens in the preceding loop, including the raising of an exception.
> >>> 
> >> Hmm Yeah, now that I think about it, having it on that way would make it inconsistent with the try-catch-finally behavior... Maybe adding another keyword? or something like:
> >> 
> >> for(int i = 0; i < 30; i++)
> >> {
> >> if (something)
> >> break;
> >> } catch (break) {
> >> Foo();
> >> }
> > That's redundant. Its the same as
> > 
> > try {
> > for(int i = 0; i < 30; i++)
> > {
> > if (something)
> > throw breakException;
> > }
> > } catch (breakException) {
> > Foo();
> > }
> 
> Tell me that is a joke. If you don't see the problems with that then....
> 
> Do you have any idea how mush potential for overhead there is in that? The other solution has one jump, that has a memory allocation (and a free at some point) a bit of stack un winding, Maybe a RTTI work and who only known what else. Plus it will (incorrectly) trigger any intervening scope(failure) and sooner or later you will need to start fabricating types to keep track of what loop the break is for.
> 
Fair point but the expense of exceptions depends on how they are implemented and what else is going on. You effectively have a form of stack unwinding when you leave a scope. An exception doesn't have to be allocated on the stack but yes its less efficient than a break. I try to avoid using breaks myself because they pollute the control flow too much. Likewise exceptions but they are not supposed to be used for control flow. I should know better. How about:
bool breakNow = false;
for(int i = 0;
    i < 30 && breakNow == false;
    i++)
{
   if (something)
       breakNow = true;
   ...
}
if (breakNow)
{
   Foo();
}

It still doesn't justify a language enhancement as far as I can see.

> > Are you sure there's really a problem here? How about posting something 'evil'. If no-one in the group can think of a good refactoring
> > 
> > then you may have a case.  I suspect structured programming has been around too long to benefit much from anything new. That said, foreach was an awful long time coming so you may have a case.
> > 
> > Regards,
> > 
> > Bruce.
> > 
> 



November 05, 2007
BCS Wrote:

> Reply to Bruce,
> 
> > BCS Wrote:
> > 
> >> I'd rather an extension of the scope syntax
> >> 
> >> while(cond)
> >> {
> >> scope(last) DoOnCondFailed();
> >> scope(break) DoOnBreak(); // or any explicet quit
> >> scope(skip) DoIfCondNeverPasses();
> >> scope(first) goto SkipSomeStuff;
> >> ...
> >> }
> > Lets take these one at a time:-
> > 
> > scope(last):  - totally useless assuming I understood the meaning
> > 
> > while(cond) {
> > }
> > DoOnCondFailed();
> 
> for(int i = 5; i>0 i--)
> {
>    scope(last) ThisNeverRuns();
>    break;
> }
>
Right. So what you really want is something that runs when the scope ends naturally but not on a break.

for(int i=5;i>0;i--)
{
  ...
  break;
}
if (i<=0) ThisNeverRuns();


> > scope(skip):  - saves you one conditional - not very useful
> > 
> > if (!cond)
> > DoIfCondNeverPasses();
> > else do
> > {
> > ...
> > }
> > while(cond);
> > 
> 
> mine looks better (IMHO)
>
It doesn't justify a syntax change (IMHO)

> > scope(break): - saves you a function call or two.
> > 
> > while(cond)
> > {
> > ...
> > if (cond2) DoOnBreak(); break;
> > ...
> > if (cond3) DoOnBreak(); break;
> > ...
> > }
> 
> you prove half of my point and don't address the other half
>
What was the other half again?

> while(cond)
> {
> ...
> if (cond2)
>   DoOnBreak();
> break;
> ...   // this never runs
> if (cond3)
>   DoOnBreak();
> 
> break;
> ...
> }
> 
> If you didn't get it correct in this case, what are the chances of making an error in a more complicated case
> 
Harsh. I was writing at 2am or thereabouts. Also you missed a semi-colon in your for loop above does that render anything moot?
Personally I try to keep the body of a loop and in particular the control flow simple. I try to avoid breaks and put anything too large separate functions where possible.

> The other part is that it is very easy to forget to add the DoOnBreak to one of the breaks (you try finding them all in old code) or adding it to every new one (Now what needs to be done on break this time).
> 
> Also, it will work with mixin(string) when you can't get to the string.
>
That might be a more valid use. Care to post an example? I think we have to be careful with mixin's. They could easily be as abused as macros. There's no reason you couldn't write with a style that has an exit condition specified in the mixin. I don't like the idea of having a mixin with a break or return hidden in it (that goes beyond the scope of the mixin itself). That could make it very hard to follow the control flow.

> > scope(first):
> > 
> > if (cond)
> > {
> > DoFirstTimeOnly();
> > }
> > while(cond)
> > {
> > ...
> > }
> > goto is evil so you want the opposite too
> > 
> > scope(notonfirst):
> > 
> 
> good idea.
> 
> > while(cond)
> > {
> > static bool firstTime = false;
> > if (firstTime==false) { firstTime = true; body1(); }
> > body2();
> > }
> > For this case there is slight justification for something. but that
> > something is a notfirst() functor.
> > 
> > class notfirst
> > {
> > private:
> > bool first;
> > delegate doOnFirst;
> > delegate doOnSubsequent;
> > public:
> > notfirst(delegate doOnFirst_,
> > delegate doOnSubsequent_):
> > first(true)
> > doOnFirst(doOnFirst_),
> > doOnSubsequent(doOnSubsequent_)
> > {}
> > void run() {
> > if (first)
> > {
> > doOnFirst();
> > first = false;
> > }
> > else doOnSubsequent();
> > }
> > };
> > while(cond)
> > {
> > doOnFirst(body1(); body2());
> > }
> 
> My eyes!!! I can't think of anything good to say about that solution.
>
Rather than notOnFirst I think the functor is more properly called once.

once {
  foo();
}

As a shorthand for:

bool hasBeenRunOnce = false;
if (hasBeenRunOnce == false)
{
  hasBeenRunOnce = true;
  foo();
}

> > What is this equivalent to in the worst case scenario that you need all four?
> > 
> > if (!cond)
> > DoIfCondNeverPasses();
> > else
> > {
> > DoFirstTimeOnly();
> > do
> > {
> > doOnFirst(body1();
> > {
> > ...
> > if (cond2) DoOnBreak(); break;
> > ...
> > if (cond3) DoOnBreak(); break;
> > ...
> > }
> > }
> > while(cond);
> > }
> > DoOnCondFailed();
> > 
> > On balance this doesn't seem like a necessary or really useful syntax improvement to me.
> > 
> 
> the same can be said for scope(failure/success/exit). It's all sugar. I think the scope(*) solution looks better and is easier to read an maintain in _all_ the cases you list.
>
If you have too much sugar you can get diabetes. Cut down or switch to saccharin or get yourself some insulin :)

> In all these cases the compiler can trivially implement them with copy/paste and by rearranging jumps.
> 
I'm more worried about the programmer having to maintain code using bizarre constructs. The compiler can be clever out of sight.

Regards,

Bruce.