1

The following ruby statement will result in x being "defined" (i.e. defined(x) returning "local-variable" even if it is undefined prior to execution of this code and even though the assignment is not performed:

x = 1 if false

Specifically, the x local variable will be set to nil. The behavior is similar for never-executed assignments subject to while false and until false clauses. You can verify this in either irb or running ruby on some code fragment.

My question is twofold:

  1. Is this behavior documented anywhere?
  2. Is there a rationale for this behavior documented anywhere?
Peter Alfvin
  • 28,599
  • 8
  • 68
  • 106
  • 1
    this concept is called hoisting & the answer is here -- http://stackoverflow.com/questions/12928050/why-does-ruby-seem-to-hoist-variable-declarations-from-inside-a-case-statement-e – jm0 Dec 06 '13 at 03:13
  • @jm0 Thanks. I just voted to close (FWIW, given 100k VTC queue ;-)) – Peter Alfvin Dec 06 '13 at 03:17
  • yes sir glad it helped. hoisting is commonly discussed in javascript & i read those docs all day lately :-/ – jm0 Dec 06 '13 at 03:19

1 Answers1

6

Here's a bit of an insight from the compiler. This is what Ruby 2.0.0 translates x = 1 if false; puts x; puts y into (before any code is executed):

== disasm: <RubyVM::InstructionSequence:<compiled>@<compiled>>==========
local table (size: 2, argc: 0 [opts: 0, rest: -1, post: 0, block: -1] s1)
[ 2] x          
0000 trace            1                                               (   1)
0002 jump             7
0004 putobject_OP_INT2FIX_O_1_C_ 
0005 setlocal_OP__WC__0 2
0007 trace            1
0009 putself          
0010 getlocal_OP__WC__0 2
0012 opt_send_simple  <callinfo!mid:puts, argc:1, FCALL|ARGS_SKIP>
0014 pop              
0015 trace            1
0017 putself          
0018 putself          
0019 opt_send_simple  <callinfo!mid:y, argc:0, FCALL|VCALL|ARGS_SKIP>
0021 opt_send_simple  <callinfo!mid:puts, argc:1, FCALL|ARGS_SKIP>
0023 leave          

You can see that the local table allocates its slot 2 to the local variable x. The assignment itself (putobject, setlocal 2) is skipped over by jump 7. puts x thus fetches the slot 2 and calls puts; however, since y is not recognised as a local variable either in this scope or a higher one, it is thought it might be a method, really, almost as if you had written puts(y()). At execution time, y cannot be resolved, and the error is raised.

Amadan
  • 191,408
  • 23
  • 240
  • 301
  • I asked a similar question, this seems to be the best explanation ever, @Amadan, how did you get this to display?? – bjhaid Dec 06 '13 at 04:01
  • @bjhaid: `puts RubyVM::InstructionSequence.compile('x = 1 if false; puts x; puts y').disasm` – Amadan Dec 06 '13 at 04:02