Assignment in Ruby - Block Assignment
For the second installment of Assignment in Ruby, I would like to explore block assignment. Block assignment is very similar to the simple scoped assignments I explored in my first post. To understand the differences, it is necessary to understand how block variables are scoped.
When a block uses a local variable, Ruby has to determine what is being used.
If there is an
If there is no
If there is no
The different scopes are each illustrated in the following code:
Written a little more nicely, the code under inspection is:
Note the 2nd to last line of the S expression
This can be a sticky situation and I know I missed it when I was learning Ruby. I only internalized how block scopes are handled when I saw it reflected in ParseTree and the AST. I still don't really understand why you would want to reuse local scoped variables inside a block but that is probably an indication of my weakness with closures. If you have any ideas, please let me know.
I hope you have enjoyed another brief forray into Assignment in Ruby. Join me next time as we explore multiple assignment and how it can simplify your life.
When a block uses a local variable, Ruby has to determine what is being used.
{ |x, y, z| puts x, y, z }
uses three local variables. Interestingly, these three variables could each potentially have a different scope.If there is an
x
in the current local scope, the variable x
used in the block will be that local variable, meaning the assignment will be an :lasgn
(local assignment).If there is no
y
in the current local scope but there is a y
in an enclosing block, the y
variable used will be the block scoped variable from the outer block and the assignment will be a :dasgn
(block assignment).If there is no
z
in the current local scope and there is no z
in any enclosing blocks, then a new z
will be created in the scope of the current block and the assignment will be a :dasgn_curr
(block local assignment).The different scopes are each illustrated in the following code:
$ echo "x = 123; foo() {|x| y = 456; bar() {|x,y,z| puts x, y, z}}" | parse_tree_show -f
[[:lasgn, :x, [:lit, 123]],
[:iter,
[:fcall, :foo],
[:lasgn, :x],
[:block,
[:dasgn_curr, :y],
[:dasgn_curr, :y, [:lit, 456]],
[:iter,
[:fcall, :bar],
[:masgn, [:array, [:lasgn, :x], [:dasgn, :y], [:dasgn_curr, :z]]],
[:fcall, :puts, [:array, [:lvar, :x], [:dvar, :y], [:dvar, :z]]]]]]]
Written a little more nicely, the code under inspection is:
x = 123
foo() { |x|
y = 456
bar() { |x,y,z|
puts x, y, z
}
}
Note the 2nd to last line of the S expression
[:masgn, [:array, [:lasgn, :x], [:dasgn, :y], [:dasgn_curr, :z]]]
. The first thing to notice is:masgn
. :masgn
stands for multiple assignment and will be covered in its own post. After the :masgn
you can see concrete examples of the local assignment to x
, the outer block assignment to y
and the current block assignment to z
.This can be a sticky situation and I know I missed it when I was learning Ruby. I only internalized how block scopes are handled when I saw it reflected in ParseTree and the AST. I still don't really understand why you would want to reuse local scoped variables inside a block but that is probably an indication of my weakness with closures. If you have any ideas, please let me know.
I hope you have enjoyed another brief forray into Assignment in Ruby. Join me next time as we explore multiple assignment and how it can simplify your life.