Official expansion of ||= conditional assignment operator - ruby

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.

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.

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.

Related

Why symbol is not equal to symbol

I did:
x = :foo, y = %q{foo}.to_sym, x.==(y)
# => [:foo, :foo, false]
I am wondering why :foo is not equal to :foo.
Parallel assignment works differently in Ruby, and if you want multiple statements on one line, you need to separate them with semicolons (not commas).
The statement you executed:
x = :foo, y = %q{foo}.to_sym, x == y
Is equivalent to:
x = [:foo, y = %q{foo}.to_sym, x == y]
N.B. x is not yet defined when the right-hand side of the expression is evaluated, so the last term is effectively comparing nil and :foo. The y assignment also happens while the right-hand side of the expression is evaluating, and the result gets included in the array literal being assigned to x.
Here's what you meant to do:
x, y = :foo, %q{foo}.to_sym; x == y # => true
Just an aside, x.==(y) works because :== is technically a method, but in idiomatic Ruby you just write x == y. The interpreter knows what you mean.
Nice answer, but there is a logical leap from "x is not yet defined" to "the last term is effectively comparing nil and :foo.", in particular, why an undefined variable is evaluated as nil is unexplained.
This has to do with how variable hoisting is implemented in Ruby. It's an obnoxious feature of JavaScript too.

Understanding precedence of assignment and logical operator in Ruby

I can't understand precedence of Ruby operators in a following example:
x = 1 && y = 2
Since && has higher precedence than =, my understanding is that similarly to + and * operators:
1 + 2 * 3 + 4
which is resolved as
1 + (2 * 3) + 4
it should be equal to:
x = (1 && y) = 2
However, all Ruby sources (including internal syntax parser Ripper) parse this as
x = (1 && (y = 2))
Why?
EDIT [08.01.2016]
Let's focus on a subexpression: 1 && y = 2
According to precedence rules, we should try to parse it as:
(1 && y) = 2
which doesn't make sense because = requires a specific LHS (variable, constant, [] array item etc). But since (1 && y) is a correct expression, how should the parser deal with it?
I've tried consulting with Ruby's parse.y, but it's so spaghetti-like that I can't understand specific rules for assignment.
Simple. Ruby only interprets it in a way that makes sense. = is assignment. In your expectation:
x = (1 && y) = 2
it does not make sense to assign something to 1 && y. You can only assign something to a variable or a constant.
And note that the purpose of the precedence rule is to disambiguate an otherwise ambiguous expression. If one way to interpret it does not make sense, then there is no need for disambiguation, and hence the precedence rule would not be in effect.
My understanding is that in the case of
x = 1 && y = 2
The logical AND is parsed first. The AND then is forced to evaluate its left side and its right side. In the evaluation the left side the first assignment occurs, and then in the evaluation of the right side the second does the same. It is for this reason that in the case of:
x = false && y = 2
"y" will never be assigned. The AND is hit, forcing the x assignment to evaluate, but never bothering to run the y assignment, as it is unnecessary for the AND to fulfill its purpose.
In other words, the parser is, again, simply to my understanding, smart enough to recognize that in running the AND the equation is split into a left and right side (equations), which are in turn evaluated according to the order of operation.

What is the := operator?

In some programming languages, I see (ex.):
x := y
What is this := operator generally called and what does it do?
In all languages that support an operator := it means assignment.
In languages that support an operator :=, the = operator usually means an equality comparison.
In languages where = means assignment, == is typically used for equality comparison.
does := mean =?
I can't recall any languages where := means the same as =.
In MySQL := and = are both used for assignment, however they are not interchangeable and selecting the correct one depends on the context. To make matters more confusing the = operator is also used for comparison. The interpretation of = as either assignment or comparison also depends on context.
This is a new operator that is coming to Python 3.8 and actually had a role in BDFL Guido van Rossum's early retirement.
Formally, the operator allows what's called an "assignment expression". Informally, the operator is referred to as the "walrus operator".
It allows assignment while also evaluating an expression.
So this:
env_base = os.environ.get("PYTHONUSERBASE", None)
if env_base:
return env_base
can be shortened to:
if env_base := os.environ.get("PYTHONUSERBASE", None):
return env_base
https://www.python.org/dev/peps/pep-0572/#examples-from-the-python-standard-library
I usually see it more in pseudocode where it means an assignment. Thus x := y means 'set the value of x to the value of y' whereas x = y means 'does the value of x equal the value of y?'
The symbol is called "becomes" and was introduced with IAL (later called Algol 58) and Algol 60. It is the symbol for assigning a value to a variable. One reads x := y; as "x becomes y".
Using ":=" rather than "=" for assignment is mathematical fastidiousness; to such a viewpoint, "x = x + 1" is nonsensical. Other contemporary languages might have used a left arrow for assignment, but that was not common (as a single character) in many character sets.
Algol 68 further distinguished identification and assignment; INT the answer = 42; says that "the answer" is declared identically equal to 42 (i.e., is a constant value). In INT the answer := 42; "the answer" is declared as a variable and is initially assigned the value 42.
There are other assigning symbols, like +:=, pronounced plus-and-becomes; x +:= y adds y to the current value of x, storing the result in x.
(Spaces have no significance, so can be inserted "into" identifiers rather than having to mess with underscores)
A lot of languages use common operators. Generally the = is reserved for variable assignment and should not be viewed in a mathematical context if it is alone. Equality in some languages like Java and Bash is tested though ==
PL/I has (had?) both = and :=. = is used for both assignment and comparison -- the compiler tries to figure out which you meant based on context. When/if it decides to do comparison where you really meant assignment, you can use := to force assignment.
For example, consider x=y=0; In C (for one example) this would mean "assign 0 to y, then the result of that (also 0) to x."
In PL/I, it means compare y to 0, and then assign the Boolean result of that comparison to x (i.e., equivalent to x = y == 0; in something like C). If you (being sane, unlike the designers of PL/I) intended that to mean "assign 0 to x and y", you'd use x = y := 0; (or x := y := 0;).
:= it means "set equal to"
An assignment with syntax
v := expr
sets the value of variable «v» to the value obtained from expression «expr».
Example:
X := B Sets the Definition of X to the value of B

"Ruby-esque" if else conditional statements

I'm currently reading The Ruby Programming Language, and I am not sure how to read Ruby-esque if else statements properly. Can you help me write the ruby code below in the second code block in regular if-else statements like this?
if some_condition
return x
else
return y
end
So the ruby codes I am unsure of are these.
minimum = if x < y then x else y end
max = x > y ? x : y
Thank you!
Both of the forms you seem to be having difficulty with make use of an idea Ruby takes from the Functional Programming paradigm: namely, Everything is an expression, and therefore returns a value. This is even true for conditional statements, an idea that languages like Java don't really support (for example:
public boolean test() {
boolean x = if (1 > 2 ) { false; } else { true; };
return x;
}
simply isn't syntactically valid).
You can see this in a Ruby terminal:
will_be_assigned_nil = false if (1 > 2) # => nil
will_be_assigned_nil # => nil
So, to your question.
The first one can be rewritten like this:
if x < y
mininum = x
else
minimum = y
end
The second is like the ternary operator in other languages, and is equivalent to:
if x > y
max = x
else
max = y
end
It's helpful to remember the roots & heritage of languages when trying to understand their constructs. Ruby shares the "More Than One Way To Do It" philosophy with Perl, and idiomatic Ruby code often has a high emphasis on elegance.
The "post-expression"-style conditionals are a good example of this. If I have guard expressions at the start of my methods, it's not uncommon for me to write:
raise "Pre-condition x not met" unless x # (or "if !x" , preference thing)
raise "Pre-condition y not met" unless y # etc., etc.
instead of
if !x
raise "Pre-condition x not met"
end
if !y
raise "Pre-condition y not met"
end

What does ||= mean in Ruby? [duplicate]

This question already has answers here:
Closed 12 years ago.
Possible Duplicate:
What does ||= mean in Ruby?
What does ||= mean in Ruby?
It's mainly used as a shortform for initializing a variable to a certain value, if it is not yet set.
Think about the statement as returning x || (x = y). If x has a value (other than false), only the left side of the || will be evalutated (since || short-circuts), and x will be not be reassigned. However, if x is false or nil, the right side will be evaluated, which will set x to y, and y will be returned (the result of an assignment statement is the right-hand side).
See http://dablog.rubypal.com/2008/3/25/a-short-circuit-edge-case for more discussion.
x ||= y is often used instead of x = y if x == nil
The idea is the same as with other similar operators (+=, *=, etc):
a ||= b is a = a || b
And this trick is not limited to Ruby only: it goes through many languages with C roots.
edit to repel downvoters.
See one of Jörg's links for more accurate approximation, for example this one.
This is exactly why I don't like Ruby: nothing's ever what it seems.

Resources