0

I wanted to use the classical ||= re-assignment (cf Set Ruby variable if it is not already defined) with ActiveInteraction pretty much like in https://github.com/AaronLasseigne/active_interaction/issues/395

However by testing different syntaxes in ActiveInteraction I stumbled upon a much more peculiar issue that happens even in vanilly Ruby.

A non-executed line (blocked by a if false) can still have a major impact on the rest of the code:

class A
  attr_accessor :a

  def run
    (puts defined? a; a) if true
  end

  def run2
    (puts 'change a'; a = 0) if false
    puts defined? a
    a
  end

end

x = A.new
x.run  # "method"; nil
x.run2 # "local-variable"; nil
x.a = 5
x.run # "method"; 5
x.run2 # "local-variable"; nil

Can anyone explain if this is a bug or a feature? And if a feature: how come? It seems very odd.

EDIT: Thanks to the answer of @Sergio Tulentsev I managed to find that my question is pretty much a duplicate of Ruby instance method & conditional local variable assignment with same name with a different focus for the title name.

sawa
  • 165,429
  • 45
  • 277
  • 381
Ixio
  • 517
  • 6
  • 21

1 Answers1

3

[is this] a bug or a feature?

Neither. It's a... peculiarity. What happens is, when parser sees assignment to local variable in the code, it goes ahead and adds the name to the scope (starting from that line, possibly shadowing other names, like your method here). With default value of nil. If the actual assignment is then never executed, the new local variable is still in scope and still evaluates to nil.

This is documented in https://docs.ruby-lang.org/en/2.5.0/syntax/assignment_rdoc.html#label-Local+Variables+and+Methods.

Ixio
  • 517
  • 6
  • 21
Sergio Tulentsev
  • 226,338
  • 43
  • 373
  • 367
  • 1
    (one would think it'd be easy to find a good dup target for this...) – Sergio Tulentsev Mar 05 '19 at 23:56
  • It is a feature. Not sure why you claim it is not. – sawa Mar 06 '19 at 05:43
  • @sawa: I can't imagine someone _specifically designed_ this behaviour, because it was desirable. :) – Sergio Tulentsev Mar 06 '19 at 07:19
  • @SergioTulentsev: It is desirable to be able to call methods with an implicit receiver and without argument list. It is desirable to not require sigils for local variables. Therefore, it must be possible to distinguish between variables and method calls. It is desirable to be able to decide this statically (not because of the implementation effort but because of the cognitive overhead for the reader of the code). The only other solution I can think of that satisfies those constraints, would be to require all local variables to be explicitly declared. – Jörg W Mittag Mar 06 '19 at 09:47
  • @JörgWMittag: what I was talking about is the quirk of "local variables spring into scope when parses sees the assignment". That's what you meant by "decide this statically", yes? Alas, this failed to reduce the cognitive overhead. As we see from numerous questions on the topic, the intuitively expected behavior is create local var when actual assignment is performed. Not that you'd hit this often in "good" code, but still, one has to keep this quirk in mind. – Sergio Tulentsev Mar 06 '19 at 10:08
  • If you create the local variable when the actual assignment is performed, then it is impossible from looking at the code to decide whether you are looking at a local variable or a method call. E.g. `if rand < 0.5 then foo = 42 end; foo`, you cannot know whether `foo` is a call or local variable without running the code. Whereas with the current rule, it is crystal clear: it is a local variab.e – Jörg W Mittag Mar 06 '19 at 10:20
  • @JörgWMittag: not "crystal" clear, or at least not intuitive, but I see the benefits, yes. I'd hate explicit declarations more :) – Sergio Tulentsev Mar 06 '19 at 10:26
  • Still, it's hard to call this a "feature". – Sergio Tulentsev Mar 06 '19 at 10:27
  • @JörgWMittag why do you need to know whether `foo` is a call or local variable without running the code ? Like Sergio says not being able to know probably isn't "good" code but other than that ? – Ixio Mar 06 '19 at 10:27
  • @Ixio: It is in general a desirable property to be able to tell what code does by reading it, without having to execute it. Of course, there will always be dynamic behavior in your code, otherwise it would be boring code, but at least for fundamental language features such as variables or methods, it should be possible to tell them apart. – Jörg W Mittag Mar 06 '19 at 10:41