http://qs1969.pair.com?node_id=418058

One of the most common and frustrating bugs that can occur in programs in any language with C-derived syntax is the accidental substitution of "=" for "==". For example,

if( $splort = 0 ) { print 'narf!'; }

What makes this bug so nefarious is that it's syntactically correct, and the similarity of the assignment operator to the numerical equivalence operator makes it difficult to spot visually. Our brains are very good at pattern recognition, and fluent, literate people read words as discrete entities rather than processing each letter.

Looking at code is similar. I find, in the case of this error, that my mind expects to see == inside a conditional, and that's what I see. Until about the 20th time, when I force myself to read each individual letter, and notice the mistake.

For years I was plagued by tracking down these frustrating bugs. There's a certain pleasure in discovering the reason for a malfunction, but the == bug is just annoying. You can't blame a miscalculation or design flaw, it's just a typo. Worse, most typos are caught by invalid syntax or, in the case of bad variable names, by strict. But not this one. At one point, I had even considered writing source filter to look for assignments that had a high probability of actually being comparisons.

While I was lamenting about this topic to a former boss, he casually remarked, "Ah, yes, that's easy. Just put the constant on the left."

I blinked. "What?"

"Put the constant on the left," he repeated. "You can't assign to a constant, so it's a syntax error if you say = instead of ==."

The simplicity of this solution astounded me. All I had to do was get in the habit of writing:

if( 0 == $splort )

If I screwed it up, it simply wouldn't compile, instantly identifying the error. Best of all, it works the same for C, Java, PHP and other languages which I have used in the course of my career. Since then, I have made it my mission to point out this tip to fellow programmers, and I have been surprised that so few have ever considered it. Once they do, I am hailed as a genius. Which is why I like this trick even more.

In conclusion, one of life's most important lessons is that complex problems can often have ridiculously simply solutions, and often it takes the eyes or ears of another person to see them.

Replies are listed 'Best First'.
Re: Avoiding the == blues
by BrowserUk (Patriarch) on Dec 29, 2004 at 19:41 UTC

    Even easier, use warnings or -w:

    >perl -w my $splort; if( $splort = 0 ) { print 'narf!'; } ^Z Found = in conditional, should be == at - line 2.

    Examine what is said, not who speaks.
    Silence betokens consent.
    Love the truth but pardon error.

      That is not nearly as useful as it only catches trivial cases:

      > perl -w use strict; my $bool = 0; if( $bool = 0 ) { print "never\n"; } <EOF> Found = in conditional, should be == at - line 3. > perl -w use strict; my $bool = 0; sub test { return 0 == @ARGV } if( $bool = test() ) { print "Match\n"; } <EOF> Match

      - tye        

      You are absolutely correct, of course, but this doesn't work for other languages, or for badly written scripts which generate thousands of warnings that you have to maintain. :)
        In some other languages, if takes a boolean, which is (deliberately) not what an assignment returns, so if (x = 0) doesn't get past the compiler.
        Well, saying "other languages" isn't quite correct, as it's the compiler that will issue a warning, if any. And gcc with -Wall will. It will even catch cases like:
        if (x = y)
        unlike Perls -w, which will let you do
        if ($x = $y)
        while keeping silent.
Re: Avoiding the == blues
by webfiend (Vicar) on Dec 29, 2004 at 23:02 UTC

    Good advice. You still have to be careful about when you are comparing two non-constants, though:

    if ($x = $y) { print "Ah jeez, I did it again.\n"; }
Re: Avoiding the == blues
by Mutant (Priest) on Dec 30, 2004 at 10:27 UTC
    I really really don't like:
    if ( 0 == $foo )
    There's just something wrong with it to me. I usually catch these bugs with proper unit testing.
      Yes, what's really going on here is that, while the computer treats $a == $b as commutative, the human brain is linguistically wired, and tends to parse such constructs asymmetrically as a topic and a predicate. And as it happens, the topic likes to be first. For similar reasons most OO programmers will choose to write:
      foo.equals(0)
      rather than:
      0.equals(foo)
      Despite the fact that Ruby considers 0 to be a Real Object, I suspect that even Ruby programmers will prefer the first form to the second, because that's how our brains are wired.

      The solution to all this in Perl 6 will be that assignment to an existing variable in boolean context will produce a warning under strictures, but assignment to a my declaration will not. The typical use for assignment in a boolean context is actually not the if statement but the while statement:

      while $item = shift(@foo) {...}
      and in most cases this is probably better written as:
      while my $item = shift(@foo) {...}
      or even better:
      for @foo -> $item {...}
      Likewise,
      if $x = long_expression() {...}
      can turn into
      given long_expression -> $x {...}
      I suppose we might also allow the gcc-like workaround of putting extra parens when you want to hide the assignment from the boolean context.
        Depends on the developer. It's better to write literal comparisons (literal the noun) as "bob".equals(someVariable), when there is a chance that someVariable could be null/nil/undef. Sorta the take on the if(someLiteral == someVariable) in c, so you couldn't write things like if(0=variable) by accident. Compiler err and all...
      i don't see your point....what is your point, about not liking it? Seriously though, I think that too many programming teachers actually teach the "$foo == 0" way, which gets ingrained in a lot of us. if you think of the equality test as a weight scale, with each variable/constant being a weight, it should make it fit better for you. you can also have some fun with short circuit logic:
      if ( 5 == $foo) print "tadaa";
      can become
      ( $foo -5) && print "tadaa";
      so you don't use == at all
      the hardest line to type correctly is: stty erase ^H
Re: Avoiding the == blues
by SolidState (Scribe) on Dec 30, 2004 at 08:37 UTC
    Since most people tend to write the "if" and and conditional on the same line, the following one-liner should work for most Perl scripts:
    perl -ne'if(m/if\s*\(.+?=[^~].+?\)/){print "Possible use of = instead +of == in line $.\n"}' script.pl
    Of course this won't catch cases where the if and conditional are not on the same line, or constructs such as:
    something() if $var1 = $var2;
    Covering more cases will require a full blown script... or perhaps a new CPAN module :-)