"James F.Hranicky" <jfh / cise.ufl.edu> wrote 
> "Damon" <adamon / mailandnews.com> wrote:
> 
> > That is: Modifications to a block variable are visible outside of the
> > block only if explicitly specified using a 'with' clause.
> > 
> > To put it another way, variables are passed into the block "by value".
> > Only if specified using a 'with' clause are they passed in "by
> > reference".
> > If a variable is passed in "by value" then any changes made to that
> > variable are restricted to the block; thus we get shadowing for free.
> 
> Hey, I like it.
> 
> The brace version isn't quite as pretty, but it's still fine with me:
> 
>     (1..10).each { with{i} |i|
>  	a = i*i
>     }
> 
I'd actually meant for the 'with' clause to appear outside of the
block.  Perhaps that might make the brace version a little prettier?

a = 1
i = 2

(1..10).each with{i} { |i|
    a = i*i
}

p a  # -> 1
p i  # -> 10


> How about we extend it to block variables as well:
> 
>     (1..10).each { with{i,a,b} |i|
>  	a = i*i
> 	b = 10
>     }
> 
> "i" and "a" now reference outer variables, b is local.

I had also intended for block variables not specified in the 'with'
clause to always be local.  Thus in the above example, 'b' would not
be local to the block:

a = 1
i = 2
b = 3    # b defined outside of the block prior to executing the block

(1..10).each with{i,a,b} { |i|
    a = i*i
    b = 10
}

p a  # -> 100
p i  # -> 10
p b  # -> 10


a = 1
i = 2
        # b not defined outside of the block

(1..10).each with{i,a,b} { |i|
    a = i*i
    b = 10
}

# This should raise an error, because b is expected to be defined when
# the 'with' clause is run.


To make 'b' local to the block, simply leave it out of the 'with'
clause.
Thus:

a = 1
i = 2
b = 3    # b defined outside of the block prior to executing the block

(1..10).each with{i,a} { |i|
    a = i*i
    b = 10
}

p a  # -> 100
p i  # -> 10
p b  # -> 3

or:

a = 1
i = 2
        # b not defined outside of the block

(1..10).each with{i,a} { |i|
    a = i*i
    b = 10     # b defined only inside block, making it block local
}

p a  # -> 100
p i  # -> 10
p b  # error: undefined variable 'b'


> 
> Of course, we could always use a clause to force locality instead:
> 
>     (1..10).each { local{b} |i|
>  	a = i*i
> 	b = 10
>     }
> 
> I do like the idea of having block params and variables be localized/de-
> localized in the same fashion.
> 

Unfortunately, this looks a little too much like a variable
declaration for my taste.  The default behavior is to have all
variables in a block local to the block, unless specified with a
'with' clause.  The 'with' clause can be likened to an "accessor" for
variables that are private to the block.  If the variable is not
specified in the 'with' clause then it should remain private to the
block, or local to the block.

By adopting this rule, we can do away with variable declarations
altogether, using lexical scoping rules to determine a variable's
visiblity and 'with' clauses to determine if changes made to a
variable inside a block are propagated outside of the block.

To summarize:

1)  Variables in the outer body are "passed by reference" if specified
    in a 'with' clause, passed "by value" otherwise.  Thus changes to
    the variable are propagated back to the outer body if the variable
    is specified in the 'with' clause.


2)  A variable is local to the block, if it is defined in the block
    and does not appear in the 'with' clause of the block.

3)  Changes to a variable defined in the outer body are local to the
    block if the variable does not appear in the 'with' clause of the
    block.

I suppose one can invert the functionality and have instead a
'without'
clause, which would mean the same thing as a 'local' clause.  Ie, now
we have variables passed in by reference, unless they are specified in
the 'without' clause, in which case they would be passed in by value.

a = 1
i = 2

(1..10).each without{i} { |i|
    a = i*i
}

p a  # -> 100
p i  # -> 2

a = 1
i = 2

or equivalently,

a = 1
i = 2

(1..10).each local{i} { |i|
    a = i*i
}

p a  # -> 100
p i  # -> 2

So in the absence of a 'without' or 'local' clause, we get pass by
reference

a = 1
i = 2

(1..10).each { |i|
    a = i*i
}

p a  # -> 100
p i  # -> 10

which conforms to current behavior.

However, I think it's better to use 'with' clauses rather than
'without' clauses, as it is safer to default to pass-by-value rather
than pass-by-reference.

Damon