10

Consider the following irb snippet from a freshly-started session:

irb:01> baz            # => NameError, baz is not defined
irb:02> baz || baz = 0 # => NameError, baz is not defined
irb:03> baz            # => nil

baz was an undefined variable and trying to evaluate it produced a NameError. Yet, somehow, after this operation, baz was defined, and has a value of nil. Seemingly, the value nil was assigned to the variable baz even though no one (explicitly) asked for it to be. Is there an underlying language reason why this behavior is desirable?

What is the rule that explains this behavior and other similarly confusing constructs, such as these:

irb:04> true if foo            # => NameError
irb:05> foo                    # => NameError; name still undefined
irb:06> foo = (true if foo)    # => nil
irb:07> foo                    # => nil; name defined as nil
irb:08> true || i = 0 || j = 2 # => i and j are nil; || appears nonlazy
irb:09> raise || quux = 1      # => RuntimeError, quux is nil
door_number_three
  • 5,843
  • 2
  • 16
  • 21
  • 3
    You're not actually using `||=` in any of your examples, question title is a bit misleading – nzifnab Oct 25 '13 at 16:54
  • Even more strangely: `>> spam # => NameError; >> spam ||= "eggs" # => "eggs"; >> spam # => "eggs"`. Inconsistent. – Reinstate Monica -- notmaynard Oct 25 '13 at 16:56
  • possible duplicate of [Confusion with the assignment operation inside the fallacy \`if\` block](http://stackoverflow.com/questions/15183576/confusion-with-the-assignment-operation-inside-the-fallacy-if-block) – Arup Rakshit Oct 25 '13 at 18:27

1 Answers1

9

I don't know if it is desirable, but it comes from how Ruby parses the code. Whenever you have a piece of code that assigns a local variable, that local variable is assigned nil even if that piece of code is not evaluated. In your code line 2:

baz || baz = 0

the first baz returned an error because no such variable was assigned. Hence the assignment baz = 0 that follows it was not evaluated, but nevertheless it was parsed, so in the context to follow, a local variable baz was created, and is initialized to nil.

With your second code chunk, foo is not assigned during true if foo and foo. After that, foo = (true if foo) has an assignment to foo, so even though (true if foo) is evaluated before assigment of foo, an error is not raised in that line.

sawa
  • 165,429
  • 45
  • 277
  • 381
  • 1
    Have to delete my comment as you've fixed your answer ;-) Now it's correct, assignment to baz is **not** performed but whenever Ruby **parser** detects an assignment, it allocates a space for it and sets to `nil`. – David Unric Oct 25 '13 at 17:01
  • @DavidUnric You may have had something to say to my answer as it were earlier, but now, I don't think your comment adds any information beyond my answer. – sawa Oct 25 '13 at 17:07