View mode: basic / threaded / horizontal-split · Log in · Help
February 26, 2006
Re: DMD 0.148 - scope guard
Kyle Furlong wrote:
> for any scope:
> 
> in
> {
> }
> out
> {
> }
> failure
> {
> }
> success
> {
> }
> body
> {
> }
> 
> Pros: fits into and expands current language structures
> Cons: still a bit verbose as compared to current syntax

I'm sorry to say but IMO this miss the whole point.

Current keywords: in, out, body are place-insensitive . They are just blocks
that may be moved relatively for themselves and this will not change
anything. The whole point in "scope guarding" is that expression you type
_registers_ some piece of code (to be called in some conditions) in place
where it appears.

You gain nothing with such syntax. If you want to deal with whole scope then
you can use finally and catch blocks.

Examples:

Current syntax:
void LongFunction()
{
   foo1();
   on_scope_failure clean_what_foo1_did();

   foo2();
   on_scope_failure clean_what_foo2_did();

   foo3();
   on_scope_failure clean_what_foo3_did();

   foo4();
}


What I understand from your proposal:
void LongFunction()
{
   foo1();
   failure {
       on_scope_failure clean_what_foo1_did();
   }
   body {

      foo2();

      failure {
          on_scope_failure clean_what_foo2_did();
      }
      body {
           foo3();
      
           failure {
              on_scope_failure clean_what_foo3_did();
           }
           body {
               foo4();
           }
      }
   }

}

If you think about:
void LongFunction()
{
   foo1();
   failure{
     clean_what_foo1_did();
   }

   foo2();
   failure {
    clean_what_foo2_did();
   }
   
   foo3();
   failure {
    on_scope_failure clean_what_foo3_did();
   }

   foo4();
}

Then concept is OK, but you can no longer say this "will fit and expand
current syntax", because it's used totally diffrent from  body {} in {} out
{} blocks.
February 26, 2006
Re: DMD 0.148 - scope guard
Cris wrote:
> Why do you need "case" at all?

Good point. Intention was to make it similar to switch (smth.) block.

void LongFunction()
{
   State save = UIElement.GetState();
   register (scope) {
       pass: UIElement.SetState(save); break;
       fail: UIElement.SetState(Failed(save));
   }
   ...lots of code...
}

This looks better. I think about "(scope)" part. It's redundant _but_ very
informative and leaves open doors for expanding.

Currently I like this syntax best. IMO it naturally fits, "register" keyword
is old (but rarely used) old C-keyword and it realy means: "register this
piece of code to be called when something with scope happens".
February 26, 2006
Re: DMD 0.148 - scope guard
Walter Bright wrote:
> Scope guards are a novel feature no other language has. They're based on 
> Andrei Alexandrescu's scope guard macros, which have led to considerable 
> interest in the idea. Check out the article 
> www.digitalmars.com/d/exception-safe.html

Awesome :-)  I've been wondering when this would make it into D.  It 
certainly beats the heck out of the RAII approach.


Sean
February 26, 2006
Re: DMD 0.148 - scope guard
Dawid Ciężarkiewicz wrote:
> What do you think? Maybe later we'll come with better ideas.

VERSION I: (yeah, I know ...)

h3r3tic on #D channel said that VERSION H is too long and too switch-like.

This is hybrid of two concepts - verbose and informative "register" keyword
and short usage without switch-like syntax.

void LongFunction()
{
   State save = UIElement.GetState();
   register (scopepass) UIElement.SetState(save);
   register (scopefail) UIElement.SetState(Failed(save));
   ...lots of code...
}
February 26, 2006
Re: DMD 0.148 - scope guard
Dawid Ciężarkiewicz wrote:
> Kyle Furlong wrote:
>> for any scope:
>>
>> in
>> {
>> }
>> out
>> {
>> }
>> failure
>> {
>> }
>> success
>> {
>> }
>> body
>> {
>> }
>>
>> Pros: fits into and expands current language structures
>> Cons: still a bit verbose as compared to current syntax
> 
> I'm sorry to say but IMO this miss the whole point.
> 
> Current keywords: in, out, body are place-insensitive . They are just blocks
> that may be moved relatively for themselves and this will not change
> anything. The whole point in "scope guarding" is that expression you type
> _registers_ some piece of code (to be called in some conditions) in place
> where it appears.
> 
> You gain nothing with such syntax. If you want to deal with whole scope then
> you can use finally and catch blocks.
> 
> Examples:
> 
> Current syntax:
> void LongFunction()
> {
>     foo1();
>     on_scope_failure clean_what_foo1_did();
> 
>     foo2();
>     on_scope_failure clean_what_foo2_did();
> 
>     foo3();
>     on_scope_failure clean_what_foo3_did();
> 
>     foo4();
> }
> 
> 
> What I understand from your proposal:
> void LongFunction()
> {
>     foo1();
>     failure {
>         on_scope_failure clean_what_foo1_did();
>     }
>     body {
> 
>        foo2();
> 
>        failure {
>            on_scope_failure clean_what_foo2_did();
>        }
>        body {
>             foo3();
>        
>             failure {
>                on_scope_failure clean_what_foo3_did();
>             }
>             body {
>                 foo4();
>             }
>        }
>     }
> 
> }
> 
> If you think about:
> void LongFunction()
> {
>     foo1();
>     failure{
>       clean_what_foo1_did();
>     }
> 
>     foo2();
>     failure {
>      clean_what_foo2_did();
>     }
>     
>     foo3();
>     failure {
>      on_scope_failure clean_what_foo3_did();
>     }
> 
>     foo4();
> }
> 
> Then concept is OK, but you can no longer say this "will fit and expand
> current syntax", because it's used totally diffrent from  body {} in {} out
> {} blocks.

That is definitely *NOT* what I was saying *AT ALL*. Obviously that is a terrible syntax.

Here's what it would look like:

Current syntax:
void LongFunction()
{
    foo1();
    on_scope_failure clean_what_foo1_did();

    foo2();
    on_scope_failure clean_what_foo2_did();

    foo3();
    on_scope_failure clean_what_foo3_did();

    foo4();
}


What I understand from your proposal: (how it should have been)
void LongFunction()
failure
{
	clean_what_foo1_did();
	clean_what_foo2_did();
	clean_what_foo3_did();
	clean_what_foo4_did();
}
body
{
	foo1();
	foo2();
	foo3();
	foo4();
}

I think you missed the point that each function call does not introduce a new scope. Now, its true that if you wanted the 
operations foox() to each be in its own scope, then it would look like this:

void LongFunction()
{
	failure
	{
		clean_what_foo1_did();
	}
	body
	{
		foo1();
	}
	failure
	{
		clean_what_foo2_did();
	}
	body
	{
		foo2();
	}
	failure
	{
		clean_what_foo3_did();
	}
	body
	{
		foo3();
	}
	failure
	{
		clean_what_foo4_did();
	}
	body
	{
		foo4();
	}
}
February 26, 2006
Re: DMD 0.148 - scope guard
I'm not at all disagreeing; but aside from language constructs like 
looping, which seem to be (in my experience) the easiest thing to get a 
good grasp on for newcomers, there aren't many ways in which (general) 
programming is non-linear.

I am saying this is strange; tasks, in many cases, are not linear. 
Obviously it must map well to something the computer understands, but 
that can often be handled by the compiler (as in this case.)

For example, academic programming typically teaches that multiple 
returns in a function are evil.  This is because it is mixing non linear 
programming (not returning always at the very end) with linear programming.

Scope exit and such cases are a good example of a clean way to resolve 
this problem without saying that "returns and continues are evil." What 
more, they are logical.

-[Unknown]


> Consider the for loop:
> 
>     for (expr; expr; expr)
> 
> The 3rd expression is executed at the *end* of the loop, yet it is placed at 
> the beginning. So there is precedent for the utility of putting code where 
> it conceptually belongs rather than where it is executed.
February 26, 2006
Re: DMD 0.148 - scope guard
Julio César Carrascal Urquijo wrote:
> James Dunne wrote:
> 
>> Kyle Furlong wrote:
>> bool LongFunction()
>> {
>>     bool  passed = false;
>>
>>     scope (State save = UIElement.GetState())
>>     catch { UIElement.SetState(Failed(save)); writef('0'); }
>>     pass { UIElement.SetState(save); writef('1'); }
>>     pass { passed = true; writef('2'); }
>>     body {
>>         ... lots of code ...
>>     }
>>     exit { writef('3'); }
>>
>>     return passed;
>> }
> 
> 
> This is the best I've seen but:
> 
> - Why two "pass" blocks?.

Just to demonstrate that the ordering of the blocks matters, and that 
multiple blocks can be defined.

> - About "exit" would be better before the "body" block and maybe replace 
> it with "finally" to save one more keyword.

I noted this was a matter of personal style where the body block goes, 
as it does not depend on the order of the other blocks.  Personally, I 
like seeing the exit block after the body, as it should flow naturally.

> 
> I'm also assuming that we could also do one liners without the braces:
> 
> 
> bool LongFunction()
> {
>     bool  passed = false;
> 
>     scope State save = UIElement.GetState();
>     catch UIElement.SetState(Failed(save));
>     pass UIElement.SetState(save);
>     finally writef('3');
>     body {
>         ... lots of code ...
>     }
> 
>     return passed;
>  }

Sure, why not?

-- 
Regards,
James Dunne
February 26, 2006
Re: DMD 0.148 - scope guard
Kyle Furlong wrote:
> Dawid Ciężarkiewicz wrote:
>> Kyle Furlong wrote:
>>> for any scope:
>>>
>>> in
>>> {
>>> }
>>> out
>>> {
>>> }
>>> failure
>>> {
>>> }
>>> success
>>> {
>>> }
>>> body
>>> {
>>> }
>>>
>>> Pros: fits into and expands current language structures
>>> Cons: still a bit verbose as compared to current syntax
>>
>> I'm sorry to say but IMO this miss the whole point.
>>
>> Current keywords: in, out, body are place-insensitive . They are just 
>> blocks
>> that may be moved relatively for themselves and this will not change
>> anything. The whole point in "scope guarding" is that expression you type
>> _registers_ some piece of code (to be called in some conditions) in place
>> where it appears.
>>
>> You gain nothing with such syntax. If you want to deal with whole 
>> scope then
>> you can use finally and catch blocks.
>>
>> Examples:
>>
>> Current syntax:
>> void LongFunction()
>> {
>>     foo1();
>>     on_scope_failure clean_what_foo1_did();
>>
>>     foo2();
>>     on_scope_failure clean_what_foo2_did();
>>
>>     foo3();
>>     on_scope_failure clean_what_foo3_did();
>>
>>     foo4();
>> }
>>
>>
>> What I understand from your proposal:
>> void LongFunction()
>> {
>>     foo1();
>>     failure {
>>         on_scope_failure clean_what_foo1_did();
>>     }
>>     body {
>>
>>        foo2();
>>
>>        failure {
>>            on_scope_failure clean_what_foo2_did();
>>        }
>>        body {
>>             foo3();
>>                    failure {
>>                on_scope_failure clean_what_foo3_did();
>>             }
>>             body {
>>                 foo4();
>>             }
>>        }
>>     }
>>
>> }
>>
>> If you think about:
>> void LongFunction()
>> {
>>     foo1();
>>     failure{
>>       clean_what_foo1_did();
>>     }
>>
>>     foo2();
>>     failure {
>>      clean_what_foo2_did();
>>     }
>>         foo3();
>>     failure {
>>      on_scope_failure clean_what_foo3_did();
>>     }
>>
>>     foo4();
>> }
>>
>> Then concept is OK, but you can no longer say this "will fit and expand
>> current syntax", because it's used totally diffrent from  body {} in 
>> {} out
>> {} blocks.
> 
> That is definitely *NOT* what I was saying *AT ALL*. Obviously that is a 
> terrible syntax.
> 
> Here's what it would look like:
> 
> Current syntax:
> void LongFunction()
> {
>     foo1();
>     on_scope_failure clean_what_foo1_did();
> 
>     foo2();
>     on_scope_failure clean_what_foo2_did();
> 
>     foo3();
>     on_scope_failure clean_what_foo3_did();
> 
>     foo4();
> }
> 
> 
> What I understand from your proposal: (how it should have been)
> void LongFunction()
> failure
> {
>     clean_what_foo1_did();
>     clean_what_foo2_did();
>     clean_what_foo3_did();
>     clean_what_foo4_did();
> }
> body
> {
>     foo1();
>     foo2();
>     foo3();
>     foo4();
> }
> 
> I think you missed the point that each function call does not introduce 
> a new scope. Now, its true that if you wanted the operations foox() to 
> each be in its own scope, then it would look like this:
> 
> void LongFunction()
> {
>     failure
>     {
>         clean_what_foo1_did();
>     }
>     body
>     {
>         foo1();
>     }
>     failure
>     {
>         clean_what_foo2_did();
>     }
>     body
>     {
>         foo2();
>     }
>     failure
>     {
>         clean_what_foo3_did();
>     }
>     body
>     {
>         foo3();
>     }
>     failure
>     {
>         clean_what_foo4_did();
>     }
>     body
>     {
>         foo4();
>     }
> }

David has brought some things to my attention on IRC that invalidate this syntax, sorry for the clutter.
February 26, 2006
Re: DMD 0.148 - scope guard
Dawid Ciężarkiewicz wrote:
> James Dunne wrote:
> 
>>I don't like the words 'success' and 'failure' used all over the place.
> 
> 
> Yes. I have to agree they are little missused. Changing them to "pass" as
> success, "fail"/"catch" as "failure" and "default" as exit is good idea.
> 

Yeah, after posting I realized this was the best idea out of my post. 
Really, there's no *nice* way of fixing the syntax problem.  You have a 
fixed statement keyword - nobody will like what you name it; or you have 
the curly-brace blocks nesting - which looks busy.

> 
>>void LongFunction()
>>{
>>     scope (State save = UIElement.GetState())
>>     catch {
>>         UIElement.SetState(Failed(save));
>>     }
>>     pass {
>>         UIElement.SetState(save);
>>     }
>>     body {
>>         ... lots of code ...
>>     }
>>}
>>
>>This way, it looks like a function contract, except it applies to any
>>scope.  The catch block is reused but only for purposes of noting that
>>there was an exception caught.  The pass block is quite simply the
>>'else' to the catch block, and should happen only if no exceptions were
>>caught.  Furthermore, there could be a general 'exit' block.
>>
>>One could add as many of these blocks as necessary, and the compiler
>>will guarantee to call them in order of definition, much like the
>>original solution but not requiring one to think backwards.
>>
>>bool LongFunction()
>>{
>>     bool  passed = false;
>>
>>     scope (State save = UIElement.GetState())
>>     catch { UIElement.SetState(Failed(save)); writef('0'); }
>>     pass { UIElement.SetState(save); writef('1'); }
>>     pass { passed = true; writef('2'); }
>>     body {
>>         ... lots of code ...
>>     }
>>     exit { writef('3'); }
>>
>>     return passed;
>>}
>>
> 
> 
> IMHO now it looks overcomplicated. You are explicitly declaring new scope
> using body {} . This is for what whole idea was invented - to prevent it.
> 
> Using "catch" keyword is missleading. Keyword scope (expr.) works now as try
> { expr. } . And I have to say that I needed to read this code few times to
> understand it. It looks for me as reinventing try { } catch { } finally {}
> way of handling exceptions. Sorry, but I don't like it.

Reusing the catch keyword was not intended to be misleading, but instead 
to keep other whining about introducing new keywords.  Though, 
technically there's no reason for any keyword to actually be a reserved 
word in the first place... ;)

Yes, it does somewhat analogue try/catch/finally, but it is more 
explicit in its meaning.

Thanks for your opinion.

-- 
Regards,
James Dunne
February 26, 2006
Re: DMD 0.148 - scope guard
On Sun, 26 Feb 2006 12:20:58 -0800, Kyle Furlong <kylefurlong@gmail.com>  
wrote:
> What I understand from your proposal: (how it should have been)
> void LongFunction()
> failure
> {
> 	clean_what_foo1_did();
> 	clean_what_foo2_did();
> 	clean_what_foo3_did();
> 	clean_what_foo4_did();
> }
> body
> {
> 	foo1();
> 	foo2();
> 	foo3();
> 	foo4();
> }

In the above:
 - is clean_what_foo1_did only called if foo1() has returned successfully  
and foo2,foo3,or foo4 failed?
 - is clean_what_foo2_did only called if foo2() has returned successfully  
and foo3,or foo4 failed?
 - is clean_what_foo3_did only called if foo3() has returned successfully  
and foo4 failed?

(there is actually no point to clean_what_foo4_did)

Because, this is an important part of the scope guard feature, each  
on_scope_failure statement registers something which is then called on  
failure of the scope in which it is registered. The order in which they  
are registered WRT the other code is important.

Regan
1 2 3 4 5 6 7 8 9
Top | Discussion index | About this forum | D home