Years ago I coined the phrase “Perlaphobia”, the fear that Perl will come back into my life again and again, and every time it happens I have to relearn it all over again almost from scratch.
Still happens about every five years, although I am reaching for Python more and more in those situations.
What always kills me is the weirdness of the basic data structures, array/stack function calls, and the truly impressively weird ways Objects are done.
> What always kills me is the weirdness of the basic data structures,
> array/stack function calls, and the truly impressively weird ways
> Objects are done.
Well, Perl 5 has scalars, arrays and dictionaries -- just like Python. Unlike Python, however, Perl function arguments are always implicit/variadic by default (and stored inside the implicit "@_" variable). This can be a benefit or a curse depending on the context. Furthermore, Perl 5 does support both "call by value" and "call by reference". This distinction can confuse programmers coming from more modern languages where usually everything is a scalar reference. But you have to keep in mind Perl was invented in the 80s and put great emphasis on compatibility. Changing calling conventions would have broken many modules.
edit:
Regarding objects: The object system is rather simple, it's just many Perl hackers invented their own object systems on top of that. Basically, a Perl class is just a module. An object is just a reference to a dictionary that got the class name attached by applying the "bless" operator:
package Foo;
sub new {
my ($class) = @_;
my $object = {};
bless $object, $class;
}
sub do_stuff {
my ($self) = @_;
# do stuff
return $self;
}
my $obj = Foo->new();
$obj->do_stuff()->do_stuff();
That's about it! Well, you also have inheritance using "use base ..." and you can bless other fundamental types which usually is not a bright idea. Many other object oriented languages make object orientation unnecessarily much more complex (looking at you C++).
I worked briefly at a shop that maintained a complex outsourced project by Yahoo (some ad bidding optimizations pipeline).
As it turns out, they used Perl, one of the few sanctioned languages at Yahoo.
The coding style handed over from the client forbade every other thing that Perl fans tell you that make the language warm and endearing to them; you were allowed to use about a few of the sigil variables, all the data structures were to be used in their "object reference" form, because it sidestepped the "contexts" functionality of Perl, which was to be avoided at all costs.
I remember the tedium of having to maintain your own stacktrace, because all you got was a the `$@`, `$!` and `$?` variables, which contained precisely the last of various types of error encountered during execution within the process. If you encountered errors while performing error handling, the original error would be lost, so you had to keep a backup.
Edit: man, the contexts, the fans sweared by them, making analogies to natural language; I'm sorry to sound elitist, but they often came across as enthusiastic amateurs rather than people with a formal background in Computer Science. They claimed that things like passing an array to a place where an integer is expected to get the length was richness, not insanity.
> I remember the tedium of having to maintain your own stacktrace
With Carp::confess you get the stack trace easily when throwing an exception. It's been a core module for ages.
Of course, Perl lacks a flexible condition system like Common Lisp. So all you got is an exception object you can try to handle but no restart handlers you can use to go back down the stack trace. But that's a common limitation of almost all languages supporting exceptions natively.
I worked at Yahoo, and it was Perl and PHP. Sheer madness.
Their were some talented folks but I'd like them more to cowboys - git'r done instead disciplined engineers.
Lots of production applications that had the code quality of your typical hacky side project.
I once had a debate with my manager about classes/OOP in PHP5. He didn't like it because it usually meant having separate files and includes per class, and he likes to have all code for a PHP page in a single pane in Vim. "But Kenny it's all in one file, everything I need to work on is right here!"
If a Perl script is written and maintained by just one person, or a few people who can agree with each other, then features like that are not a problem: you just agree not to use them. They are, however, an argument for not using Perl for a big project that lots of people will work on.
That trick "works" in Java because, for compatibility reasons (Java didn't have generics originally), the generic type is erased by the compiler. Your example compiles to:
static List listOfAnything() { return (List) new ArrayList(); }
List stringList = listOfAnything();
List listOfInts() { return listOfAnything(); }
The generic type isn't monomorphised or even reified; within your method, you don't have access to the generic type T "passed" by the caller. If you need it, you have to pass it explicitly, as commonly used by deserialization libraries:
static <T> T deserialize(String input, Class<T> cls) { ... }
Which can be used like this:
MyStruct result = deserialize(input, MyStruct.class);
Note that the deserialization library has no way of knowing the type of the variable the caller is putting the result in, the caller had to pass it explicitly. This is because what the Java compiler actually generated from the code above was something like:
static Object deserialize(String input, Class cls) { ... }
MyStruct result = (MyStruct) deserialize(input, MyStruct.class);
It's possible for the deserialization library to know the type from the payload itself, but of course it has to "happen" to coincide with what's being cast to. You don't need to pass the Class object at all.
Generics are awesome. They allow you to do partial typing. You don't specify the exact type but you constrain the output type based on the input type. So you have functions that can operate on many types but still have strong typing.
Wantarray is not a generic implementation. It's closer to reflection. You can detect the type of variable the caller is putting your functions result in. Then you can branch your program based on the variable type that the caller wants. I guess this is similar to type inference but not exactly as it's not part of the type system. For example, if they want an array return the amounts of each transaction in an array if not return the sum of the transaction amounts.
And that well-studied for of polymorphism is entirely unlike having the callee knowing if the caller expects an array or an it, and changing the result.
wantarray() is a sensing operation that allows a type of transparent function overloading: Oh you wanted just a scalar? OK.
Python's ordered arguments mixed with out of order labeled arguments is worse IMO: Tell me arguments in this order. Or not... I mean, you could just tell me which they are. Wait, I got two of these... F*ck this, I quit
(1) Un-named arguments come before any named arguments and should be passed in order (this is familiar to programmers in almost any mainstream language).
(2) Named arguments can then follow, in whatever order.
Yes, they not complicated, and I imagine it's a non issue for most.
The interface is awkward however in that in other languages you would overload the function or pass a data structure, or reference of one, to manage additional optional parameters when you didn't want the extra arguments explicitly listed.
Python is a popular language and there are a ton of contributors who have developed third party packages. Unfortunately, these are not always well written or maintained and it can be easy to stumble across a bad one when looking for useful things. It's never fun to debug code in an unfamiliar language.
Yes but if you want to know an arrays length you can just put it in a scalar. So you consume some API and want to get the length of the result. You put the array result into a scalar variable. Then what this is not the length?
The API documentation should reflect that the result is context sensitive. Not reading that is a good way to get burned despite the language.
Perl is well known for it's multitude of idiosyncrasies, this is one of the reasons why it can be frustrating to programmers who have better experience in more modern or rigidly structured languages.
That's actually a good point. It seems to work well in that context. I'm not sure I have an entirely rational reason for liking it in one context and not the other.
As a Perl developer, I never once used that feature. It does make some sense in the context of how Perl's own operators work, making them "do the right thing" for different cases. But for user code... not needed.
I adopted the contextual thinking fairly easily so it was not an issue for me but having worked with so many languages since perl I can see why this would be annoying to others. It's not the most unfortunate aspect though.
It's not so bad when you know what is going on but when you first hit something like that it's hard to figure out what is going on. Basically violating the Principle of least astonishment
PHP's string datatype (in the C code) had a length field in addition to the actual bytes (probably still has), so the idea is not always bad (if most of what you do is string manipulation, so len() is O(1) and not O(n)) but I get your point, that's hidden deep in the language for perf reasons, not in user code :P
Still happens about every five years, although I am reaching for Python more and more in those situations.
What always kills me is the weirdness of the basic data structures, array/stack function calls, and the truly impressively weird ways Objects are done.