jastrachan | 31 Jan 21:13 2005
Picon

Re: thoughts on the closure/return/break/continue issue

BTW we might wanna ensure that there is no ambiguity between

     continue label
     continue expression

So we might wanna go with some way to disambiguate in the grammar. If 
in doubt, I'd rather us make continue-to-label more ugly as its gonna 
be less common, so how about

     continue x: 	// x is a label
     continue x 	// x is an expression

it'd be easy to catch 'continue x' being an invalid expression, but a 
valid label, so we could automatically warn users if they are cutting 
and pasting code from Java?

Thoughts?

On 31 Jan 2005, at 20:05, jastrachan@... wrote:
> Just a quick summary on the recent closure / return / break / continue 
> discussions. I've tried to focus on simple rules which will be easy to 
> understand from a Java developers view; yet do the kinds of things 
> that folks using closures/blocks do in other languages which have 
> exploited the concept of closures/blocks successfully in the past...
>
>
> I think its a good guideline that return, break, continue (with and 
> without labels) should work the same in a foo.each {} closure as they 
> do in a for loop. e.g. Chris's example...
>
> for (i in list) {
>   if (something) {
>     break
>   }
>   else if (whatnot) {
>     return i
>   }
> }
>
> could be written as
>
> list.each {|i|
>   if (something) {
>     break
>   }
>   else if (whatnot) {
>     return i
>   }
> }
>
> i.e. we should be able to cut and paste the same code from a for() 
> block to a .each {} closure block and it should work the same. We 
> shouldn't have to translate the grammar just because we chose to use a 
> method & closure block rather than using the for/while loop
>
>
> return rules
> ============
>
> So return, break, continue should work the same at any point in a 
> method body - whether it happens to be in a closure block or not.
>
> The basic rule on return is that it is mandatory at the end of any 
> non-void method body. Clearly there is no equivalent 'return' inside 
> the closure call as return only means, return from a method call (not 
> a closure invocation).
>
> For closures, the last expression in the closure is returned to the 
> invoker of the closure - or null if there is no expression or the 
> expression has no value.
>
> no dumb expression rule
> =======================
>
> We have a no-dumb-expression rule to catch bad expression & typos 
> (like line terminators in the wrong place)
>
> def foo() {
>   x = 5
>   +5	// dumb expression, probably didn't mean a line terminate here
>   return 2
> }
>
> no dumb expression rule in closures
> ===================================
> We allow the last expression inside a closure to be a dumb expression, 
> but previous ones cannot be.
>
> list.each { def y = it + 1; +5; y }  // +5 is a dumb expression
>
>
>
> terminating a closure invocation early
> ======================================
>
> So how do we terminate closure invocations, since return isn't 
> allowed? Normally in method bodies we can use return to stop (and if 
> necessary return a value) from some nested loop structure.
>
> Firstly here's an example of how we do it in for loops..
>
> for (i in list) {
>   if (something) {
>     continue
>   }
>   doSomething()
> }
>
> To do a kinda return from a closure invocation, really what we're 
> doing, if we consider the iteration example above - is to continue on 
> to the next iteration, stopping execution of the current block.
>
> So we should be able to do the same in .each()
>
> list.each {|i|
>   if (something) {
>     continue
>   }
>   doSomething()
> }
>
> Now when using for(), the block of statements return no value, so a 
> simple continue is enough.
>
> However if we are invoking a closure and using the value returned, 
> such as if we are adding up items in a list, then we should be able to 
> associate an expression with continue.
>
> e.g.
>
> list.sum {|i|
>   if (something) {
>     continue 5
>   }
>   doSomething()
> }
>
> e.g. if we wanted to sum the squared value of all of the even numbers 
> in a list...
>
> list.sum {|i|
>   if (i % 2 == 1) {
>     continue 0
>   }
>   i * i
> }
>
>
> Normally the continue grammar is
>
> continue [<label>]
>
> so we have extended this above as
>
> continue [label] | [<expression>]
>
> So 'continue' becomes the kinda 'return from a closure invocation' 
> statement - and 'return' remains the 'return from method body' 
> statement.
>
>
> But there is an ambiguity with the above continue syntax, if we have a 
> variable and label of the same name...
>
> label = 123
> foo.each {
>   if (something) {
>     // is this continue-to-label or with-expr
>     continue label
>   }
> }
> y = 44
> label: println "Hey"
>
> We could say that you are not allowed to have a label with the same 
> name as a local variable or field (i.e. that could be confused with an 
> identifier). So we could say that the above is a compile error, as 
> 'label' is ambiguous. i.e. a continue <identifier> statement can only 
> have one possible definition of the identifier; if there is a vanilla 
> name (field, local variable, parameter) with the same name as a label 
> then its a compile error.
>
> This seems reasonable - as its quite easy to check and enforce and 
> seems to match the 'do what I mean' rule.
>
> Another option is to make this difference explicit in the grammar via
>
> continue [<label>] | [<continueSeparator> <expression>]
>
> Inside a closure block, we may wish to 'continue to label' or 
> 'continue with expression' where <label> and <identifier> are 
> ambiguous - so lets differentiate in them in the grammar. (BTW would 
> we ever want to continue-to-label with an expression being passed? I 
> don't think so - can anyone think of a use case?)
>
> So how could we disambiguate between a label and expression. Here are 
> some examples...
> // continue to a label
>     continue x
>
> // continue with expression
>     continue | x
>     continue : x
>     continue (x)
>
>
> It might be simpler and easier to read if we could just use an 
> expression and figure out if its a label or not - and enforce that you 
> cannot continue to a vanilla name which is ambiguous (there is a label 
> and local variable, parameter or field with the same name)?
>
> I think this is my preferred solution as its cleaner for the common 
> case; so
>
>    continue <identifier>
>
> the identifier is assumed to be a label and if not, its assumed to be 
> an expression - but the compiler will generate a compile error/warning 
> if there is a label and a suitable vanilla name available (parameter, 
> local variable, field) which could conflict.
>
>
> The nice thing about this is that the continue statement then works 
> like the return statement...
>
> return [<expression>]
> continue [<expression>]
>
> The main difference being, the case of
>
> continue <identifier>
>
> will look for a label name and use that, otherwise it'll use a 
> variable/field/parameter expression (and perform the check that its 
> not ambiguous).
>
> James
> -------
> http://radio.weblogs.com/0112098/
>
>

James
-------
http://radio.weblogs.com/0112098/


Gmane