Last week I wrote a post about the differences between special blocks of code called Proc and lambda. This post will cover how these closures can get around scope gates and pass around variables defined that were defined outside of the current scope. To remind ourselves how scopes work in ruby, here’s an example

outer_scope = 'this is defined outside of scope'

def some_method
  inside_scope = 'this is from inside the scope'
  puts inside_scope
  puts outer_scope
end

some_method
# this is from inside the scope
# => NameError: undefined local variable or method 'outer_scope' for main:Object

Now check out how we can pass the variable outer_scope with the yield keyword

outer_scope = 'this is defined outside of scope'

def some_method
  inside_scope = 'this is from inside the scope'
  yield inside_scope
  puts 'back to the method scope'
end

some_method { |foobar| puts foobar; puts outer_scope }
# this is from inside the scope
# this is defined outside of scope
# back to the method scope
# => nil

As we can see, the method #some_method is passed a block kind of like an argument { |foobar| puts foobar; puts outer_scope } which also passes in outer_scope from beyond the scope of the method.

Now let’s try using a proc

outer_scope = 'this is defined outside of scope'

def some_method(some_var)
  inside_scope = 'this is from inside the scope'
  proc { some_var + ' and ' + inside_scope } if some_var
end

new_proc = some_method(outer_scope) # We can assign our block to a variable!
new_proc.class # gets the class of the new_proc object
outer_scope = 'something else' # We've changed the value of outer_scope after we've defined new_proc
# => Proc
new_proc.call # the proc defined by new_proc is executed by call
# => "this is defined outside of scope and this is from inside the scope"

Here we’ve used a proc to combine the strings from inner_scope and outer_scope which was passed in when we defined new_proc and available to the method scope as some_var.

Proc and lambda are pretty similar (except for the differences discussed in my previous post), and we can change our method to use a lambda instead by

def some_method(some_var)
  inside_scope = 'this is from inside the scope'
  lambda { some_var + ' and ' + inside_scope } if some_var
end

lamb = some_method(outer_scope)
lamb.class # => Proc
lamb # => #<Proc:0x00...@(irb):4 (lambda)>

Thanks for reading, and stay tuned in for more posts!