Change multiple assignment in conditional from parse error to warning
There is currently an inconsistency between regular and multiple assignment in conditionals.
Regular assignment causes a warning, multiple assignment causes a parse error.
The historical reason for this is that in 1.8 multiple assignment would always return an Array, but since 1.9 it returns whatever the RHS evaluates to.
a, b = nil #=> nil a, b =  #=>  (but a and b are both nil) a, b = 1,2 #=> [1, 2]
Since multiple assignment behavior has changed, it makes sense to remove the (artificial) parse error for multiple assignments.
That makes it possible to test the return value of a method used for multiple assignment without having to use a temporary variable.
# CURRENTLY WORKING CODE: tmp = some_method_returning_array_or_nil a, b = tmp if tmp # method returned an array (possibly empty) else # method returned nil. end # PROPOSED WORKING CODE: if(a, b = some_method_returning_array_or_nil) # method returned an array (possibly empty) else # method returned nil end
(the parenthesis are needed due to LALR limitations, as discussed in #10450)
Attached is a patch that does the necessary change.
parse.y: massign in cond
- parse.y (assign_in_cond): allow multiple assignment in conditional expression. [Feature #10617]
#1 [ruby-core:67212] Updated by recursive-madman (Recursive Madman) about 3 years ago
I'm not sure if it's decent to push this - I understand 2.2.0 release is taking up a lot of time on the core devs side and holidays are also in progress in some parts of the world, so no hurry here.
Anyway, I feel I should ask:
- are there any known reasons not to accept this patch?
- does the issue need more clarification? #10450 has more information (I know that issue's discussion got a bit heated and confusing, which is why I opened this one to limit it to the actual change that was requested)
If there's anything more I should do to make this go through (maybe write a test or something?), I'm willing to do.
#2 [ruby-core:67251] Updated by duerst (Martin Dürst) about 3 years ago
Just in private:
Adding some tests to the patch is a good idea.
If there's no more action, I'd wait for the next call
for proposals for features in Ruby 2.3,
then produce a slide summarizing your proposal
(see e.g. Normalization.pdf on
Such calls normally occur somewhere in the middle
of the year.
#4 [ruby-core:73345] Updated by avit (Andrew Vit) almost 2 years ago
I think it's too confusing or ambiguous to allow multiple assignment in conditional.
It's very easy to just do the assignment on a previous line.
This could be confusing, especially if it comes from splat values:
a, b = [false, false]
This has been rejected before:
#5 [ruby-core:73347] Updated by bughit (bug hit) almost 2 years ago
Andrew Vit wrote:
I think it's too confusing
There is nothing intrinsically confusing about testing an expression that may be truthy or falsy. One would only be confused if one did not know that it could be either, in which case one should find out and so unconfuse oneself.
The restriction is not there to deal with confusion, it's historical cruft from the time when multiple assignment expression was always truthy, as Yusuke Endoh explained:
I'll tell you guys the rationale: multiple assignment always used to return an array.$ ./ruby -ve 'p((a, b = nil))' ruby 1.8.7 (2013-06-27 patchlevel 374) [x86_64-linux] [nil]
This behavior changed since 1.9.0, so the restriction is indeed meaningless now.
#6 [ruby-core:74082] Updated by mikecmpbll (Mike Campbell) almost 2 years ago
I find this really unintuitive and share some of 'bug hit's exasperation. It's clear that the return value, like everything, is either truthy or falsey, why treat this differently to everything else in Ruby?
It's particularly annoying because it prevents well known patterns from being used with tuples, for instance:
while (a = queue.pop(true) rescue nil) # do something end
I see that quite a lot for exhausting a queue. However, when you want to use the same pattern with tuples Ruby arbitrarily restricts you:
while (a, b = queue.pop(true) rescue nil) # do something end # SyntaxError: (irb):4: multiple assignment in conditional
The work-around with the throwaway variable is simple enough, but unintuitive. I understand from reading the other bug issue that this has historical relevance, but clearly this is no longer the case so I don't see any reason to object to it's removal.
#8 [ruby-core:83595] Updated by Eregon (Benoit Daloze) 3 months ago
This is now allowed, but produces a warning:
$ ruby -e 'if (a, b = 1, 2); puts "y"; end' -e:1: warning: found = in conditional, should be == y $ ruby -W0 -e 'if (a, b = 1, 2); puts "y"; end' y
Is that expected?
It seems to only happen with literals though:
$ ruby -e 'if (a, b = 1, 2); puts "y"; end' -e:1: warning: found = in conditional, should be == y $ ruby -e 'if (a, b = 1, 2.itself); puts "y"; end' y