My question was triggered by this discussion on SO, which did not lead to an answer that would really explain the issue. I am "rewriting" it here in a slightly differe
From the doc: https://ruby-doc.org/docs/ruby-doc-bundle/Manual/man-1.4/syntax.html#assign
Assignment expression are used to assign objects to the variables or such. Assignments sometimes work as declarations for local variables or class constants. The left hand side of the assignment expressions can be either:
variables variables `=' expression
On the right there is an expression, so the result of the expression is assigned to the variable.
So, you should look for expressions (*) before following the precedence.
1 && a = 3
are basically two "chained" expressions:
3
and 1 && 3
Maybe it is more readable as:
1 && a = 3 + 4
where the expressions are 3 + 4
and 1 && 7
, see:
1 && a = 3 + 4 #=> 7
1 && 7 #=> 7
res = 1 && a = 3 + 4
res #=> 7
(*) The precedence table also helps to find the expression (Find the precedence table in the linked doc at the Operator expressions paragraph):
What's above the =
in the table "forms" an expression to be assigned by =
, what's below does not.
For example:
1 + 3 and 2 + 4 #=> 4
a = 1 + 3 and b = 2 + 4 #=> 4
(a = 1 + 3) and (b = 2 + 4) #=> 4
a = (1 + 3 and b = 2 + 4) #=> 6
You can also check these examples respect to the precedence table:
1 && 3 #=> 3
1 && a = 3 #=> 3
a #=> 3
3 and 1 #=> 3
3 and b = 1 #=> 3
b #=> 1
2 ** c = 2 + 1 #=> 8
c #=> 3
d = 2 ** 3
d #=> 8
e = 3
e **= 2
e #=> 9
I think the understanding of 1 && (a = 3)
is, understandably, mislead.
a = false
b = 1
b && a = 3
b
=> 1
a
=> 3
Why is a
being assigned to in the &&
expression when a
is false
? Should the &&
expression not return when encountering a false
value? Spoiler, it does return
!
Taking a step back, we think of the purpose of the &&
operator to control the flow of logic. Our disposition to the statement
1 && a = 3
is to assume the entire statement is returned if a
is nil
or false
. Well no, the interpreter is evaluating like so:
(1 && a) = 3
The interpreter does not raise a
if it is nil
or false
nor does it return the left side if a
is nil
or false
a = nil
1 && a
=> nil # a was returned
The interpreter returns the variable, this is why the original statement can be read:
a = 3
due to 1 && a
returning a
which is a variable that can be assigned to by the =
operand on the second half of the statement.
TLDR
In your origin example: 1
is neither nil
nor false
so the variable a
is returned in (1 && a)
which is subsequently assigned in a = 3
Probably because the other interpretation does not work:
irb(main):003:0> (1 && a) = 3
Traceback (most recent call last):
3: from /home/w/.rbenv/versions/2.7/bin/irb:23:in `<main>'
2: from /home/w/.rbenv/versions/2.7/bin/irb:23:in `load'
1: from /home/w/.rbenv/versions/2.7.1/lib/ruby/gems/2.7.0/gems/irb-1.2.3/exe/irb:11:in `<top (required)>'
SyntaxError ((irb):3: syntax error, unexpected '=', expecting `end')
(1 && a) = 3
^
So, perhaps Ruby parenthesizes 1 && a = 3
in the only way that is legally interpretable by the language.