Hacker News new | past | comments | ask | show | jobs | submit login
Ruby Demystified: and vs. && (tinfoilsecurity.com)
47 points by angelirizarry on March 8, 2013 | hide | past | favorite | 69 comments



Not to start a language war, but this blog post demonstrates two things Python got right that Ruby got wrong.

First, in Python, there's only one way to express a boolean "and". The && operator is left out.

Second, this post demonstrates a danger of allowing assignment via '=' evaluate to an expression (granted with less weird precedence rules it wouldn't turn out as bad in this case).


Not to acknowledge we are at war, but allow me to react to your act of war.

This article demonstrates one thing that Ruby does awesomely, and python does not at all. Wether that makes Ruby better than Python.. well yes it does, no point in being diplomatic now, we're at war!

In Ruby there is only one way to express a boolean and, it goes: &&. Besides the boolean and there is also a binary operator with the name `and` which evaluates its left child, _and_ when it is true returns the value of its right child.

In this way it mimics the way we build sentences, that's smart. That's intuitive. That's Ruby.


"which evaluates its left child, _and_ when it is true returns the value of its right child"

I like the way you put that. I always stumble for a moment when asked to explain why using "and" and "&&" interchangeably is not a good idea because it can lead to subtle bugs in your program. Maybe a mnemonic would help to remember this more easily.

    left && right is true or false, but
    left and right is right when true
or something along those lines.


That seems more confusing, since they evaluate to the same thing.

    1.9.3p392 :001 > true && 1
     => 1 
    1.9.3p392 :002 > true and 1
     => 1 
    1.9.3p392 :003 > false && 1
     => false 
    1.9.3p392 :004 > false and 1
     => false 
    1.9.3p392 :005 > nil && 1
     => nil 
    1.9.3p392 :006 > nil and 1
     => nil 
Which is why explicitly checking for "true" or "false" in a conditional is unusual.


>In this way it mimics the way we build sentences, that's smart. That's intuitive. That's Ruby.

The problem is that "just like English" does not always translate to "intuitive for a programming language". Programmers don't have a problem with learning a new set of rules that don't work the same as their native language, because programming isn't (and shouldn't be) like writing a letter to your computer.

The simple rule is "if it's a binary operator, it has higher precedence than assignment". That rule works for almost every binary operator in almost every language. Show me an exception (like the comma operator in C), and I'll show you an operator that confuses the hell out of most programmers.


My biggest complaint is that you called Ruby's way "intuitive."


  | In this way it mimics the way we build sentences,
  | that's smart. That's intuitive. That's Ruby.
That's also a nod to Perl. :P


Both && and and are short-circuiting operators in Ruby. To eagerly evaluate both operator arguments you would need to use &.


"&" isn't even the same category, since it is a definable method and its meaning varies with the value on the left hand side.


I think Haskell wins out here since it has neither in the core language. Much easier to reason about. ;)


This explanation is an excellent way to teach and vs &&.


I don't think it actually works as a good explanation of the differences, the article it self does a good job by explaining the precedence rules which is the actual technical difference between the two. My explanation just shows the intuitive difference.


Python's "and" is equivalent to Ruby's "&&". Python has no equivalent to Ruby's "and" which is copied from Perl. In other words, Ruby has only one way to express a boolean "and", "&&", but the existence of the flow-control modifier "and" trips up new users constantly. Which is why this topic lands on HN regularly.


Which is, from the Python mindset, a flaw in Ruby the language.


And from a Ruby mindset, is a limitation in the Python language. :)


Yep. There's no right answer. I say, a pox on both their houses.

  (and predicate-1? predicate-2?)


As someone currently dealing with a lot of Java code, I love that Java only has && and not "and", it allows Java programmers to construct simple, easy-to-understand constructs like this:

  if (true && false == true) {
     return new BooleanFactory(true);
  } else {
     return new BooleanFactory(false);
  }


> Second, this post demonstrates a danger of allowing assignment via '=' evaluate to an expression (granted with less weird precedence rules it wouldn't turn out as bad in this case).

I think it's just an issue of precedence. Assignment evaluates to an expression in all Lisps, ML, Haskell, Scala, and Rust, without this issue.

Stricter typing can also solve the problem. If assignment evaluates to unit, as it does in ML/Haskell/Scala/Rust to name a few, then the attempt to perform the logical-and operation with unit and bool would throw (in a dynamically typed language) or won't compile (in a statically typed language).


Whether right or wrong, I personally prefer the Python way and I'm not a particular fan of Tim Toady. Especially when working in teams, it's simply easier if you don't have to make up rules, but it's clear upfront what to use, because there is no need for discussion.


This is not really a Tim Toady. && and and has different use cases.

One (ruby/perl: &&, python: and) is used to evaluate truthness of two expressions.

The other (ruby/perl: and, python: N/A) is more of a program flow operator. Now that I think about it.. If it is a Tim Toady, it is more of _if_ vs _and_ rather than _&&_ vs _and_.


Good point, I haven't thought about it that way.


This is basically the author finding out something in Ruby that was copied from Perl. E.g. it allows one to do:

  $fh = open() or die("File did not open")
Instead of:

  $fh = open()
  die("File did not open") unless $fh


What's wrong with

    $fh = open() || die("File did not open")
? The only difference is that the results of `die` are assigned to `$fh`, but I don't think `die` even returns. It still short-circuits.


Nothing is wrong with that. However the actual Perl open() would be like this...

  open my $fh, '<', 'file.txt'  or die "file.txt not found";

  open(my $fh, '<', 'file.txt') || die 'file.txt not found';
Above two lines are equivalent.


Maybe not in this specific instance, but both operators have different behaviour and different uses.


> When I first started learning Ruby, I was excited to discover that Ruby has the keywords and and or as boolean operators. I quickly got into the habit of writing all of my boolean statements with these, instead of the more conventional && and ||, because my code became so much readable

     pry(main)> a = true && false
     
     pry(main)> a = true and false

Hmm...I dunno, I don't think the latter is necessarily easier to read than the former. The use of the symbols helps, for me at least, to delineate between value/variables and operators. Those symbols aren't pretty but their ugliness can serve a purpose.

I don't mind using them in SQL, but only because SQL is case-insensitive and I can stick to the style of making all syntax uppercase.


I dislike using symbols, I think because they draw a mostly-arbitrary line between operators and method calls. As a scala/python fan I like to blur that line, and write code that reads as close to English as possible, so I'd write expressions like

    (user isActive) and (user.expiry before now)
and I think that really is more readable than '&&', in a way that your example obscures because you're still using '>' and 'false'.


In a well-designed, flexible language (which includes Scala), operators are not a special case. In fact, in Scala, they are just methods, so there's no arbitrary line between the two!

I personally like the Haskell/OCaml approach of having operators just be normal functions that happen to be parsed in infix position. It's simple, elegant, uniform and flexible.

Either way, the core idea is that operators should be nothing special.


And Lisp takes this idea one step further by having operators just be normal functions that are in the prefix position like normal functions. This, combined with the explicit parentheses to denote evaluation, mean that operator precedence is not even an issue in Lisp.


Yes, that's exactly my point. You wouldn't use symbols for the names of normal functions (at least I hope you wouldn't).


This article isn't about replacing symbols with words, ruby isn't about replacing symbols with words either, and there is no place in the article where the author states that

    a = true and false
is prettier than

    a = true && false
And if you would actually have read the article and paid close attention you would have learned that the two lines you just said are not functionally equivalent.

No ruby programmer would ever have a line that says

    a = true and false
It just makes no sense.

Maybe a very experienced ninja ruby programmer would, but that programmer would know exactly what he was doing.

The beauty in ruby and in the 'and' keyword is that a ruby programmer intuitively knows when to use which. This article just clears up why exactly they work like you expect them to work.

edit: maybe a weird programmer would do something like this

  @message = "We failed :(" and false
at the end of a function that has conditionals with return statements in them.


Wow, I must have missed the point. Because I could've sworn the OP was talking about how he thought that 'and' and 'or' could be stand-in's for && and ||, but he found out that they aren't quite equivalent. Because if they were equivalent, he would substitute words for symbols because, as I quoted, "[his] code became so much readable."

I'm not evaluating what he discovered at the end of the post. I'm evaluating his original intent for using 'and' and 'or' in the first place, which was readability. I'm simply stating my opinion on that comparison.

And yes, "a = true and false" doesn't make sense. I just copy-pasted his sample code as a quick example. I didn't expect people to take it literally, as if I were showing off a best practice.


Hmm you're absolutely right. I guess I'm projecting, I just read that as using `and` and `or` in places that seemed logical to him, but the paragraph is quite clear in that he just replaced all instances of the symbols with the words.

Since that is the case, I fully agree with you and shouldn't have been so snarky. I apologize :(


Well, it's a Friday afternoon :) You were right I didn't read to the very end (I kind of guessed what the OP was going to conclude)...I was mostly interested in seeing of other people thought 'and' and 'or' were good Ruby idioms to use (ignoring the precedence confusion).


If by "no place" you mean "first paragraph", you're right:

> I quickly got into the habit of writing all of my boolean statements with these, instead of the more conventional && and ||, because my code became so much readable.

And I definitely agree with danso and disagree with the blog on this issue: the operators gives more structure to the code and makes it much easier to parse at a glance. With the 'and' version, I have to actually read the code to figure out what its doing; with the '&&' version, I can get the basic idea just from the code's "shape" (for lack of a better word).

I think being able to quickly grasp the idea at a glance is far more important than having code look like English.


there is no place in the article where the author states that a = true and false is prettier than a = true && false

Something like this, you mean?

I was excited to discover that Ruby has the keywords and and or as boolean operators. I quickly got into the habit of writing all of my boolean statements with these, instead of the more conventional && and ||, because my code became so much readable

If you would actually have read the article (or even just the comment you replied to) and paid close attention you would have learned that it said precisely that.


  and and or are control-flow modifiers like if and unless.
http://devblog.avdi.org/2010/08/02/using-and-and-or-in-ruby/


tldr: && has higher operator precedence than "and"

Significantly, "&&" > "=" > "and", which can lead to unintended results when refactoring "&&" --> "and"


I.E.: why you should always use && instead of "and".


To avoid a potentially useful language feature because you might make a mistake with it is, to me, a absurd.

A much better argument might be Use <feature> only when you fully grasp its use, which is true of any feature. Some are more difficult to understand than others, especially when trying to relate a feature to another language, but that's just not an argument I can support.


It might not just be you working. Sure, use things that you understand. The problem comes when you aren't even aware you don't understand due to subtleties you may have not encountered yet. Then you do some debugging, do some googling, and bam, now it's "obvious to anyone with half a brain, everyone should just RTFM"


I understand the potential harm when working with teams, but I also recognize that it's a shame to limit your use of the language to avoid potential trouble. Surely there's some middle ground to be found over dogma such as Never use <feature>.

I'm not suggesting that everyone should understand every detail of every potentially misunderstood feature. I'm suggesting that these situations should be dealt with on a case-by-case basis.


No. You should use them appropriately.


So why have both?


You guys would benefit from reading the article.


Read the article.


I did. And I still don't see why you'd have both.

It seems like a landmine for someone learning the language and a place for bugs to creep in to code.

Pick one and go with it. The extra expressiveness or whatever a ruby person would call it, that comes from the alternate forms just doesn't seem worth it.


> Pick one and go with it.

They did. It's `&&` and `||`.

I understand your point when it comes to it being a potential landmine, and honestly I never use them myself. But I've never seen any ruby guides or docs use `and` instead of `&&`, and I have seen skilled rubyists make great (and correct) use of `and`.


> They did. It's `&&` and `||`.

I assume you mean idiomatically chosen, since clearly the decision wasn't made at the language level.

> I have seen skilled rubyists make great (and correct) use of `and`.

I'm obviously not a ruby programmer, so it doesn't matter much to me, but it seems odd to argue that having a largely disused second set of logical operators in a language that skilled people periodically trot out for some cases is anything but a design wart. Maybe it's a cultural thing in the ruby world.

And, honestly, I don't mean that to sound snide or slighting. It's just odd to me.


Fair point. It is a bit odd. IIUC the low/reverse-precedence operators came from Perl, and whether or not to use them is just a stylistic choice. It's clear what this means:

> raise "ooops" unless do_something()

But even so, it reads a bit backwards. Some codebases do this instead:

> do_something() or raise "oops"

That's just repeating one of the examples in the article, but it jumps out to me as the one I've seen most often.


I never understood what Perl (and now Ruby) has against if statements.

  foo or do_something()
is exactly the same as

  if foo {
      do_something()
  }
Except the latter is WAY less likely to be accidentally misread.... and don't even get me started on

  do_something() unless foo


It's all just a matter of taste and preference. If you find yourself consistently misreading commonplace ruby, maybe ruby isn't for you.


Makes for an interesting blog post, I guess. But precedence for "&&" and "||" in Ruby are similar to precedence for "&&" and "||" in C, Java and others, and they are similar to precedence rules for "and" and "or" in Python. Precedence rules for "and" and "or" in Ruby are an anomaly not just in the Ruby world but in many (most?) mainstream languages.


"In many (most?) mainstream languages" doesn't really agree with "anomaly". If it's in most mainstream languages, then it's not an anomaly, it's the norm.


It seems to me that Ruby's "and" and "or" are more similar to Prolog's ','/2 and ';'/2

([Edit] not that Prolog is necessary a shining example of a mainstream programming language)


I think I may be the last person who still thinks "and/or" are just fine for boolean expressions in Ruby.

It is true that "and/or" are not equivalent to "&&/||". But I don't find any of the arguments that we should then avoid "and/or" in favor of "&&/||" convincing.


Yup. They're like any other tool. If you're not sure how to use and/or, then don't use them. Or, better yet, study up. Programming can benefit from nuance.


Operator precedence (and && vs. and with different precedence) has been around since forever - this particular one since Perl was a fresh and novel thing at least. If it's a mystery that needs "demystifying" for somebody, it's time to hit the books.


Seeing "and" or "or" in Ruby code now leaps out at me with danger signs. I've decided it's just not worth it.


I've never actually seen "and" or "or" in Ruby code. I very frequently see it in Perl code.


I see it fairly often in Rails controllers, e.g.

render :action => "some_action" and return


Not that I want Ruby to be anything like PHP and Perl, but this is the same thing that happens in those languages due to the difference in precedence of and, &&.


It's no accident. Ruby is heavily influenced by Perl.


Oh I know... someone said ruby is two parts Perl, one part Python, and one part Smalltalk. I like to think it's a bit less Perl than that, but my judgement is clouded - I'm a bit of a Perl hater.


I think it's a little less Perl than that, and a little more Lisp than that.

I don't have enough Perl experience to have a strong opinion on it, but I know that Matz says he created Ruby because he wanted a language that was more powerful than Perl and more object-oriented than Python.

Ruby is a little bipolar in that sense. It's both highly functional and highly object-oriented, for an imperative language. Even more of a "Swiss-army chainsaw" than Perl.


tl;dr:

'&&' is a boolean and 'and' is for control flow


I don't know why everyone thinks a race to the fewest lines of code is a good thing.


It was an interesting experiment while it lasted, but I don't think they make sense.


If this will help the brogrammers and fauxgrammers in the ruby world to take security and software engineering seriously, then it is a good thing.




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

Search: