Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

how to use truly local variables in ruby proc/lambda

Tags:

ruby

Beginner Ruby question. What is the simplest way to change this code, but leaving the block completely intact, that eliminates the side effect?

x = lambda { |v| x = 2 ; v}
x.call(3)
#=> 3
x
#=> 2

This is the simplest example I could contrive to illustrate my issue, so "remove the assignment" or "don't assign the Proc to x" is not what I'm looking for.

I want to set local variables in a Proc (or lambda) that can be assigned without affecting the original enclosing scope. I could dynamically create a class or module to wrap the block, but that seems overkill for such a basic thing.

Equivalent Python to what I'm trying to do:

def x(v):
  x = 2  # this is a local variable, what a concept
  return v
like image 661
wberry Avatar asked May 22 '13 18:05

wberry


2 Answers

Sometimes it is the desired behavior:

total = 0
(1..10).each{|x| total += x}
puts total

But sometimes it's accidental and you don't want to mess with an outside variable which happens to have the same name. In that case, follow the list of parameters with a semicolon and a list of the block-local variables:

x = lambda{|v; x| x = 2; v}
p x.call(3) #3
p x #<Proc:[email protected]:2 (lambda)>
like image 184
steenslag Avatar answered Sep 28 '22 03:09

steenslag


The reason for this is that the lambda is bound to its defining scope (NOT its calling scope), and is a full closure, which, among other things, includes the local variable x. What you really want here is an unbound proc to pass around and call without any particular binding. This isn't something that Ruby does very easily at all, by design (it's possible with eval, but that's less of a block and more just of a string statement). Procs, lambdas, and blocks are all bound to their defining scope. Lexical scope is only established on classes, modules, and methods; not on blocks/procs/lambdas/anything else.

It's worth noting that Python doesn't even permit assignment in lambdas in the first place.

like image 45
Chris Heald Avatar answered Sep 28 '22 04:09

Chris Heald