5 ms·
Ruby statement modifiers behave differently than conditional statements
- figgs 17y agoThe following is wrong. Only read the rest of this reply if you want to see how dumb I am. This is not a bug. "a = 5 unless defined? a" is not meant to be equivalent to this: unless defined? a a = 5 end It is supposed to be (and is) equivalent to this: a = unless defined? a 5 end*
- angilly 17y agoThat makes sense, but I've never seen statement modifiers defined that way. Every explanation I've ever seen, the pickaxe included, seems to outright state, or at least imply, the former.
- pvg 17y agoI think it's easy to get confused here because there's a natural tendency to read and interpret 'unless' as English and the fact that assignment in Ruby is also implicit declaration. Once this happens - a = a is defined. It doesn't matter at all what comes after (as long as it keeps the expression syntactically valid) - a is defined. The only question remains is what will end up getting assigned to it. Try a = asdfgasdfweradsf it will complain there is no asdf... but a is defined.
- deleted 17y ago[deleted]
- gaius 17y agoThat definitely sounds like a bug to me. Either a statement is fully valid and is executed, or it isn't and it isn't. In Python: >>> a=b Traceback (most recent call last): File "<stdin>", line 1, in <module> NameError: name 'b' is not defined >>> a Traceback (most recent call last): File "<stdin>", line 1, in <module> NameError: name 'a' is not defined >>> I think that follows the POLA...
- angilly 17y agointeresting. in ruby: ra:~$ irb irb(main):001:0> defined? a => nil irb(main):002:0> defined? b => nil irb(main):003:0> a = b NameError: undefined local variable or method `b' for main:Object from (irb):3 irb(main):004:0> a => nil irb(main):005:0> b NameError: undefined local variable or method `b' for main:Object from (irb):5 irb(main):006:0> defined? a => "local-variable" irb(main):007:0> 'a' gets defined.
- pvg 17y agoDepends what you are astonished by, I suppose. As far as I can tell the Ruby behaviour has to do with the fact that Ruby variable definition appears to take place sometime before evaluation, maybe during parsing. So as long as the statement parses and looks like a variable definition/assignment, you get your variable. On evaluation, undefined variables cause barfage but in the case above, the a got defined before the statement was actually executed.
- gaius 17y agoWell, I am astonished that entering invalid code can cause a persistent state change within my interpreter. I can't think of any other REPL language in which that happens. OCaml: # let a=b;; Error: Unbound value b # a;; Error: Unbound value a # Haskell: Prelude> let a=b <interactive>:1:6: Not in scope: `b' Prelude> a <interactive>:1:0: Not in scope: `a' Prelude> Scheme: guile> (define a b) Backtrace: In standard input: 1: 0* (define a b) standard input:1:1: In expression (define a b): standard input:1:1: Unbound variable: b ABORT: (unbound-variable) guile> a ERROR: Unbound variable: a ABORT: (unbound-variable) You can see where my astonishment comes from...
- pvg 17y agoYeah I was a little surprised too. It looks like the parser just defines foo for any parsable foo = .... before anything actually runs. Kind of a funky design choice, especially for such a dynamic language.
- aaronblohowiak 17y agothis is wrong! >> a = 1 => 1 >> b = 2 => 2 >> a = 3 unless b==2 => nil >> a => 1 >> a = unless b==2 >> 2 >> end => nil >> a => nil The difference is in the variable definition semantics, not in the assignment semantics. Please do not spread this misinformation.
- figgs 17y agoMy bad. Thanks for clarifying that.
- Locke 17y agoYes, they are different. For example: def ex1 a = 5 if a puts "a: #{a}" end end def ex2 if a = 5 puts "a: #{a}" end end def ex3 puts "a: #{a}" if a = 5 end If we start with the code in ex1 and decide to shorten it, ex2 is okay (although some people don't care for assignment in a conditional because it could be confused for a typo). But, ex3 will raise an exception that 'a' has not been defined. I've just internalized using the longer form when using assignment in a conditional.
- chief 17y agoI think the article is wrong. His rewrite on line 06. should use if instead of unless. $ irb >> b NameError: undefined local variable or method `b' for main:Object from (irb):1 >> if defined? b >> b = 5 >> end => nil >> b => nil >> defined? b => "local-variable"
- angilly 17y agoNo, it's using unless like I intended. But your example also demonstrates the weird behavior. As a few people have pointed out here and over in the post comments, the issue at hand isn't so much about statement modifiers as it is about how Ruby 'defines' local variables. Take this for example: ra:~$ irb a = irb(main):001:0> a = b NameError: undefined local variable or method `b' for main:Object from (irb):1 irb(main):002:0> defined? a => "local-variable" irb(main):003:0> It just so happens that using statement modifiers in the way I tried using them brings this behavior to light.
- deleted 17y ago[deleted]
- aaronblohowiak 17y agoI contend that if you are using defined? you are doing something wrong. local variables should be local. If you don't know what exists in the current scope, you are dealing with confusing code. Rails erb evaluation for partials makes this mistake, and creates a poor reimplementation of function calls where the function header is defined by the caller with the :locals =>{} hash.
- pvg 17y agoOr at the very least you are likely to get terribly confused. The posters confusion has absolutely nothing to do with conditionals and modifiers and is entirely about Ruby variable definition and scoping semantics. In fact if you broke one of his examples by reversing the conditional, the variable, the assignment of which is never evaluated is still defined. >> a = 1 => 1 >> unless defined? a >> b = 5 >> end => nil >> defined? b => "local-variable" >> b => nil
- angilly 17y agowhoa. didn't see that coming.
- aaronblohowiak 17y agoRight, variable definition happens as early as when the code is parsed, even if it is never used.
- jrmurad 17y agoI don't know whether they're planning to deprecate that but they do provide the alternative local_assigns hash for partials now.
- tmm1 17y ago>> if false >> a = 1 >> end >> defined? a => "local-variable" >> a => nil
- india 17y agoThat explains it! It does break the principle of least surprise though...
- rue 17y agoI think the main part was already covered, but there is one important detail left uncorrected: They are expression modifiers and conditional expressions. Everything in Ruby is an expression, not a statement.