6

I want to emphasize I am looking for the actual way the ||= operator is expanded by the Ruby 1.9.3 interpreter, not how it appears to be expanded based on its behavior. What I'm really hoping for is someone who has grokked the actual interpreter source, a task which I sadly am probably not up to. The only resource I have found that appears to examine this question is out of date: "A short-circuit (||=) edge case".

The resource I mentioned above seems to suggest that the "official" expansion of x ||= y to x = x || y was either inaccurate or buggy in interpreter versions prior to 1.9. In any case, the edge case indicated seems to have been smoothed out. The resource above claims that x || x = y or x or x = y are "more accurate". Neither of those, however, is correct, because they don't work when x is a previously undeclared global variable:

[11:04:18][****@asha:~]$ irb
1.9.3-p194 :001 > a || a = 3
    NameError: undefined local variable or method `a' for main:Object
1.9.3-p194 :002 > b or b = 3
    NameError: undefined local variable or method `b' for main:Object
1.9.3-p194 :003 > c = c || 3
    => 3 

So in 1.9.3, at least, the x = x || y expansion appears to be correct, as far as these examples are concerned. However, to reiterate my original point, I would really like to see some truly authoritative source resolve this question, well, authoritatively rather than anecdotally as I (and others) have done.

the Tin Man
  • 158,662
  • 42
  • 215
  • 303
cbmanica
  • 3,502
  • 7
  • 36
  • 54
  • possible duplicate of [Is the ruby operator ||= intelligent?](http://stackoverflow.com/questions/2989862/is-the-ruby-operator-intelligent) – jamessan Sep 06 '12 at 12:54

2 Answers2

3
x ||= y

is a shorthand form for

x || x = y

If x is not nil and x is not false, the assignation will have place because of the short-circuit evaluation of the || operator.

luis24fps
  • 31
  • 1
2

EDIT: This post is about the spec, read the comments to get the somewhat less ideal "implementation story"


The Ruby draft spec (PDF) section 11.4.2.3.2 defines it fairly specifically (even if fairly hard to interpret); let's do a (theoretically somewhat loose) example with c ||= 3;

a) Evaluate the variable as a variable reference (see 11.5.4). Let V be the resulting value.

V is set to the value of c

b) Evaluate the operator-expression or the method-invocation-without-parentheses. Let W be the resulting value.

W is set to 3.

c) Let OP be the assignment-operator-name of the assignment-operator.

OP is set to ||

d) Let X be the operator-expression of the form V OP W.

X is set to c || 3.

e) Let I be the variable of the abbreviated-variable-assignment-expression or the abbreviated- variable-assignment-statement.

I is set to refer to c.

f) Evaluate a single-variable-assignment-expression (see 11.4.2.2.2) where its variable is I and the operator-expression is X.

c = c || 3 is evaluated.

g) The value of the abbreviated-variable-assignment is the resulting value of the evaluation.

The result of the assignment is 3.

In other words, the expansion c = c || 3 is (excluding bugs like in pre-1.9) correct.

Joachim Isaksson
  • 176,943
  • 25
  • 281
  • 294
  • (stupid time limit on edits) Cool, that's a lot more comforting than some dude's blog post. Some followup questions however: 1) To what degree does the 1.9.3 interpreter conform to this draft standard? (the word "draft" makes me slightly apprehensive) 2) If I read the section on global variable references correctly, a) poses no problems. However, 11.5.4.5 (class variables) seems to indicate that the ||= operator should not work with a class variable whose value has not yet been assigned - but it does, and so does the expansion. So I'm a bit confused on that point. – cbmanica Sep 05 '12 at 19:11
  • The spec doesn't specifically address `||=`. It was my understanding that that's a special case in MRI (and the other implementations, for compatibility). – echristopherson Sep 06 '12 at 01:30
  • 5
    @echristopherson: That's the problem. `||=` and `&&=` are special cases but they are not special-cased in the spec, which makes either the spec or every single Ruby implementation wrong. Since the spec was specifically written with the intent that all existing Ruby implementations should be automatically compliant without having to change, the bug is clearly in the spec. – Jörg W Mittag Sep 06 '12 at 01:48
  • 1
    Uh oh. Although I accepted the answer that correctly links to the standard, apparently the standard is wrong in this instance. http://stackoverflow.com/questions/2989862/is-the-ruby-operator-intelligent?lq=1 has a great counterexample to the `x = x || y` expansion theory, because demonstrably no assignment takes place if x is already "truthy". I also wish I had seen http://www.ruby-forum.com/topic/151660/ prior to asking this question, because then I probably wouldn't have added to an apparently massive list of posts on this topic. – cbmanica Sep 05 '12 at 23:43