Perl Already Won
programming.tudorconstantin.com
programming.tudorconstantin.com
The most common complaints about Perl tend to be about the code of people who don't know how to write Perl. Since when is it reasonable to judge a language on how the amateurs use it, instead of the professionals?
Perl does have its warts, but no language is an exception to that. When you learn to use a language well, the warts no longer matter. You get into the habit of writing code in a way that doesn't brush up against them, and that holds true for every language out there.
To clarify any mention of Perl 6, you need to realize Perl 6 isn't Perl. It is a whole different language with a really bad name. I know this has caused great confusion, but Perl 5 is extremely stable, still being developed, and not going anywhere. You don't need your Perl 5 programs to run in a Perl 6 interpreter any more than you would need them to run in a Python interpreter.
I have written my share of code in other languages, but I always return to Perl. As a C and Unix programmer, it feels like a very natural fit in ways that other languages completely do not. It has amazing string parsing, a superb (though extremely misunderstood) OO system, tons of great libraries, direct interfaces to POSIX and other system interfaces, really good database interfaces, networking, and protocol wrappers, very flexible interfaces to native code, and a really brilliant and well evolved design.
While you should always use the right tool for the job, Perl is a better choice far more often than people are realizing these days. There is so much anti-Perl sentiment out there, and the great majority of it is completely misplaced and made by people who haven't properly learned how to use Perl.
In a field like mine (computational chemistry) where most of the programmers are expert chemists but amateur programmers, then it's very reasonable to judge a language choice based on the expected user base.
I expect the same holds for most other computational-based sciences, where it may take 4 years to have the domain knowledge to start programming, even though the programming itself does not require deep knowledge of the language or of computer science and can be picked up in a few months of self-study.
Well, if you want to judge the appropriateness of a language for a given task, you will have to consider how it is going to be used by the people actually using it.
Maybe some super-disciplined Perl-gurus can write beautiful Perl code, but since Perl actively encourage ugly and unmaintainable code, the average developer will produce better code in a language like say Python which encourages readable code.
You can write ugly code in any language, and you can write beautiful code in any language. But that definitively does not mean that all languages are equally good at supporting and encuraging clean and maintainable code!
It doesn't take a guru. Only anyone who actually cares about readability or learned from recent books like Ovid's Beginning Perl or chromatic's Modern Perl.
The main problem Perl has is not anything to do with its language. It's all the old books from the 90s (that some diehard still recommend), but which teach outright shit when it comes to readable or reliable code.
What are your thoughts on PHP and Visual Basic (pre- and post-.net)? I assume that, under experts, they can be excellent languages. But that's not the typical opinion either.
Then I happened upon some Python code I needed to fix. Within a week or less I already felt like I "knew" Python better than I would ever know Perl. It was fun and inspiring and felt so elegant and powerful! I never typed another line of Perl.
You should rephrase this because you can't de-reference an array. Plus a reference to an array is always a scalar so it always has the '$' sigil, thus it's not clear what you're talking about.
$scalarref = \$foo;
$arrayref = \@ARGV;
$hashref = \%ENV;
$coderef = \&handler;
$globref = \*foo;So you tell me the first solution is dumb and the other one is smart? I would say that exactly opposite is true.
Perl uses sigils. You can think of them like inflection in spoken language. They provide hints as to what is being referred to, to help make intent clear.
Perl uses references. In some respect, almost all languages use references, and it's just a matter of whether it's explicit or implicit. Perl makes it explicit, which means the programmer has to understand it early to make some things work (nested data structures). Other languages may have it as implicit, but that doesn't mean the programmer can ignore it, it just means they may be able to ignore it for a short while when first learning the language. Sooner or later they will encounter and have to deal with whether they are working with a copy of data or the original data. If they are lucky, this happens before it's a nasty bug.
Python looked like a weird toy language to me because it had such little syntax compared to Perl. Until I actually had to write some, that is, and I realized how much of Perl's syntax was superfluous and bug-inducing. For example, Perl kept C's distinction between values and references, so I always had to plan ahead about which one a function would accept and return. It was eye opening to realize there were comparable languages that allow me to not care about such things.
You keep believing that. It's not true, but keep believing it.
EDIT: What? Why was I marked down for thanking someone???
Assuming this is true and assuming a dev enters into production at 23 years old, it results that the average Perl developer has 12 years of software development experience. What kind of code would you prefer in your business critical, money making software products? one written by people with an average of 12 years experience in the language of choice"
Whoah, hang on there. Even if they did have an average of 12 years of experience, it hardly means that it's been Perl all this time.
Obviously the reasoning that led to this bit isn't rock solid, either ;)
"[...] or one written in a language that appeared on the radar in the last decade: Ruby on Rails, the framework that made Ruby popular was launched in late 2005"
2005, that's 9 years of experience at best. How lame in compare to 12 :)
(Don't trust Android devs, Android didn't come out until autumn 2008)
(Very rough Paraphrase).. It sucks in this business when our knowledge base starts becoming obsolete, and we are sometimes tempted to think of reasons we don't have to learn new things, and also why no one else should either.....
Android isn't a language but it's the kind of heavily specialized environment that developers will put on their resume and companies will hire for.
Indeed, there is no reason to assume that RoR developers who have been in the business for >=9 years were Ruby devs since the day Rails was released, but I can't see how it is any different in case of Perl.
I just accepted your logic (which I believe to be flawed) for the sake of argument.
Don't get me wrong, I found your article interesting and I like when people go against the tide of hype, speaking for (currently) underappreciated technologies. I just think you used some exaggerated rhetorics.
You can write ugly code with Python, but the Python language and community clearly renounces itself from that and is all about readable code. Every feature addition or remove is done with consideration of code readability and creating one best practice to perform a task correctly.
JavaScript "maintainers" try their best to build a solid language on top of an ugly hack. The community tries every day to avoid its own language with another precompiler.
Java is all about defining solid interfaces, which creates new problems, but at least they try.
On the other side, Perl people were historically often proud of their crazy hacks.
PHP does not remove dangerous or objectively ugly language features. It could avoid SQL injections in new versions all together but instead is focused more on adoption and backwards compatibility.
The language is partly responsible for the type of code that is written in it.
I hate to defend PHP, but they have removed some dangerous features. Over the years there have been a lot of dangerous hacks that were deprecated and eventually killed. register_globals was deliberately disabled, breaking a lot of old PHP. Magic quotes got removed (though PHP's morass of variable escaping is still a tar pit). PHP 6 (or maybe 7 depending on the number they decide to go with) will kill mysql in favor of mysqli which will help enforce the use of placeholders/prepared statements (you can still screw it up, but you have to work harder). They could do better, but they have been improving in removing dangerously broken features, and have broken back-compatibility over and over to do so. I actually hate PHP, but when I've had to use it, PHP 5.5/5.6 isn't nearly as dangerous/broken as PHP used to be (though it's still horrible).
I give them, that they are slowly changing now (probably because they can not come up with further constructs), but for a while they still deserve the rants.
Any language can be written badly.
Also, I'm not sure, but I believe, had the original hacks not been done in Perl, the authors (and others after them) would have had much less trouble untangling the mess and they wouldn't have had 10 year old bugs no one could fix.
https://www.youtube.com/watch?v=gweDBQ-9LuQ
---
I don't know if the talk is any good if you actually do know Perl.
$user = new User({
userid => $foo,
role => "user",
username => cgi->param("username"),
password => bcrypt(cgi->param("password"))
});
If you're seriously telling me you instantly spot the bug (if that were real perl, i don't know perl) without having seen the talk before, then congratulations, nobody else did, please start auditing perl software.For anyone that didn't see the talk, no the bug isn't SQL injection or something like that (though that'd likely be possible too, watch the talk).
If you pass ?username=foo&username=bar then cgi.param("username") will actually return a list ("foo", "bar").
Now say you pass a list of three elements ?username=foo&username=role&username=admin ... yes seriously. It'll add the list into the hash and use every %2 element as key and the other as value. allowing you to overwrite stuff in hashes.
Of course, all of this is documented. But that doesn't make it not terrible.
PS: I don't hate perl. Perl -e is still very handy sometimes.
I understand your code is pseudocode, but I should note that the "." is considered string concatenation in Perl. My very first thought before reading too carefully was that the bug was that someone tried to concatenate strings instead of calling functions. :) But I kept in mind that this was pseudocodish.
Yes, my second thought was, "He says there is a bug here; I wonder if this is one of those cases where the function returns values in list context." For any experienced Perl programmer, this is a thing to be aware of, especially when calling functions in hash maps, not only because it may return multiple values but also because it may return NO values, which would match the "username" key with "password".
We may debate the pros and cons of list context, but it is one of the first features I teach new hires because it can catch people accustomed to other languages by surprise. Perl is not the only language to support this kind of feature; it is present in Ruby with the "splat" operator (*array).
As I said, it may be documented and known in the perl community, in my option it's still horrible. But yes, that's just my opinion of course.
> Perl is not the only language to support this kind of feature; it is present in Ruby with the "splat" operator (*array).
Unfortunately I also don't know ruby, but as far as i understand from what you're saying is that in ruby you need to explicitly tell ruby to "splat" the list in.
I really have problems coming up with usecases for this kind of behaviour, but yes, sure under some curcumstances it might be nice. But i'd argue it's the minority and so having to explicitly "whitelist" it would make sense. To avoid exactly this problem, of accidentaly calling a function that might return a list. The problem that I also see is that at some point you could refactor a function that previously couldn't return a list.
Except maybe that's actually a thing in perl, to use this behaviour as a feature, I don't know.
Yes, that's common. List processing via variadic arity functions is an explicit feature of Perl.
sub f { return 1, 2 }
my %x = (a => f(), b => f());
produces a three-element hash that has '2' as a key and 'b' as a value with no warnings. "Even offsets are keys" is far more error-prone than an actual syntax for hash literals, and splicing a list (not a hash!) into a hash doesn't need syntax this terse because it's hardly ever what I wanted. I think even PHP is more sane here.I'm not sure how an appeal to PHP as lowest-common-denominator of language sanity argues against understanding the return values of functions you call.
Sure you should. But confusion between keys and values isn't useful, so there's no reason for a subexpression to be able to do that, and in PHP it can't.
my $arr1 = [1, (), 2, (3, 4, (5, 6)), 7]; # $arr1 is [1, 2, 3, 4, 5, 6, 7]
my $arr2 = [1, [], 2, [3, 4, [5, 6]], 7]; # $arr2 is [1, [], 2, [3, 4, [5, 6]], 7]
Perl's fat arrow => is the same thing as a comma, except that the thing on the left of it may be a bare word under "use strict". In other words, it has little special meaning and helps you to construct a list of things. my $arr3 = [1 => 2 => 3, 4 => 5]; # $arr3 is [1, 2, 3, 4, 5]
For readability, when we construct hashmaps, we use the fat arrow so that you can more easily distinguish key from value. my $hash1 = {x => 1, y => 2}; # $hash1->{x} is 1
But you don't have to use the fat arrow: my $hash2 = {'x', 1, 'y', 2}; # same as $hash1
An array used in an expression like this is considered to be in list context: my @pairs = ('x', 1, 'y', 2);
my $hash3 = {@pairs}; # same as hash1
Since we can boil about anything down to expression in list context, we have a lot of concise flexibility on constructing arrays and hashes and a variety of ways to express the same concept. Here is a fun use case: Build a hashmap where the keys are the numbers from 1 to 100, and the values are all the string "foo". Other languages perform an elegant dance with infinite cycles and zippers. In Perl, knowing the list context magic, this is a possible solution that shows that "map" may not return the same number of elements as the input list/array: my %answer = map { $_ => "foo" } (1..100);
Let's join together two arrays with the letter 'x' between them: my @result = (@array1, 'x', @array2);
Take an array reference, and return a reference to its elements cycled once (this specific problem is awkward in Perl and easier in other languages with nice array operators): my $doubled = [@$ref, @$ref];
Construct a hashmap where some of the keys are completely optional. This is also an alternative expression for the "many optional parameters to object creation" problem, which most languages let you resolve by allowing you to instantiate the object with defaults and then alter the defaults with accessors in "if" clauses afterwards. In this example, if $self->y is false, then there will not be a "y" key. my %params = (
x => $self->x,
$self->y ? (y => $self->y) : (),
z => $self->z,
);
MyClass->new(%params); # this actually converts the hashmap into list context!
Tip of the iceberg. Note that I am not defending, just demonstrating. I am on the fence on if I prefer the Ruby explicit style over Perl's style. For one, in Ruby, the fat arrow actually means that you are now constructing a hashmap. In Perl, since it is just a comma, I can exploit it to mean new things, depending on my context. use Moose;
has age => (isa => 'Int', required => 1);
use Test::More;
subtest 'Math is still valid' => sub {
is(1, 1 => "1 is still equal to itself");
is(2, 2 => "2 is still equal to itself");
};The main problem with the talk is that almost every single thing he says is wrong, except for that bug. He may have reported it, but he knows almost nothing about the language, and is complaining because he never actually learned Perl, but expects Perl to conform to his expectations.
The fact that he pretends he is correct makes it even worse, since it means he is spreading his ignorance and misconceptions to others who take it as fact.
He is like an english person complaining about the fact that pronouns are gendered in german.
> That bug is legit (although it was caused by an inexperienced developer who didn't read the manual).
Well that's exactly the problem, as I said before, the fact that this is documented doesn't make it not horrible. At least to me, as a non perl person.
> He is like an english person complaining about the fact that pronouns are gendered in german.
Valid complaint in my opinion. And that's my mother tongue.
Also, regarding the gendered pronouns: They perform a very valid function in german, which i can understand you may not be consciously aware of: They serve the disambiguation of similarly sounding words (die Schüssel, der Schlüssel), similarly to Perl sigils.
Back to the issue of whether the bug is horrible. Any Perl developer learns these simple facts very early on:
- a function can return 0 to many values, depending on what kind of list of arguments you call return() with
- the contents of a list depend on the sigils in front of the arguments used in the list constructor
- if the sigil is $, then the list will have at the place of that argument exactly one value
- if the sigil is @, then list will contain zero to many values at the place of that argument
- if the sigil is none, then it's a function call, and the list will, following the first factoid, contain zero to many values at the place of the function call
- you must do two things with functions when calling them in a list constructor:
- 1. manually force them into scalar context in list construction by wrapping the call in scalar()
- 2. read the documentation of the function to see if the function makes any promises of its output
In the case of the bug you have an assignment to a hash, by way of a list constructor. That list constructor calls the cgi function params(). That function is documented to be able to return multiple values.
The programmer in this case didn't take care to read the documentation of the function, and this is not remotely the same thing as having a documented bug.
To the average Perl developer all this information is known and simple.
> password => bcrypt(cgi.param("password"))
There's two glaring and obvious bugs right there that would be there if you did a straight port to any other language. Always validate user input. If you ran this with taint mode on in Perl it'd fail, so you wouldn't even need human eyes to look at it to spot the problem.
If you had no familiarity with Perl I could see you getting bit by that. After you'd learned enough to be proficient enough to be writing code taking actual user input in a production env. you should really know about that issue since it's discussed in the Perl dev. community often enough. Also by that point you should be at a minimum testing any and all code taking actual user input with taint mode which would point out that problem immediately.
If you read the docs, you wouldn't have that bug: http://perldoc.perl.org/perlsec.html
I've fixed that bug in code as far back as 2000. There've been discussions on that anti-pattern on PerlMonks for at least a decade.
1: https://news.ycombinator.com/item?id=8813479
2: Comment follows:
From the changelog of the latest release of Perl: "CGI has been upgraded from version 3.63 to 3.65. NOTE: CGI is deprecated and may be removed from a future version of Perl." You can still make arguments about list handling in Perl, but using a deprecated module, which has been understood by the community for years to be problematic and exists mostly for backwards compatibility, does not bolster your argument, it does the opposite.
Using large monolithic projects that were started 10+ years (Twiki: 1998, Movable Type: 2001, Bugzilla: open sourced by Mozilla in 1998) ago also doesn't lend itself well towards pointing out modern usage of Perl.
Feel free to make any argument you want, but you should use relevant examples if you hope to be taken seriously.
I'm not sure this can be considered a bug.
Yes, indeed, sexism in talks is bad. I stand with you to condemn it. Regardless, the talk could be good technically.
(I didn't downvote you.)
I think the misunderstanding of Perl is based in the "There is more than one way to do it" philosophy. A corollary to that motto could be "There is no right way to do it." Using that corollary, no matter what Perl code you look at the correctness is in the eye of the beholder.
Even looking at your own code a year later, you'll ask yourself "why did I do it that way?" This is because the Perl syntax and choice of modules is so deep and varied that you cannot help but continuously evolve your style.
It takes real world trial and error of writing and reading Perl code to build your own style and get a handle on what good, readable Perl code should be... for you.
Haters are going to hate, but Perl programmers know the power and aren't going to it give up. Winning isn't everything. Getting there first? Well, that just means you should respect your elders. :)
I mean, what are we even talking about? Pearl "won"?? Won what, exactly? The internet? The.... money? WHAT THE FUCK! Please stop this, ok? This makes no sense at all and it's probably the most ridiculous discussion on the planet right now.
HN is unlikely to ever earn that accolade... http://www.reddit.com/r/dogtalk
Perl 5 is thriving too. It has many improvements to the Perl 5 of 10 years ago.
The same guy (Larry Wall) who originally wrote Perl 1 to 5.6 is the one who is writing the spec (though not the implementations) for Perl 6. People let him do it out of respect, even though it's disastrous for Perl 5.
Are there really more differences than things they share in common, to the extent that "new language" is an accurate description?
A short example from the recent Perl6 calendar[1]:
Perl5:
package Timer;
use Time::HiRes qw(gettimeofday tv_interval);
use Mojo::Base -base;
use experimental 'signatures', 'postderef';
has _event_log => sub { [] };
has _start_time => sub { [ gettimeofday ] };
sub record ($self, $event_description) {
# record the event and timestamp
my $event = {
timestamp => sprintf('%.3f', tv_interval($self->_start_time)),
description => $event_description,
};
push($self->_event_log->@*, $event);
}
sub report ($self) {
# render a full report
my $report = '';
foreach my $event ($self->_event_log->@*) {
$report .= sprintf "[%s] %s\n", $event->{timestamp}, $event->{description};
}
return $report;
}
1;
Perl6: class Timer {
has $!start-time;
has @!event-log;
method record ($event_description) {
# initialise the start-time if this is the first time through
$!start-time //= now;
@!event-log.push({
timestamp => now - $!start-time,
description => $event_description
});
}
method report {
my @report_lines;
for @!event-log -> %event {
@report_lines.push("[%event<timestamp>.fmt("%.3f")] %event<description>");
}
return @report_lines.join("\n");
}
}
[1] https://perl6advent.wordpress.com/2014/12/13/day-14-a-perl-6...That's not really true in two senses:
1) If you're just talking about AJAX request handlers, those are trivial to implement in Perl (I put together a number of them). If you're talking about real async processing in an app, POE/threads/etc. can handle async processing readily.
2) People moved off Perl/chose to adopt different langs. for many reasons but async was very rarely one of them.