Using “and” and “or” in Ruby
avdi.org
avdi.org
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...
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.
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.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.
'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'.
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.
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 %>
It's stuff like this that makes me appreciate lisp.
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()And it's not like that's exactly unusual in the ruby language. Post-conditionals have a similar low precedence.
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.
This is a very simple, easily understandable and easily readable feature of Ruby. It's not obscure, complex, or even that unusual. Precedence is something every competent programmer needs to understand, and it should be part of every programmer's research to learn a new language. After all, this is a conversation about the existence of "and" & "or", not their abuse.
I try to learn what I can of such precedence in the language of the day, since I will have to maintain other people's "code" (cypher?), but I try to write obvious "programs" (who is in this play, what are the acts?).
I've been at this over 2 decades, and it's much easier to read something that uses a few parentheses, a well named intermediate variable or two, or even a few functions, than it is to read bunch of multiple operators on the same line gobble-de-gook. Watching somebody else generate a hundred thousand dollars of wasted product in a manufacturing preparation process a few years back, due to such a run-on if-statement being fouled up, was also a good confirmation of this bias. I'm sure my current job in finance offers similar opportunities for expensive blunders.
However, I think it's a big mistake to make those constructs use "and" and "or", because it overloads the semantics normally associated with those keywords. Yes, it's true that it's precisely the same logic under the hood, but you have to fight the "oh, boy, that's so neat!" reflex and think about all that could go wrong.
There's a similar gotcha in Python, where instead of ternary operator (a ? b : c) you use a combination of "and" and "or" (a and b or c). The gotcha is that it backfires if "b" evaluates to false.
All in all, it's probably not "worth it" to have nasty surprises like that, but that could be avoided by using different keywords, not "and" and "or".
x = 'foo' if foo else bar
which is at least more readable than the C/PHP/Ruby equivalent. x = if foo then 'foo' else 'bar' end
in Ruby, if you'd like.I agree that we should not write code only for experts, but that doesn't mean we should take the absolute opposite approach and refuse to use case statements or blocks because somebody might read our code without bothering to learn the basics of the language.
I agree that if you're writing a medium to large technology stack, you should take it as an assumption only proficient users of the language will hack on it.
Finally, I disagree with your assertion that only people with "hardly any familiarity with the language at all" would not know the order of operation difference between && and 'and'. I think the popularity of this story on this site speaks to that.
And no, I haven't read the original post yet. I was doing a skim-the-comments-before-reading run,
That thinking can cover a whole lot of code, and raises the question of what defines an expert?
Use the whole language; people become experts by experiencing new things.
The guidelines should be, can you yourself still read and understand it a month later, and do you find that a particular idiom tends to make bugs easier or harder to find.
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.
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.