|Problems? Is your data what you think it is?|
In quantum physics, watching something happen can change its behavior. In particular, a single particle going through two slits interferes with itself - unless you measure which slit it goes through, which destroys the interference.
Perl's ++ operator seems to behave like this, when the "watching" is done with a subroutine call.
Consider the output of
What's going on here?
If you'd like to see how arcane your knowledge of Perl really is, try guessing what this prints without reading ahead or evaluating the code.
For those with way too much time on their hands, this document and the code samples it refers to can be found at at http://cs.marlboro.edu/talks/increment_weirdness/.
Before going into any detail, I'd like to make it clear that this is an academic exercise only.
Steve Oualline has a nice description of the use of increment operators and their side effects in his book "Practical C++ Programming", pg 79. Essentially he says that if you want to understand how these tricky increment expressions work, the right answer should really be
"If you don't write code like this, then you don't have to worry about these sorts of questions."
Even though clearly none of *us* would write code like this (ahem), a friend (Mark Francillon) gave me some expressions like (++$m + $m++) as a puzzlers, and I was curious enough to look into it.
To really understand the pre and post increment ops, I wrote my own preInc and postInc subroutines, doing what I *thought* these operators were supposed to do.
This all makes perfect sense to me. What's going on here is
If this was the whole story then I wouldn't be writing all this down. However, the value returned by the Perl interpreter is *not* 42, but 43. If you don't believe me, try it for yourself.
And then I started pulling out my hair.
look()-ing at intermediate results
My first attempt at understanding what was going on was to write a subroutine that would examine the intermediate results. You can find all the gory minutia in increment_detail.pl.
But that's where the quantum weirdness popped up.
After a variety of attempts it became clear that any subroutine call wrapped around (++$m) changes the result of the calculation to 42.
So if I tried to watch it do this weird thing, it wouldn't do it.
By now this felt like a conspiracy.
overload-ing to look without touching
Another friend (Brandt Kurowski) suggested using operator overloading to watch the intermediate steps, without disturbing the calculation. This works, and has helped me understand what's going on, but hasn't quite answered all my questions.
See IncrementOverload.pm for that analysis.
The discussion on pg 357 and thereabouts of the Camel describes some of the inner workings of the increment operators; in particular, if there's more than one pointer to something then it makes a copy first and increments the copy. Thus to overload ++, you must also overload the copy operator. In the listing above, the "inc", "copy" and "add" lines are printed out by overloaded subroutines, all invoked while evaluating ++$m + $m++.
Here's a blow by blow account.
The calculation starts out as I'd expect, with ++$m incrementing $m (..23c) in place.Staring at all this long enough gives the gist of *how* Perl gets 43 : ++$m evaluates to $m itself, which is incremented *again* by $m++. So by the time the addition operation actually needs a value for the term on the left, its value is 22.
There are more comments at the end of Increment.pm, along with printouts for some other (perhaps illuminating) variations.
After all this analysis on *how* this works, I'm still left with one big question.
Why is 43 the right answer?Clearly Perl's answer must be the right one, and so I'm sure there's some really good reason why the operators *should* behave this way in this context - I just can't quite get my head around what that reason might be. :)
Anyway, it was fun trying to look a bit under the hood.