February 26, 2006
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
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
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
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
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
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
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
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
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
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 10 11 12 13 14 15
Top | Discussion index | About this forum | D home