7 ms·
Using “and” and “or” in Ruby
- GrandMasterBirt 16y agoWow, honestly this article helped :), I really did think it was a synonym and did not realize the precedence implications.
- aneth 16y agoIf you're relying on obscure not well known operator precedence rules, you are writing less maintainable code. It's as bad or worse than using meaningless variable or function names. Use parentheses so your code communicates your intention.
- cageface 16y agoAgreed. It's stuff like this that makes me appreciate lisp.
- brlewis 16y agoThis use of "and" and "or" is exactly what a Lisp programmer would expect. They're used that way a lot in Common Lisp and Scheme.
- lmkg 16y agoThe article isn't talking about the short-circuiting behavior, which I agree is what a Lisper would expect. The article is talking about their operator precedence, and why it's useful to have two sets of operators that are identical except for precedence rules. That is something that you won't find in Lisp, not even buried deep in the guts of LOOP.
- pjscott 16y agoI'm convinced that you can find anything in the guts of LOOP if you look hard enough. Except for an adequate number of parentheses. LOOP doesn't have nearly enough parentheses.
- cageface 16y agoMy point, which downmodders seem to be missing, is that you don't have to rely on the language's magic operator precedence rules in lisp do this kind of thing. The syntax is yours to command.
- ihodes 16y agoIt's funny you say that, because I just used this technique to make a few lines of my Clojure (a Lisp) code a little neater. I find it more readable, and prettier.
- klochner 16y agoIn that spirit, his last example could be improved: foo = cach[:foo] or foo = get_foo() or raise "Could not find foo!" foo = cach[:foo] || get_foo() || raise "Could not find foo!" And I've seen the "foo() and bar()" usage screwed up more than a few times by people not fully understanding the return value of foo()
- GrandMasterBirt 16y agoThis example REALLY illustrates the differences. Very nice.
- KirinDave 16y agoExcept that it's not obscure at all. "and" and "or" exist specifically to facilitate this kind of code. That's their primary purpose. And it's not like that's exactly unusual in the ruby language. Post-conditionals have a similar low precedence.
- acgourley 16y agoIs "this kind of code" worth it? It certainly is aesthetically pleasing and somewhat terse compared to a parenthesis jungle. But that does outweigh the fact non-experts don't know exactly what the statement is doing?
- sketerpot 16y agoTo anyone who learns the idiom, this code is fine, and perfectly readable. I don't even know Ruby yet, and it took me less than a minute to become fully comfortable with this kind of code. This isn't a huge barrier to "non-experts".
- CodeMage 16y agoIt's always easy to claim something is okay when you "don't even know [language] yet", isn't it? ;) All joking aside, the problem with this is not that "non-experts" don't understand what the code does at all. It's that they don't understand all that it does. If you never read anything that warns you about the difference between "and/or" and "&&/||" (or if you skim over it), it can bite you really bad.
- acgourley 16y agoOf course it took under a minute, you were reading a nice blog post on the topic. The problem is not every instance of 'and' will include that information. And so I worry that if I drop 'and' into some minor glue script I write - it becomes less self documenting to my coworkers. It's a minor point, but it can become a slippery slope (see: perl)
- 16y ago
- billybob 16y agoWhat I took away from this is "don't use these in an if statement." Even if I understand how the precedence is different, it isn't obvious at all. It would be better to use more parens to make the evaluation order explicit. The other uses shown, however, were interesting - "or" can be like a one-line if/else. If done right, I think it could be pretty clear.
- jim_h 16y agoI'm slow this morning. That took me a couple of minutes to get the example. 'foo = 42 and foo / 2' I was expecting an undefined variable since foo was never set because I was thinking of it as foo = (42 and foo / 2) Basically I would write the example as this in 2 lines to avoid confusion and not use 'and': foo = 42 foo / 2 Maybe another example in the article would have been better to show the power of 'and'.
- avdi 16y agoWriting it in two lines would defeat the purpose of demonstrating the difference between && and 'and'.
- CodeMage 16y agoYep, that's an important point: you should probably have chosen a different example. The one you used will either make people say "it's a lot better to write it in two lines" or "oh, cool, I can write one liners like that". Your post is explaining and demonstrating the use of "and" and "or", whose purpose is to allow idioms which rely on short-circuiting logic to control side effects, but that particular example didn't involve side effects after the operator (which is the whole point). Perhaps it would be a good idea to replace that example with one that shows what would happen if you used "and" in an "if". It would serve the same purpose as the one you have currently -- show the unintended effects of using the wrong flavor of operator -- without confusing people or unintentionally teaching bad practices.
- stevejohnson 16y agoI agree that the numeric example in the blog post was a bad one. The use of 'and' just looks confusing in that situation. You might try something like "foo = open_connection() and send_data()" instead.
- draegtun 16y agoAlso see this blog post: Logical operators in Perl and Ruby http://eli.thegreenplace.net/2007/06/02/logical-operators-in-perl-and-ruby/ http://eli.thegreenplace.net/2007/06/02/logical-operators-in...
- sabat 16y agoThe logic for and/or vs. &&/|| is borrowed directly from Perl. It makes sense once you use them for a bit.
- _delirium 16y agoMaybe I'm not good at the context-switching between parsing things as operators versus as English, but I find them confusing in Perl except in some very simple idiomatic cases, where they're fine because I read them as boilerplate. The common do_thing or die "error" idiom is perfectly readable, but anything more complex and I start explicitly reasoning about short-circuit semantics. It's possible I have that reaction due to the strong negative consensus about using short-circuiting as control flow in C being hammered into my head at an early age, though.
- tomstuart 16y agoAs per Tim Bray (http://www.tbray.org/ongoing/When/201x/2010/06/29/No-Defaults http://www.tbray.org/ongoing/When/201x/2010/06/29/No-Default...), remembering this kind of nonsense is a waste of a perfectly good brain. Just don't bother.
- msie 16y agoI agree with you, but I always wonder if we discount memorization too much that it impedes learning a language. We can't expect all of a language to be "logical". There will be always quirks that have to be remembered. If we discount those parts that have to be remembered then have we shortchanged ourselves in mastery of a language? Warts and all?
- raganwald 16y agoI use this a lot. I dislike side effects in the subject clause of an if statement, so I used to rewrite: if user = User.find(...) ... end as: user = User.find(...) if user ... end I then got into the habit of using and: user = User.find(...) and begin ... end It just seems to resemble something I'd say to a colleague: "user becomes blah blah blah and then we do this with it..." Likewise: user = User.find(...) or begin user = User.create(...) SecurityTheatre.starring(user) end This looks to me like it says "find the user or create the user." The precedence of the words "or" and "and" seems obvious when you think of them as words rather than as operators, while the precedence of operators naturally seems higher/tighter. JM2C...
- jseifer 16y agoI really like that find and/or begin pattern. The if user= User.find never really felt right to me either.
- KirinDave 16y agoI like your find-or-begin pattern, and I've used the or operator to pun that way before as well. But just a note, the if x = ... is not a side effect unless the variable "x" already exists. There's not problem using that as a pun to get rid of lots of if nesting. Similarly if x &&= ... is very good for avoiding an if-cascade, albeit more obscure and perhaps less performant. It's too bad there isn't a beautiful way to do monads or the thrush operator in Ruby.
- jamesbritt 16y agoFWIW user = User.find(...) and ( ... ) also works. I find it cleaner. But using begin/end means you can toss a `rescue` in there if you like.
- bryanlarsen 16y agoThere's just too much semantic overload on those two operators. I myself have misread these before. When I use these operators, I always follow with "raise" or "begin". This customary usage is not going to cause confusion and covers 99% of their usage anyways.
- jewbacca 16y agoThis seems like a kind of hack-y laziness, specifically in the form of a poor man's exception. It's no more "magic" than an if statement or any other kind of block. I would probably use this plenty if I were to use Ruby (Haskell has spoiled me for strict evaluation forever), but it definitely smells like language bloat -- you wouldn't need to tell me it's Perl-inspired for me to strongly suspect it.
- sunkencity 16y agoYeah I've looked briefly at and before and not gotten it's use, this is awesome. In a similar vein it's possible to just hang on a rescue at the end, very practical and easy to read, like in a view: <% percent = (count /total.to_f * 100 ).round_with_precision(2) rescue 0 %>
- code_duck 16y agoIt works exactly the same in PHP, another twisted offspring of Perl.
- gmac 16y agoI knew this, but I always find it a rather disappointing choice in the language design, as I think its capacity for inspiring bugs rather outweighs its usefulness (which extends as far as making some parentheses unnecessary). In a nutshell: there are two forms of the AND and OR operators, with different precedences; in many contexts, you can use them interchangeably; but in some, they'll behave differently, and could bite you.