Странный смысл || и || = в Ruby (2.0, 1.9.3, jruby 1.7.4)

Рассмотрим следующий фрагмент irb из недавно запущенного сеанса:

irb:01> baz            # => NameError, baz is not defined
irb:02> baz || baz = 0 # => NameError, baz is not defined
irb:03> baz            # => nil

baz была переменной undefined, и, пытаясь ее оценить, она произвела a NameError. Однако каким-то образом после этой операции был определен baz и имеет значение nil. По-видимому, значение nil было присвоено переменной baz, хотя никто (явно) не просил ее об этом. Существует ли основной язык, почему это поведение желательно?

Каково правило, объясняющее это поведение и другие аналогично запутывающие конструкции, такие как:

irb:04> true if foo            # => NameError
irb:05> foo                    # => NameError; name still undefined
irb:06> foo = (true if foo)    # => nil
irb:07> foo                    # => nil; name defined as nil
irb:08> true || i = 0 || j = 2 # => i and j are nil; || appears nonlazy
irb:09> raise || quux = 1      # => RuntimeError, quux is nil

Ответ 1

Я не знаю, желательно ли это, но это происходит из того, как Ruby анализирует код. Всякий раз, когда у вас есть кусок кода, который назначает локальную переменную, этой локальной переменной присваивается nil, даже если этот фрагмент кода не оценивается. В вашей строке кода 2:

baz || baz = 0

первая baz возвратила ошибку, потому что такая переменная не была назначена. Следовательно, присваивание baz = 0, которое следует за ним, не оценивалось, но тем не менее оно было проанализировано, поэтому в контексте для этого была создана локальная переменная baz и инициализируется на nil.

С вашим вторым фрагментом кода foo не назначается во время true if foo и foo. После этого foo = (true if foo) имеет назначение foo, поэтому, несмотря на то, что (true if foo) оценивается до присвоения foo, ошибка не возникает в этой строке.