banister banister - 1 year ago 45
Ruby Question

How to make block local variables the default in ruby 1.9?

Ruby 1.9 gives the ability to define variables that are just local to a block and do not close over variables of the same name in an outer scope:

x = 10
proc { |;x|
x = 20
x #=> 10

I would like to have this behaviour as default for some blocks I define - without having to use the |;x, y, z| syntax (note the semicolon).

I do not think Ruby allows this natively but is it possible to hack this functionality?

I have one solution currently but it's quite ugly as it requires checking to see which locals have changed at the end of a block and then reverting them to their values prior to the block. I do not mind if your solution requires specifying which variables are block-local at the start of the block i.e
scope(:x) { x = 20 }

Answer Source

The solution I am choosing is based on bobbywilson0's idea. Here is how it works:

x = 99
y = 98

scope { |x, y|
    x = 20
    y = 30

x #=> 99
y #=> 98 

This is useful as the variables used in the scope are created at the start of the scope and do not close over any variables defined outside it, they are also GC'd at the end of the scope.

Here is the implementation:

def scope(&block)
    num_required = block.arity >= 0 ? block.arity : ~block.arity
    yield *([nil] * num_required)

This solution also takes default values into account making it functionally equivalent to a let* in lisp.

scope { |x = 20, z = (x * 3)| 
    x #=> 20
    z #=> 60

I blogged on it here: