Feature #4965


The problem of "print line if line = DATA.gets"

Added by yimutang (Joey Zhou) about 11 years ago. Updated over 10 years ago.

Target version:


Right now, I find the following code will raise an exception:

print line if line = DATA.gets

undefined local variable or method `line' for main:Object (NameError)

"print line while line = DATA.gets" also doesn't work, maybe "unless" "until" modifiers either.

It seems that the Ruby interpreter is too impatient, once seeing unassigned variable "line", it croaks.

However, the expression after "if" or "while" modifier should be evaluated first. Logically, I think the code above should work properly, there's no need to write "line = nil" before it.

Related issues 3 (0 open3 closed)

Is duplicate of Ruby master - Feature #1141: assignment of variable in "right" if statement failsRejected02/11/2009Actions
Has duplicate Ruby master - Feature #6224: Make variables in if/unless conditions available to conditional statementsRejectedmatz (Yukihiro Matsumoto)03/29/2012Actions
Has duplicate Ruby master - Feature #8600: Recognize variables declared in if modifier at end of lineRejected07/04/2013Actions
Actions #1

Updated by adgar (Michael Edgar) about 11 years ago

Some technical background: the current issue stems from how the parser must differentiate between local variables and method calls. Presently, local variables are created by the parser upon their introduction (assignment, rescue handlers, etc.), and they are available lexically after that in the given scope. This approach allows the parser to create the AST as each grammar rule is parsed. However, as you note, the if-guard and until-guard constructs, which are parsed after the guarded body, are executed before the guarded body. When not using begin...end with while-mod and until-mod, the condition is executed first as well.

Fixing this would involve post-processing in the parser, because the parser will discover the assignment after it has parsed reference and created the relevant AST node(s). One strategy is, when a guarded expression is parsed, to go back and examine the guarded body for local variable references that were instead considered method calls, replacing them with lvar references. It'd be a bit messy, but should work. It would break backwards compatibility, but not really in a meaningful way; it'd probably end up targeted for 2.0 though, regardless.

I'll whip up a patch implementing this today.

Updated by mame (Yusuke Endoh) over 10 years ago

  • Status changed from Open to Assigned
  • Assignee set to matz (Yukihiro Matsumoto)

Updated by duerst (Martin Dürst) over 10 years ago

Just for completeness, at today's developers' meeting in Akihabara, there were several people who were supportive of this proposal. Arguments given were that humans read code top-down, but not necessarily left-to-right character-by-character, and that if we would want to claim that Ruby works strictly top-down and per-line-left-to-right, then postfix if (and friends) would be impossible in the first place.

Updated by matz (Yukihiro Matsumoto) over 10 years ago

  • Status changed from Assigned to Rejected

The reason behind has been explained in #1141. If someone want to change the rule, he need to understand the rational first, then should try persuade me.


Updated by rosenfeld (Rodrigo Rosenfeld Rosas) over 10 years ago

Hi Matz, some people tried to with the argument that if you're reading a post-if/unless and they seem natural, using a variable assignment alongside with the post-if/unless would be just as natural.


Also available in: Atom PDF