views:

3175

answers:

10

I'm a big fan of Douglas Crockford's writing on JavaScript, particularly his book JavaScript: The Good Parts. It's made me a better JavaScript programmer and a better programmer in general. One of his tips for his jslint tool is this :

++ and --
The ++ (increment) and -- (decrement) operators have been known to contribute to bad code by encouraging excessive trickiness. They are second only to faulty architecture in enabling to viruses and other security menaces. There is a plusplus option that prohibits the use of these operators.

This has always struck my gut as "yes, that makes sense," but has annoyed me when I've needed a looping condition and can't figure out a better way to control the loop than a while( a < 10 )do { a++ } or for (var i=0;i<10;i++) { } and use jslint. It's challenged me to write it differently. I also know in the distant past using things, in say PHP like $foo[$bar++] has gotten me in trouble with off-by-one errors.

Are there C-like languages or other languages with similarities that that lack the "++" and "--" syntax or handle it differently?

Are there other rationales for avoiding "++" and "--" that I might be missing?


UPDATE -- April 9, 2010:

In the video Crockford on JavaScript -- Part 5: The End of All Things, Douglas Crockford addresses the ++ issue more directly and with more detail. It appears at 1:09:00 in the timeline. Worth a watch.

+5  A: 

The "pre" and "post" nature of increment and decrement operators can tend to be confusing for those who are not familiar with them; that's one way in which they can be tricky.

McWafflestix
Agreed; however, an experienced professional *should* not be confused.
Nate Bross
I think this is similar to the guidance to avoid making everything static or global. There's nothing inherently wrong with it as a programming construct, but ignorant overuse can lead to bad code.
Joel Martinez
The question isn't whether a good programmer could "work their way" through the logic - good code ideally doesn't require any work to understand. I think McWafflestix's point is just that you shouldn't write code that upon brief inspection might lead to adding bugs in two weeks or two years. I know I've been guilty of adding code to a routine I didn't fully understand :)
Mike
@Mike: yah, notice how I didn't specify whether the operators can be tricky for the original author of the code or for later maintainers. Generally, we try to assume that original coders don't use constructs they're not familiar / comfortable with (sadly, this is frequently an incorrect assumption); however, that familiarity certainly does not extend to maintainers (and, as parenthetically noted above, may not even extend to the orignal author).
McWafflestix
+54  A: 

I'm frankly confused by that advice. Part of me wonders if it has more to do with a lack of experience (perceived or actual) with javascript coders. I can see how someone just "hacking" away at some sample code could make an innocent mistake with ++ and --, but I don't see why an experienced professional would avoid them.

Jon B
I heartily concur...
McWafflestix
Good point Jon B. That's why It's bothered me so. Crockford is a career programmer (decades-worth), conversant with multiple languages across decades, and working with JavaScript basically since its inception. I don't think his advice is coming from "I'm lazy and inattentive inexperienced hacker" -- that's why I'm trying to understand where it's coming from.
artlung
@artlung Maybe its more to do with "A lazy and inattentive inexperienced hacker" will probably mess with this code, and I don't want to confuse him.
chris
+1  A: 

Is Fortran a C-like language? It has neither ++ nor --. Here is how you write a loop:

     integer i, n, sum

      sum = 0
      do 10 i = 1, n
         sum = sum + i
         write(*,*) 'i =', i
         write(*,*) 'sum =', sum
  10  continue

The index element i is incremented by the language rules each time through the loop. If you want to increment by something other than 1, count backwards by two for instance, the syntax is ...

      integer i

      do 20 i = 10, 1, -2
         write(*,*) 'i =', i
  20  continue

Is Python C-like? It uses range and list comprehensions and other syntaxes to bypass the need for incrementing an index:

print range(10,1,-2) # prints [10,8.6.4.2]
[x*x for x in range(1,10)] # returns [1,4,9,16 ... ]

So based on this rudimentary exploration of exactly two alternatives, language designers may avoid ++ and -- by anticipating use cases and providing an alternate syntax.

Are Fortran and Python notably less of a bug magnet than procedural languages which have ++ and --? I have no evidence.

I claim that Fortran and Python are C-like because I have never met someone fluent in C who could not with 90% accuracy guess correctly the intent of non-obfuscated Fortran or Python.

Thomas L Holaday
Yeah, IFortran is from the 1950s, while C is from the 1970s, so maybe no to Fortran being C-like. The example of Python lacking something like the plusplus operator is interesting. Iterating over data structures is more plainly handled in Python, Python being "differently" object oriented JavaScript. Perhaps Crockford's advice is about using more OO-like syntax for iteration.
artlung
+7  A: 

There is a history in C of doing things like:

while (*a++ = *b++);

to copy a string, perhaps this is the source of the excessive trickery he is referring to.

And there's always the question of what

++i = i++;

or

i = i++ + ++i;

actually do. It's defined in some languages, and in other's there's no guarantee what will happen.

Those examples aside, I don't think there's anything more idiomatic than a for loop that uses ++ to increment. In some cases you could get away with a foreach loop, or a while loop that checked a different condtion. But contorting your code to try and avoid using incrementing is rediculous.

Eclipse
i = i++ + ++i; made my eyes hurt a little - :)
Hugoware
Mine too. Even if they weren't all the same variable, say I had x = a++ + ++b; -- that combination instantly makes x, a and b harder to hold in my head. now, if each were separate statements on separate lines, as in -- a++; ++b; x = a + b; it would be easier to comprehend on first sight.
artlung
But if you keep autoincrement on its own line, there's no reason to ever choose pre-increment over post-increment. So you get a++; b++; x=a+b;
Nosredna
Alas, (a++; b++; x=a=b) is not the same value as (a++ + ++b).
Thomas L Holaday
`++b; x = a + b; ++a;` @Nosredna, there is a reason: pre-increment is ever-so-slightly more efficient
nickf
It doesn't get confusing until you try a+++b, a++ +b and a+ ++b
Marius
@Marius: `x = a+++b` --> `x = (a++)+b` --> `x = a + b; a++`. The tokeniser is greedy.
Callum Rogers
+31  A: 

My view is to always use ++ and -- by themselves on a single line, as in:

i++;
array[i] = foo;

instead of

array[++i] = foo;

Anything beyond that can be confusing to some programmers and is just not worth it in my view. For loops are an exception, as the use of the increment operator is idiomatic and thus always clear.

cdmckay
Yeah, that's what I do too. Hard to go wrong, and easier for other people to figure out what you're doing.
Nosredna
That seems to be getting at the core of the issue and my confusion. Used alone, i++; is crystal clear. The minute you add other code around that basic expression, readability and clarity begin to suffer.
artlung
+5  A: 

Consider the following code

 int a[10];
 a[0] = 0;
 a[1] = 0;
 a[2] = 0;
 a[3] = 0;
 int i = 0;
 a[i++] = i++;
 a[i++] = i++;
 a[i++] = i++;

since i++ gets evaluated twice the output is (from vs2005 debugger)

 [0] 0 int
 [1] 0 int
 [2] 2 int
 [3] 0 int
 [4] 4 int

Now consider the following code :

 int a[10];
 a[0] = 0;
 a[1] = 0;
 a[2] = 0;
 a[3] = 0;
 int i = 0;
 a[++i] = ++i;
 a[++i] = ++i;
 a[++i] = ++i;

Notice that the output is the same. Now you might think that ++i and i++ are the same. They are not

 [0] 0 int
 [1] 0 int
 [2] 2 int
 [3] 0 int
 [4] 4 int

Finally consider this code

 int a[10];
 a[0] = 0;
 a[1] = 0;
 a[2] = 0;
 a[3] = 0;
 int i = 0;
 a[++i] = i++;
 a[++i] = i++;
 a[++i] = i++;

The output is now :

 [0] 0 int
 [1] 1 int
 [2] 0 int
 [3] 3 int
 [4] 0 int
 [5] 5 int

So they are not the same, mixing both result in not so intuitive behavior. I think that for loops are ok with ++, but watch out when you have multiple ++ symbols on the same line or same instruction

Eric
Thanks for doing that experiment. Particularly "simple looking" code, but gets difficult for my brain to hold onto very quickly. Definitely falls into the "tricky" category.
artlung
I stopped after the first example. You said "Consider the following code". No, no one writes that code. It's the idiot who writes that who's flawed, not a language construct.
280Z28
280Z28
You've shown that two different piece of code produce different output and this proves it's bad?
nickf
the conclusion is "watch out when you have multiple ++ symbols on the same line or same instruction". I don't think you've read it
Eric
+7  A: 

If you read JavaScript The Good Parts, you'll see that Crockford's replacement for i++ in a for loop is i+=1 (not i=i+1). That's pretty clean and readable, and is less likely to morph into something "tricky."

Crockford made disallowing autoincrement and autodecrement an option in jsLint. You choose whether to follow the advice or not.

My own personal rule is to not do anything combined with autoincrement or autodecrement.

I've learned from years of experience in C that I don't get buffer overruns (or array index out of bounds) if I keep use of it simple. But I've discovered that I do get buffer overruns if I fall into the "excessively tricky" practice of doing other things in the same statement.

So, for my own rules, the use of i++ as the increment in a for loop is fine.

Nosredna
Excellent point about the "plusplus" option being just that, an option. Based on yours and other answers I think I'm getting the sense that ++ by itself or a for-loop are not in and of themselves confusing. The second you combine that ++ into another statement you r statement becomes more difficult to read and understand in context.
artlung
everyone gets buffer overflows. Even OpenSSH with their open source and their multiple comitees of revision
Eric
A: 

I don't know if this was part of his reasoning, but if you use a poorly-written minification program, it could turn x++ + y into x+++y. But then again, a poorly written tool can wreak all kinds of havoc.

James M.
x++ + y hahahaha who would write something like that?
Stephan Kristyn
+2  A: 

I've been watching Douglas Crockford's video on this and his explanation for not using increment and decrement is that 1. Is has been used in the past in other languages to break the bounds of arrays and cause all manners of badness and 2. That it is more confusing and inexperienced JS developers don't know exactly what it does.

Firstly arrays in JavaScript are dynamically sized and so, forgive me if I'm wrong, it is not possible to break the bounds of an array and access data that shouldn't be accessed using this method in JavaScript.

Secondly, should we avoid things that are complicated, surely the problem is not that we have this facility but the problem is that there are developers out there that claim to do JavaScript but don't know how these operators work?? It is simple enough. value++, give me the current value and after the expression add one to it, ++value, increment the value before giving me it.

Expressions like a ++ + ++ b, are simple to work out if you just remember the above.

var a = 1, b = 1, c;
c = a ++ + ++ b;
// c = 1 + 2 = 3; 
// a = 2 (equals two after the expression is finished);
// b = 2;

I suppose you've just got to remember who has to read through the code, if you have a team that knows JS inside out then you don't need to worry. If not then comment it, write it differently, etc. Do what you got to do. I don't think increment and decrement is inherently bad or bug generating, or vulnerability creating, maybe just less readable depending on your audience.

Btw, I think Douglas Crockford is a legend anyway, but I think he's caused a lot of scare over an operator that didn't deserve it.

I live to be proven wrong though...

Stuie Wakefield
+2  A: 

I think programmers should be competent in the language they are using; use it clearly; and use it well. I don't think they should artificially cripple the language they are using. I speak from experience. I once worked literally next door to a Cobol shop where they didn't use ELSE 'because it was too complicated'. Reductio ad absurdam.

EJP