PHP doesn't need 'finally'
bugs.php.net
bugs.php.net
It's extremely common to write code like:
sub work_in_tmpdir(&) {
my $cwd = cwd;
my $guard = guard { chdir $cwd };
chdir mktmp;
$_[0]->();
}
Then you can safely work in a temporary directory: print cwd; # /home/jon
work_in_tmpdir { print cwd; die "OH NOES" }; # /tmp/3sdjkh387dh
print cwd; # /home/jon
If this doesn't look enough like Java, that's easy to fix: sub try(&;@) {
my ($code, %args) = @_;
my $guard;
$guard = guard { $args{finally}->() }
if exists $args{finally};
my $result = eval { $code->() };
if($args{catch} && !$result){
$args{catch}->($@);
}
return $result;
}
catch(&;@) {
return catch => @_;
}
finally(&) {
return finally => $_[0];
}
(+) Now you can say: try { something_exceptional }
catch { warn "Something bad happened: $_[0] }
finally { cleanup };
No language feature or bug tracker rant-fest required! And, I'm pretty sure that you can abuse destructors in PHP to get exactly the same result. (In Perl, that "guard" function from the Guard module is implemented something like: class Foo { has 'cleanup' => (is => 'ro', isa => CodeRef ); sub DESTROY { $self->cleanup->() } }; sub guard(&){ return Foo->new( cleanup => $_[0] ) })(+) Also, don't cut-n-paste this code into your apps. It misses special cases. use Try::Tiny, TryCatch, etc. from the CPAN.
The reason you can't compile it is because guard is not defined, nor are cwd, cleanup, and the other placeholder functions I used. When you are relying on the parser to add parens for you (and let you omit "sub"), you have to have the function in scope. Compare:
use strict;
sub foo { bar }
And use strict;
sub bar;
sub foo { bar };Those are designed, documented, and tested features; they exist to do exactly this.
... it is wrong design. Since obviously you are using the exceptions as control flow.
What convoluted understanding of "control flow" makes an exception unworthy of use as control flow?Exceptions aren't bugs, they're simply exceptional conditions. If you use them right they pose no problem but serve to make the code more legible.
( ...and I don't have anything against ruby. I'm learning rails. www.railsforzombies.com is great. In fact, my php is made on cakephp and lithium, which are pretty similar to rails... )
Well, yes and no. Correctly writing your if flags to be polymorphic on the type of the exceptions is certainly possible but you're getting into the domain of things that a compiler really ought to be doing for you.
I've successfully made use of polymorphic exceptions, but I will freely concede I seem to be in the minority. Also I love having them being objects because then I also load the logic into them for presentation both to the human user and the developer which turns out to be really useful but also apparently a minority idea. Adding that to your if/else blocks would be insane.
Frankly, exceptions are badly underutilized, but one could say that about quite a lot of OO concepts, really. Many people understand the surface forms but manage to miss the point entirely. If that's all you've ever seen for exception handling I can understand why you wouldn't think it's very useful.
try{ some ifs ... return x ... more ifs ... throw ... return y }catch{ handle exceptions }finally{ do_something_finally() }
and
try{ some ifs ... return x ... more ifs ... throw ... return y }catch{ handle exceptions } do_something_finally()
I'm missing something, I'm not?
try {
throw new IOException("bad");
} catch ( NotAnIOException e) {
System.out.println("This is something I expected to happen.");
}
do_something_finally();
With a finally block, it would be, and the IOException would then be bubbled up to the caller. Without finally, you need to do this: try {
throw new IOException("bad");
} catch ( NotAnIOException e) {
System.out.println("This is something I expected to happen.");
} catch (Exception e) {
do_something_finally();
throw e;
}
do_something_finally();1. the try block returns
2. the catch block doesn't handle /all/ types of exceptions
The point of finally is that it's always, always, always executed.
Except when it's not. If you design a large system with the assumption that finally blocks always execute, you could end up with some data integrity issues when you get a power outage, an exception in your finally clause, a hung machine, or any other number of errors that a finally clause does nothing to address.
The finally clause is very useful, but to say that it will always (x3!) execute is somewhat perilous.
This seems to be an example where the hack is preferred over the correct solution. The hack in this case is going to work almost all of the time.
I fail to see how throwing up an example of a hack is a good way to argue for or against a language feature. Generally speaking if the language is decent enough there is always a way to hack in some new semantics yourself (I'm looking at you, anonymous inner Java classes for closures), but language features allow you to compose and represent those semantics more elegantly. So, the argument shouldn't be "can we do this already with a hack" it should be "is this hack common enough that we should fix it."
The point the author is making is almost self-evidentally against his own conclusion: here's a common pattern that is broken, so we should not include this as a language feature???
Note, this does seem to be the case for javascript:
With finally http://jsfiddle.net/J5Cjt/
Without finally http://jsfiddle.net/C2vg7/
If an error occurs before "return y", both will behave equally.
Basically the finally-clause is guaranteed to be run always. This means that you can put your resource-cleanup logic there and know it will be invoked if errors occur or not, without the need to duplicate that logic within the catch-block.
Especially when the catch-block is set to rethrow (or just outright omitted) this saves the programmer a lot of time, code-duplication and makes the code more readable.
The reasons given for omitting it by the PHP team is factually incorrect and shows that they clearly don't understand how the feature is supposed to work. With that hindsight, it would be interesting to see how it would have been implemented if they had gone forward adding it instead of saying "no". It could have become a highly fascinating monster.
In fact, now with PHP 5.3 and closures one could emulate finally quite easily: just create an object that runs the closure on destruction and it will be called at the end of the block.
Finally would still, however, be a welcome addition to the language.
Using transactions would make more sense:
try {
mysql_query("START TRANSACTION");
// ... do lots of queries here
mysql_query("COMMIT");
} catch(Exception $e) {
mysql_query("ROLLBACK");
}If helly wants to say "there was a discussion about this on the internals list, and the decision was no" he doesn't have to justify anything more.
If they still care enough, they can dig up the internals thread and re-spark discussion.
And saying this will probably get me the flood of down votes, but this has always struck me as being something that is a language "requirement" only in people who have an itch in a hard-to-reach place in the engineering department of their brain - no offense meant. They need it for their sense of order, not for their software.
C++ doesn't need finally because it has deterministic destructors, which are used to emulate finally clauses. In my opinion, destructors can cause less readable/predictable code than a proper finally block, but that might be matter of taste.
Comment from the language maintainer: The only difference is the second example does rethrow the exception. Though this is still possible (however much more to type) it is wrong design. Since obviously you are using the exceptions as control flow.
This seems quite strange. Finally is a construct to manage control flow, and it's essential to control flow during error conditions. He seems to prefer just swallowing a (potentially critical) exception. Either I'm misreading him, or I think he fails to understand the point of either finally, or exception handling in general.
I think this gets to the heart of what feels 'off' about PHP to me: this kind of passive-aggressiveness against "doing the right thing" seems built-in. Kind of like how in order to avoid XSS, PHP offers htmlspecialchars(), or to avoid SQL injection, mysql_real_escape_string() (I'm aware of PDO, but it seems few if any major projects use it).
PHP has deterministic destructors as well. Using them is how I get around the lack of finally -- destructors do the clean up.
My point is that object lifetime <> external resource usage duration in all cases.
As soon as there are no more references to it. PHP is reference counted (with cycle detection). As soon as the variable containing an object goes out of scope, the destructor will be called.
The fact that Java/C# destructors aren't called immediately (and may not be called at all) is the primary reason that Java has the finally block (C++ doesn't) and why C# also has the using statement.
No one is having problems with this in C because C doesn't have exceptions.
No one is having problems with this in C++ because C++ supports RAII via destructors which are guaranteed to run on stack unwind.