The tone of this writeup isn't meant to be negative. I think we can benefit from acknowledging the flaws of the programming languages we like, especially the really popular ones, so we can ensure those flaws never make their way into other languages.
- No Object Inheritance.
Inheritance in Perl is implemented via the @ISA array (pronounced "is a", as in "this is a problem"), which allows you to call methods from one package on a reference blessed into another package. For example, if SomeClass has SomeOtherClass in its global @ISA array, then you can call any method from SomeOtherClass directly on references blessed into SomeClass.
Unfortunately, there's no real way to inherit objects from other classes in Perl, only their methods. The workaround commonly used is, in the constructor method of the class that wants to inherit, call the constructor of the class you want to inherit from to get the object, add your own hash key/value pairs to it as needed, then bless the object into your class:
package SomeClass;
use SomeOtherClass;
@ISA = 'SomeOtherClass';
sub new {
my $class = shift;
# create a new SomeOtherClass object:
my $self = $class->SUPER::new;
# mess with it:
$self->{'_something'} = 1;
$self->{'_something_else'} = 2;
# now bless it into SomeClass:
bless($self, $class);
return $self;
}
1;
The problem with this is that it violates the laws of encapsulation and abstraction: you need to know the internals of the object you're inheriting and then you go right ahead and mess with its members directly. You can use containment or declare global variables in one package, write routines to manipulate those variables, then inherit those routines from that class... but still.
With the myriad of object-oriented modules on CPAN it's easy to forget that Perl was originally designed as a purely procedural programming language. Things like this make those roots painfully clear; OO in Perl is really nothing more than blessed references and a piece of syntactic sugar.
- Reference Counting.
Perl uses reference counting for garbage collection, a simple form of memory management that ensures timely destruction of discarded resources. Every scalar, array, hash, whatever has a reference count in it that starts out at one. Every time that variable is referenced, its reference count is incremented. When the reference is removed, the count is decremented. Eventually, when the last reference is exhausted, and the count reaches zero, and the memory used by the variable is reclaimed.
The problem arises when you have two variables that refer to one another, like a parent hash and child hash that both contain references to each another. Since neither one's count can be decrement to zero until the final reference is removed, one can't be freed until the other is, and they both end up just hanging around taking up space. This is what's known as a "circular reference." (See this for more information: http://www.perl.com/pub/a/2002/08/07/proxyobject.html)
This means that it's surprisingly easy to leak memory in Perl. Many budding Perl hackers do this by accident. They think, Gee, wouldn't it be useful for this object to contain a reference to its parent, and vice versa, and viola, memory leak.
Perl is certainly not alone in going with reference counting. VB used it, Python still uses it, and so does PHP.
Maybe the biggest problem with reference counting is the burden it places on extension writers. It forces them to scatter calls to SvREFCNT_inc() and SvREFCNT_dec() all throughout their code, making sure each SvREFCNT_inc() has a corresponding SvREFCNT_dec(). Which brings me to my next gripe...
- Unintuitive API.
The Perl C API is interesting. First, there's no visible naming convention. Some routine names are mixed case, like newSViv(), while others have underscores in them, like av_push(), while others still have some odd combination, like newRV_noinc(). Many variables and structure members have terse, sometimes misleading names, like "cur" for length and "len" for size.
The API is also full of macros, many of which aren't documented in perlapi or any other man page, like HvKEYS().
And to make things even more fun, for the functions that actually are documented it often doesn't say whether or not they modify the ref count of their arguments.
- Unintuitive Behavior of Arrays/Lists in Scalar Context.
I really don't like having to write code with more parentheses like this:
my ($first_field) = split(/\t/, $tab_delimited_fields);
to stop a list or array returned from some function from behaving badly.
In Perl, arrays return their length when used in scalar context and list literals (e.g., "(70, 80, 90)" in source code) return their last element. Ok, first, why on earth is there any difference between an array and a list literal to begin with? Second, when and why would I ever put a list literal in scalar context just to get its last element?
I think it would have been far better if both arrays and list literals behaved the exact same way and returned their first element when called in scalar context instead of their length or last element. Then you could write code like this:
my $email_address = $input =~ /(\S+\@\S+)/;
and it would work.
How, you ask, would one get the length of an array then? Well, why not with the length() function? Many newbies assume it does that anyway.
- Formats.
Formats in Perl were supposedly the "Report" part of Practical Extraction and Report Language--of course "Perl" isn't an acronym anymore and when, honestly, can you last remember using formats? In fact, off the top of your head, can you even remember how to use formats correctly?
This large, largely ignored part of Perl sucks for a variety of reasons.
First, the format definition syntax itself is clunky (how do about 20 or so "<" characters back-to-back sound?), global (you have to stick it in a big, period terminated block somewhere--probably at the bottom of your program), and completely different from normal Perl syntax (making you much more likely to forget it, of course, especially since you never use it).
Second, doing anything significant with formats results in verbosity, often requiring use of the select() function and playing around with $^ before calling write(), forcing you to use three statements to accomplish what should really only take one. (Four statements if you count resetting select().)
Third, anything a format can do, printf() and its sprintf() counterpart can usually do too. Even though the format syntax is arguably more flexible than printf(), using multiple printf()s usually gets the job done, and in a shorter, clearer way, no less.
Perhaps the worst part of all of this is that write() is stuck printing nonexistent formats instead of doing fixed-length IO like read(), its English language opposite:
"Note that write is *not* the opposite of 'read'. Unfortunately"--(perldoc -f write).
- No Constants or Macros.
This could really be two separate gripes, but Perl has no simple way to declare variables as constants or define macros as alternative to scattering magic numbers throughout source code.
Yes, Perl has a "constant" pragma that purports to do both, but that's really just a hack; a clever, elegant hack, but a hack nevertheless:
use constant PORT => 80;
PORT can now be used as an rvalue in an assignment as though it were some sort of constant, and any attempt to assign something to it will result in a fatal error. But you see, PORT really isn't a constant, it's just a subroutine prototyped to take no arguments. You can still overwrite it using a sub declaration or with "use sub" or by messing around with the symbol table directly. More importantly, one purpose of constants is efficiency; making things easier on the compiler by promissing not to assign something to a variable again. Here, rather than any meager benefit, perl, the interpreter, takes an obscene performance hit.
- No Type Information.
In Perl, scalars can contain either stuff or references to stuff. Unfortunately, Perl provides no operators to find out what kind of "stuff" a scalar contains, only what kind of references. This makes, for example, finding out if a scalar contains a numeric value suitable for performing arithmetic on surprisingly difficult. Yes, I understand we Perl hackers are to think of numbers and strings as being interchangeable, but it comes up, and it's absurd to have to resort to using the regex engine to find out:
print "Not a number!" unless ($thing =~ /^\d+$/);
Of course, the above pattern match doesn't really work, as numbers can contain other characters like "+", "-", or "." to denote sign, fractional parts, or exponents.
Even the ctype.h functions would be more helpful than nothing.
This is all the more annoying because Perl apparently has a pretty good idea of what type of value an SV (a C typedef used for scalar values) holds based on its "flags" member, and can easily convert from one to the other as needed.
- Autovivification.
Hash key/value pairs autovivify in Perl. That means you can specify a new key like "autovived" and it pops into existance:
my %hash = (key1 => 'value1', key2 => 'value2');
$hash{autovived} = 1;
That also means you can mistype a key and it will pop into existence too, without so much as a warning. This isn't a big problem when you use hashes as dictionaries because your keys often come from somewhere else, but when you use a hash as a structure or object, it causes problems:
$self = {
name => undef,
age => undef
...
sub name {
my $self = shift;
$self->{Name} = shift if @_;
return $self->{name};
}
When I use hashes as objects, I always define and initialize each element of the hash inside of the constructor method to something, "undef" if nothing else. What I wouldn't give for a "static" keyword to prevent new elements from being added to the hash after it's initialized:
static my $self = {
name => undef,
age => undef
...
$self->{Name} = shift; # fatal error
This 16-year-old language has been steadily putting on weight for some time. Many aspects of it have become unintuitive, inefficient, or just plain ugly. The internals are unmanageable, and adding new features without breaking old ones is becoming exceedingly difficult
This top-to-bottom rewrite of the language is still a long way from being finished, but I, along with many others, eagerly await it. Until then, I guess I'll keep using Perl 5 to get most of my work done.