How to Use Truly Local Variables in Ruby Proc/Lambda

how to use truly local variables in ruby proc/lambda

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:0x83f7570@test1.rb:2 (lambda)>

local variables scope in anonymous function objects in Ruby

When you create an anonymous function (a lambda or Proc), you give it a block, which is the body of the function, like so:

-> { this_is_the_function_body }
Proc.new { this_is_the_function_body }

A block retains all the local variables as they existed in the scope in which the block was created:

def my_lambda
text = 'foo bar baz'
-> { "text is: #{text}" }
end

l = my_lambda
text #=> #<NameError: undefined local variable or method `text' for main:Object>
l.inspect #=> "#<Proc:0x007f9863865a80@(pry):3 (lambda)>"
l.call #=> "foo bar baz"

As we see above, the local variable text is still available even though even the lambda object resides in, and is called in, a scope in which text doesn’t exist. This is even true if a local variable with the same name exists in the calling scope:

text = 'something else'
l.call #=> "foo bar baz"

This is called a closure. No matter where you call the function, you still have access to the scope in which it was created.

This doesn’t just apply to local variables, though, but instead the whole scope, as we can see here:

class C
def f; -> { g }; end
def g; 'foo'; end
end

C.new.f.call #=> "foo"

Where and when to use Lambda?

It's true, you don't need anonymous functions (or lambdas, or whatever you want to call them). But there are a lot of things you don't need. You don't need classes—just pass all the instance variables around to ordinary functions. Then

class Foo
attr_accessor :bar, :baz
def frob(x)
bar = baz*x
end
end

would become

def new_Foo(bar,baz)
[bar,baz]
end

def bar(foo)
foo[0]
end
# Other attribute accessors stripped for brevity's sake

def frob(foo,x)
foo[0] = foo[1]*x
end

Similarly, you don't need any loops except for loop...end with if and break. I could go on and on.1 But you want to program with classes in Ruby. You want to be able to use while loops, or maybe even array.each { |x| ... }, and you want to be able to use unless instead of if not.

Just like these features, anonymous functions are there to help you express things elegantly, concisely, and sensibly. Being able to write some_function(lambda { |x,y| x + f(y) }) is much nicer than having to write

def temp(x,y)
x + f(y)
end
some_function temp

It's much bulkier to have to break off the flow of code to write out a deffed function, which then has to be given a useless name, when it's just as clear to write the operation in-line. It's true that there's nowhere you must use a lambda, but there are lots of places I'd much rather use a lambda.

Ruby solves a lot of the lambda-using cases with blocks: all the functions like each, map, and open which can take a block as an argument are basically taking a special-cased anonymous function. array.map { |x| f(x) + g(x) } is the same as array.map(&lambda { |x| f(x) + g(x) }) (where the & just makes the lambda "special" in the same way that the bare block is). Again, you could write out a separate deffed function every time—but why would you want to?

Languages other than Ruby which support that style of programming don't have blocks, but often support a lighter-weight lambda syntax, such as Haskell's \x -> f x + g x, or C#'s x => f(x) + g(x);2. Any time I have a function which needs to take some abstract behavior, such as map, or each, or on_clicked, I'm going to be thankful for the ability to pass in a lambda instead of a named function, because it's just that much easier. Eventually, you stop thinking of them as somehow special—they're about as exciting as literal syntax for arrays instead of empty().append(1).append(2).append(3). Just another useful part of the language.


1: In the degenerate case, you really only need eight instructions: +-<>[].,. <> move an imaginary "pointer" along an array; +- increment and decrement the integer in the current cell; [] perform a loop-while-non-zero; and ., do input and output. In fact, you really only need just one instruction, such as subleq a b c (subtract a from b and jump to c if the result is less than or equal to zero).

2: I've never actually used C#, so if that syntax is wrong, feel free to correct it.

Can I create a proc in the context of itself?

A general approach that is typically used in DSLs is referred to as the Clean Room pattern - an object you build for the purpose of evaluating blocks of DSL code. It is used to restrict the DSL from accessing undesired methods, as well as to define the underlying data the DSL works on.

The approach looks something like this:

# Using struct for simplicity.
# The clean room can be a full-blown class.
first_clean_room = Struct.new(:foo).new(123)
second_clean_room = Struct.new(:foo).new(321)

prc = Proc.new do
foo
end

first_clean_room.instance_exec(&prc)
# => 123

second_clean_room.instance_exec(&prc)
# => 321

It appears that what you are looking for is to have the Proc object itself serve as both the block and the clean room. This is a bit unusual, since the block of code is what you typically want to have reused on different underlying data. I suggest you consider first whether the original pattern might be a better fit for your application.

Nevertheless, having the Proc object serve as the clean room can indeed be done, and the code looks very similar to the pattern above (the code also looks similar to the approach you posted in your answer):

prc = Proc.new do 
foo
end

other = prc.clone

# Define the attributes in each clean room

def prc.foo
123
end

def other.foo
321
end

prc.instance_exec(&prc)
# => 123

other.instance_exec(&other)
# => 321

You could also consider making the approach more convenient to run by creating a new class that inherits from Proc instead of overriding the native call method. It's not wrong per-se to override it, but you might need the flexibility to attach it to a different receiver, so this approach lets you have both:

class CleanRoomProc < Proc
def run(*args)
instance_exec(*args, &self)
end
end

code = CleanRoomProc.new do
foo
end

prc = code.clone
other = code.clone

def prc.foo
123
end

def other.foo
321
end

prc.run
# => 123

other.run
# => 321

And if you cannot use a new class for some reason, e.g. you are getting a Proc object from a gem, you could consider extending the Proc object using a module:

module SelfCleanRoom
def run(*args)
instance_exec(*args, &self)
end
end

code = Proc.new do
foo
end

code.extend(SelfCleanRoom)

prc = code.clone
other = code.clone

# ...

Capturing variables in Ruby methods

Ruby has script scope, module/class/method definition scope and block scope. Only blocks create nested scopes. So, you need to use a block to define your method. Thankfully, there is a method for defining methods that takes a block:

def f
v = 5
define_method :g do
v
end
g
end
f
# => 5

However, note that this does not do what you think it does (and neither does your original code). It does not define a method g nested in method f. Ruby doesn't have nested methods. Methods always belong to modules (classes are modules), they cannot belong to methods.

What this does is define a method f, which when you run it defines a method g and then calls that method.

Observe:

methods.include?(:g)
# => true

You have now defined a new top-level method (actually a private instance method of Object) called g, and you will define it over and over and over again, everytime f gets called.

What you probably want is a lambda:

def f
v = 5
g = -> { v }
g.()
end
f
# => 5

In a comment to another answer you wrote:

Okay, so I'm messing around with lambdas, and I noticed that anything I do to v inside of g is not reflected in v once g returns. Is there a way I can make my changes to v stick?

def f
v = 5
g = -> { v = 'Hello' }
g.()
v
end
f
# => 'Hello'

As you can see, the change to v does "stick" after g returns.

Within a Ruby method, should I create a proc or a method?

It is not clear what your specific use-case is, but I would definitely go for procs or lambdas. There is less overhead when defining a proc or lambda dynamically, they are passable, so if needed you could return them and they could be used outside the function.

Using "def" exposes the method as an instance method outside of the current method scope (so in the containing class, which could be Object in your case). This may or may not be with you want. If you want to use an anonymous function only available in the local scope, use a lambda.

Also Proc vs Lambda: I generally prefer to use lambdas since they behave a little more "predictable", meaning: as you would expect (check passed variables, and return just returns from the lambda, proc returns from the called scope). But from your example it is hard to deduce what would apply. I think the key-difference is: lambas are ment to be passed around, and thus behave a little more sanely. If this is not your use-case, use Proc :) (a write-up of the difference).



Related Topics



Leave a reply



Submit