Another reason Perl is crappy. Really crappy. REALLY REALLY crappy.

I’ve been using Perl professionally for about four years now. I never really loved the language, but I certainly didn’t hate it quite so much before this job.

There are many obvious reasons to hate Perl: * The hacked-in OOP never impressed me much, but gets really old after working on a very large application for a while. * Exporter is evil – being able to pollute somebody else’s namespace just by their use of your module is truly horrific. Most modules are smart enough to only export things as requested, such as use Foo qw|cool_function|; or use Foo qw|:cool_functions|;. But the fact that you can choose to have your module muck up others’ namespaces is terrible. And the fact that there’s nothing akin to the C++ using namespace Foo; in Perl makes it highly annoying to do things the proper way and not pollute your namespace. * The lack of real exception handling is amazing. Look at how the Error module works sometime if you want to be disgusted. The way it works is so awful, and has some really nasty side effects if you’re not careful (like not being able to return from a try “block” because it’s really a function and not just a code block — or how a missing semicolon generates no errors, but can do some incredibly strange things).

But today a more insidious problem struck. I wanted to make a mock class based on the BerkeleyDB class. When we write a unit test, we need to test that our program logic works, not that the BDB class works, right? So I’ll just write a mock, right?

WRONG! The method we use the most is $bdb->db_get($key, $value), and due to the magic of XS bound to C, the second parameter, $value, gets set to the given key’s value. In a normal Perl function, you must send a value by reference in order to have it changed by a function: normal_method($key, \$value). But we can’t change the calling convention for this mock class to work – the point of the mock is to make it a drop-in replacement used just during testing!

So I looked into method prototypes to fix this for me. A method prototype in Perl is a powerful thing that can allow you to “convert” a normal argument into a reference, such that my mock BDB class would be able to keep the same calling conventions as the real one (which is kind of essential for a mock class). So I did something like this:

# db_get takes two or three arguments - the second is by-reference
sub db_get($\$;$) {
    my ($self, $key, $value, $flags) = @_;
    $value = $self->{$key};
    return 0;
}

Beautiful solution, considering how annoying Perl can be. Just one little problem: class methods can not use prototypes. There is no warning, no errors, no indication of what’s wrong whatsoever. The prototype is simply ignored. Silently. After reading the documentation a while, a coworker was able to point out the problem, and I was forced to use the real BDB class instead of a mock in order to complete my unit testing.

I could, of course, write some XS and C in order to get a mock class built, but that’s just an absurd solution to the problem.

I could also write a wrapper and retrofit all our current code to use the wrapper, such that I have complete control of the new API. The new API for the real and mock wrappers could easily be kept in sync. This, however, is even more absurd a solution, because it requires a lot of code to be changed.

That the inconsistent nature of Perl causes such headaches is just one more reason that Perl sucks. A whole lot.


Now to be fair, Perl wasn’t really meant for large-scale applications when first built. But instead of fixing things the proper way, new features seem to be done in a very band-aid sort of way, instead of fixed properly. Perl 6 will deal with a lot of this, but I suspect the conversion won’t be worth it, considering how many better options there are nowadays.

Leave a Reply

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.