Reputation: 2302
Here is my simple program to demonstrate the lazy evaluation concept.
class A
def x
y += 1
end
def y
@y ||= 0
end
end
A.new.x
But I got this result when running this program
NoMethodError: undefined method `+' for nil:NilClass
Am I doing something wrong?
When @y
is an array, thing works perfectly.
class A
def x
y << rand(10)
end
def y
@y ||= []
end
end
a = A.new
a.x
UPDATE
Finally I understand the problem. Ruby is pass-by-value. When I call method y
in the first example, Ruby doesn't care about the instance_variable, it copies and returns the value of @y
only.
In the second example, Ruby still copies and returns the value of @y
variable. But in this case, @y
is a pointer of the real array, and the copy of this pointer still points to the same array.
But why the error is undefined method + for nil
, I expect that the y + 1 which y equal to the value of @y (0). So why it returns nil
for y in this case?
Upvotes: 0
Views: 418
Reputation: 106882
It is important to understand what y += 1
really does. y += 1
is the equivalent to:
y = y + 1
That means you just told Ruby you want to assign y + 1
to the local variable y
. Because Ruby prefers to read from local variables over calling methods, it uses the current value of the local variabel y
and tries to add 1
. But the local variable y
is still nil
at this moment, therefore this operation raises a undefined method '+' for nil:NilClass
exception.
I guess you expected y += 1
to call the method y
, add 1
and write result back to @y
. To achieve this you have to change your code a bit to:
class A
attr_writer :y
def x
self.y += 1
end
def y
@y ||= 0
end
end
a = A.new
a.x
#=> 1
a.x
#=> 2
self.y
ensures that you read the method y
and do not create a local variable. Furthermore you need a setter method attr_writer
to be able to call y =
.
Why does your second example work out of the box? Because <<
isn't a shortcut that creates a local variable, therefore it receives the array from y
. And <<
shifts a value into that array in place without the need to call a setter method like y =
.
Interesting read in this context: What Ruby’s ||= (Double Pipe / Or Equals) Really Does
Upvotes: 5