Reputation: 7512
Coming from a JavaScript background I've become accustomed to being able to use JavaScript's dynamic scope to encapsulate values in a function. For example:
function Dog( firstname, lastname ) {
this.fullname = firstname + lastname
return {
say_name: function () {
return fullname;
}
}
}
Now in Ruby I'm not so sure something like this would work too well:
class Foo
attr_accessor :bar, :baz
def initialize bar, baz
@bar = bar
@baz = baz
end
def give_me_a_proc
return Proc.new { @bar + @baz }
end
end
Can anyone give a quick explanation of how scope works in Ruby? If I call the Proc returned from give_me_a_proc
, will it still have access to its define-time scope?
Also do the values become fixed once I define the proc or do any changes made in Foo
get carried down to the Proc even after it has been defined?
Upvotes: 4
Views: 1169
Reputation: 21791
Ruby has a method to get closure from object, it suits very nice to your example:
class Foo
# reopen Foo class
def get_sum
@bar + @baz
end
end
m = Foo.new(5,20).method(:get_sum)
m.call #=> 25
The m
is the Method
object and it acts as a closure in Foo
’s instance, so instance variables and the value of self
remain available.
Upvotes: 1
Reputation: 35783
Ruby procs and lambdas are closures. When you do return Proc.new { @bar + @baz }
you are really capturing self
, which is how instance variables are looked up. Ruby blocks are also closures. Ruby does let you change the variables and the changes will propagate up to the calling scope, assuming that the calling scope still exists:
@hi = 0
def get_hi()
lambda {@hi = 42}
end
get_hi.call()
@hi #=> 42
Note: Unless your proc has very lax argument requirements (it doesn't care if it gets any arguments, how many, sort of like C's int f(void)
), use lambda
instead of Proc.new
. lambda
checks to make sure that you get the right number of arguments.
Upvotes: 2
Reputation: 6076
In Ruby a proc is a closure. In Javascript, a function is a closure. The idea of a closure is that it "closes over" the environment in which it was defined. In Ruby the variables in the proc can still be changed, even by code living outside of the proc, and the new values will be reflected within the proc(see peakxu for demonstration). Not sure if Javascript closures work this way.
Upvotes: 3
Reputation: 6675
Yes, it will still have access to definition time scope. See documentation for Proc class.
Changes are carried down to the Proc post-definition. Results from irb run with your class.
> foo = Foo.new(1, 2)
=> #<Foo:0x007f9002167080 @bar=1, @baz=2>
> quux = foo.give_me_a_proc
=> #<Proc:0x007f900214f688@(irb):11>
> quux.call
=> 3
> foo.bar = 3
=> 3
> quux.call
=> 5
Upvotes: 4