I'd make it more general than that: If, in an expression, you use an operation that changes ("writes" to) some resource, then you should not use that same resource anywhere else in that expression (directly or indirectly).
$i++ changes $i so don't use $i anywhere else in an expression that uses $i++ (including either side of the assignment operator). If you need to use $i more than once in an expression, then use $i+1 not $i++ nor ++$i and do the assignment / increment of $i's value outside of the expression.
Another example is that <IN> . <IN> depends on order of evaluation. Each read changes the state of the file handle, impacting what value will be read from it in future.
Then the next concept is what separates expressions in time. In C, they use the term "sequence points" to describe the points in time across which operations will not move. In Perl, ";" and "," are supposed to create sequence points. But Perl also creates aliases so your example using "," runs into problems because the alias is created in a defined order before a change is made and then a value is pulled from the alias after the change.
When you get an alias vs. a copy can be quite subtle in Perl. So you shouldn't rely on "," being a sequence point for things that might get aliased (which includes variables and even ++$i, at least in some implementations of Perl).
Of course, all of this gets rather complicated and isn't obvious so if you'd tried to predefine order of operations you'd probably have been royally screwed when you ran across this wrinkle and then been forced to make Perl make copies in a lot of cases where it doesn't now, making Perl slower.
| [reply] [d/l] |
Thanks for attempting to answer my questions.
However, I still see some outstanding issues.
- Tied variables.
Any tied variable used twice in a single expression could have sideeffects.
As the user of a variable returned by a library call, I have no idea whether the value I am manipulating is tied or not.
If it is tied, it could have dependancy issues.
That means that the only way for me to avoid the possibility of EOD, is to never use the same variable twice in a single expression.
Even something as innocent as:
my( $x, $y ) = ( $a, $a );
can produce totally different results depending upon the order execution if $a is a tied variable.
- Object references.
Using an object reference twice in the same expression could involve EOD.
Again, with EO undefined, the only recourse for teh programmer is to never use an object reference twice in a single expression.
Again, a silly simple example:
my( $x, $y ) = ( $o->next, $o->next );
Should I have to code that as:
my $x = $o->next;
my $y = $o->next;
to ensure I get the effect I want?
- Closures.
A library function may use closures. Calling the function twice in the same expression could involve EODs.
Again, the only answer to guarentee that the function will be called in the correct order, is to call it once per expression.
And the same silly example:
my( $x, $y ) = ( func(), func() );
It doesn't matter what func()> does, if there is no guarentee that the two calls to it will be executed in some order, I cannot know what the outcome of that expression will be.
All three of the restrictions make a mockery of Perl's best feature--that it is a Very High Level Language that allows me to do a lot in each line or statement.
It means that without diving into the guts of every function, object and module that I use to discover exactly how the code is implemented, I can never use two occurances of a single variable in a single expression. One line will have to exactly one, very low level step and one step only. I might as well write in assembler.
But the really, really gaulling thing about this, that I've pointed out over and over, and the other anonymonk has chosen to completely ignore, is that the very same set of reasons above, that impose such a burden on writing code that codes a single high-level, algorithmic step per line, are the very same reasons that the Perl cannot possibly derive the slightest efficiency benefit from undefinedness of EO.
So, arguing about whether it is possible to avoid EOD expressions becomes a complete waste of time, when you realise that there is no good reason not to define an execution order.
- It isn't hard to do.
- It doesn't prevent any efficiency gains.
But if it were defined, it would allow Perl to realise it's potential as a VHLL even further. I believe this to be true for Perl 5, but I realise that it is much too late for that now.
But for Perl 6, the possibility not only exists, with real benefits for the programmer as an outcome, I believe, as far as I understand Perl 6, that it almost becomes a necessity. Once you consider the potential pifalls of undefined EO when combined with things like hyperoperators, junctions, on-the-fly roles, the ability to create temporary, single instance new classes whose lives extend for a single expression only and that's before you even begin to consider the possibilities that got me interested in the first place, namely threading.
It simply isn't enough to say "Don't write expressions that could have EODs", because already, it is near impossible to know when even a single, apparently non-lvalue expression could involve sideeffects. And that is even more true of the very complex syntax that will come with Perl 6.
Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
Lingua non convalesco, consenesco et abolesco.
Rule 1 has a caveat! -- Who broke the cabal?
| [reply] [d/l] [select] |
You've just rediscovered the fact that shared-state concurrency is a nasty mess. Humans aren't smart enough to use it correctly in anything but the most controlled conditions. Declarative concurrency, on the other hand, is possible to understand. Check into Concepts, Techniques, and Models of Computer Programming. They've also got a wiki
| [reply] |
| [reply] [d/l] |
It simply isn't enough to say "Don't write expression that could have EODs, because already, it is near impossible to know when even a single, apparently non-lvalue expression could involve sideeffects.
But this is one of the hard problems because of Turing-Completeness/Halting-Problem. What we'd really like to do is outlaw those expressions in the first place. But if the compiler was smart enough to recognize all of those instances, then there are also programs that would cause the compiler to never finish executing :-(
And, if you explictly define an order of evaluation for everything, then of course you won't gain anything through parallelization (because everything has to be executed sequentially, by definition).
You should really check into Haskell, no side-effects to screw up things.
| [reply] |