Hacker Newsnew | past | comments | ask | show | jobs | submitlogin
Using “and” and “or” in Ruby (avdi.org)
87 points by fogus on Aug 2, 2010 | hide | past | favorite | 50 comments


I 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...


FWIW

    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.


I 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.


I really like that find and/or begin pattern. The if user= User.find never really felt right to me either.


As per Tim Bray (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.


I 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?


Also see this blog post: Logical operators in Perl and Ruby http://eli.thegreenplace.net/2007/06/02/logical-operators-in...


I 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.


This 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.


I'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'.


Writing it in two lines would defeat the purpose of demonstrating the difference between && and 'and'.


Yep, 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.


I 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.


There'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.


Yeah 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 %>


It works exactly the same in PHP, another twisted offspring of Perl.


If 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.


Except 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.


I'm starting to worry how much cargo-cult work must be happening in Ruby, if operators that have been in the precedence table for years are considered to be something obscure that you'll eventually discover.


Not only that, but operators which are hardly unique to that language (hello Perl).


Yes, Perl is a great example of a language that enables maintainable clear code.


Is "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?


To 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".


It'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.


Of 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)


This entire argument is moot. No one cares how obscure a language looks to someone who is not familiar with it. Do you regularly sit down and decide, “I am going to use a language I don't know to accomplish something essential and immediate?” And even if the answer is yes, then do you still not know the language at the end of that exercise?

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 used to be competent, but now I guess I avoid writing stuff that relies on any knowledge of operator precedence.

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.


If by "non-experts" you mean "people who have hardly any familiarity with the language at all," then yes, I'm OK with people who don't know the language not understanding code written in that language.

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.


The use case I'm thinking of is when I'm using my scripting language of choice to write some glue code and I want other people in the organization to be able to grok it easily. Perhaps Python is really the better choice for those tasks, but so often these days my choice of scripting language for task X depends on the state of libraries I plan on using. And ruby does have better libraries from many tasks.

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.


"But that does outweigh the fact non-experts don't know exactly what the statement is doing?"

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.


Depends on what you call "this kind of code". Operators like "and" and "or" facilitate some very useful and expressive idioms, so having that kind syntactic constructs is rather useful.

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".


In Python, the side effects were considered so nasty that they finally added ternary operators in 2.6. The syntax goes like:

    x = 'foo' if foo else bar
which is at least more readable than the C/PHP/Ruby equivalent.


You can do

     x = if foo then 'foo' else 'bar' end
in Ruby, if you'd like.


I just want to briefly mention that I am only on the third chapter of the Poignant Guide, hardly what you'd call an expert, and I already understand completely what is going on in the top comment's example. I know why. But maybe that's because it acts pretty much like Python with anonymous functions in this case.

And no, I haven't read the original post yet. I was doing a skim-the-comments-before-reading run,


Exactly, it's a language feature. That would be similar to calling the conditional assignment (||=) operator obscure.


I agree with your larger point, but you may not have the best example. ||= is notoriously obscure in Ruby - in details at least.

http://www.ruby-forum.com/topic/151660/


The issue is that ||= doesn't have a spelled out synonym like && has 'and'.


That said... &&= might be deemed obscure. Even experienced rubyists seem confused when I use it.


In 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()


This example REALLY illustrates the differences. Very nice.


What 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.


Agreed.

It's stuff like this that makes me appreciate lisp.


This 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.


The 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.


I'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.


My 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.


It'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.


Wow, honestly this article helped :), I really did think it was a synonym and did not realize the precedence implications.


The logic for and/or vs. &&/|| is borrowed directly from Perl. It makes sense once you use them for a bit.


Maybe 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.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: