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 def
fed 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 def
fed 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 ofg
is not reflected inv
onceg
returns. Is there a way I can make my changes tov
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
Start or Ensure That Delayed Job Runs When an Application/Server Restarts
Rails 3, Check CSV File Encoding Before Import
Refactoring a Large Routes.Rb File in Rails 4
Rails Implementation of Search with Autocomplete
Strictly Convert String to Integer (Or Nil)
How to Use Ruby " Case ... When " with Inequalities
Write and Read a File with Utf-8 Encoding
Rake Assets:Precompile Gets Killed When There Is a Console Session Open in Production
In Ruby/Rails, How to Encode/Escape Special Characters in Urls
Understanding Method_Added for Class Methods
Passenger Installation with Nginx Fails
Ruby/Rails Image Processing Libraries
Checking Whether the C Compiler Works... No
Why Doesn't This Work If in Ruby Everything Is an Object
How to Load Some Activerecord Models from a Yaml File and Save Them to the Db