rename takes two scalars, not a list

Perl gets us used to the idea that we can throw a list at a function and it all works out. The first item in the list becomes the first parameter, the second list item becomes the next parameter, and so on. We expect these two calls to some_sub to act the same:


my @args = qw( one two );

some_sub( @args );
some_sub( $args[0], $args[1] );

Perl doesn’t have complicated lists (Python does). You expect it to collect all of the elements of all of the scalars and arrays to construct a single flat list with no additional structure (ignoring the reference hacks to create data structures).

But it doesn’t work for rename. When I try to compile the code with -c, I get an error if I use a single array in the argument list?

$ perl -c -e 'rename( @ARGV )' one two
Not enough arguments for rename at -e line 1, near "@ARGV )
"
-e had compilation errors.

A slice doesn’t work

$ perl -c -e 'rename( @ARGV[0,1] )'
Not enough arguments for rename at -e line 1, near "] )
"
-e had compilation errors.

WTF? rename needs two arguments and @ARGV has two items, right?

No, @ARGV has no items. I’ve only completed the compile-time phase. perl hasn’t filled in @ARGV yet, but it knows that rename need exactly two arguments (not one or three or 37). It checks for the right number of arguments at compile time.

Why is this function different? It has a prototype, which is some extra information functions carry around to tell perl how to parse code. It’s a compile-time effect for parsing. It’s not at all a way to enforce types like you’d expect from other languages. You can use the prototype built-in to check it. You can give prototype a string that is the function name. If the string starts with CORE::, prototype uses the built-in function:

$ perl  -le 'print prototype( "CORE::rename" )'
$$

The prototype for rename shows $$. As perl parses this, it expects two things, each which will be thought of as scalars. That means this nonsense compiles:

$ perl -c -e 'rename( @ARGV, @ARGV )'
-e syntax OK

But, it won’t use the items in either of those arrays. They will be converted to the number of elements in each array. That’s unlikely to be what you or anyone wants. Worse, it’s not what anyone expects with Perl’s general argument list flattening.

If you aren’t thinking about this, you’ll probably miss that the rename document ion doesn’t say it can take a list:

$ perldoc -f rename
    rename OLDNAME,NEWNAME
            Changes the name of a file; an existing file NEWNAME will be
            clobbered. Returns true for success, false otherwise.

It has an OLDNAME and NEWNAME parameter, but no other calling sequence. For instance, utime, which needs two scalars and a list, is documented to take a list:

$ perldoc -f utime
    utime LIST
            Changes the access and modification times on each file of a list
            of files. The first two elements of the list must be the NUMERIC
            access and modification times, in that order. Returns the number
            of files successfully changed.

It’s prototype is just @ (instead of $$@):

$ perl  -le 'print prototype( "CORE::utime" )'
@

But then substr does what you think utime should do:

$ perl  -le 'print prototype( "CORE::substr" )'
$$;$$

As with many things in Perl, trying to make it all fit a pattern in madness. Just accept what it is.

Further reading

It’s a way of thinking

danvk tried to solve the Josephus Problem in Perl, Python, and Ruby, comparing each one. He had some pretty ignorant things to say about each.

There’s no reason to use object orientation here. It doesn’t help the problem or organize the data any better. But, it’s cargo cult to make everything a class. Even then, why all the bells and whistles? To you really need string overloading here?

Peter Scott has a natural approach using just the stuff from Learning Perl:

#!/usr/bin/perl
use strict;
use warnings;

my ($n, $k) = (40, 3);
my @soldiers = 1 .. $n;
my $pos = 0;

while ( @soldiers > 1 ) {
    $pos = ($pos + $k-1) % @soldiers;  # -1 for the one we just removed
    splice @soldiers, $pos, 1;
    }

print "Winner: Person #@soldiers, alive\n";

danvk complains that he doesn’t like Perl’s object-oriented framework, but doesn’t realize Larry stole it from Python, the language he prefers. The joke is on him.

Object orientation is a way of thinking about things, not a particular technology. It’s not about translating a program from another language line-by-line using the same techniques.

Don’t forget that path!

I wanted to rename some files, so I pulled out File::Find and created a callback to do that. Then nothing appeared to happen:


use utf8;
use File::Find;

my $wanted = sub {
	rename $_ => q(Côte_d'Ivoire.png) if /Ivoire/;
	};

find( $wanted, @ARGV );

But, this doesn’t work! The value in $_ is just the filename, so unless that file is in the current working directory, my files aren’t renamed:

use utf8;
use File::Find;

my $wanted = sub {
	rename $File::Find::name => q(Côte_d'Ivoire.png) if /Ivoire/;
	};

find( $wanted, @ARGV );

This doesn’t work, and I know it doesn’t work because I’ve made the same mistake before. And before that. And many times before that.

Fortunately for me, all of this was in a git repository, so I discard that changes and start again.

% git checkout -- .

The problem is one of paths. $File::Find::name has the full path to a file. The callback gets just the filename in $_, which is why I can use that for the regex target with a false match from some other path portion. However, when I do the rename, I take the path and move it to a file in the current directory. All files collapse into the same file and whoever gets there last wins.

I have to be more careful. I get the directory name and prepend it to the new file name:

use utf8;
use File::Basename qw(dirname);
use File::Spec::Functions;

my $wanted = sub {
	next unless /Ivoire/;
	my $dirname = dirname( $File::Find::name );
	rename $File::Find::name => catfile( $dirname, q(Côte_d'Ivoire.png) );
	};

This is also a problem with opendir, which only returns the filename, unlike glob.

Why I didn’t submit that patch

Ovid notes his three requirements for submitting a patch:

  1. The other devs should be pleasant to work with
  2. The code base should be relevant to me or at least fun
  3. The barrier to entry should be as low as possible

I don’t particularly care about the first two, but the third is why I became a Perl user. When I started with Perl, I liked that I didn’t have to have any special tools, IDEs, editors, or anything else. If I had perl and vi (emacs you had to install extra!), I could work, even if it was with Randal “I never leave emacs” Schwartz. I could work in a way that was comfortable for me, and I think this is a big part of why Perl and PHP have been so successful.

Point 3, though, is why I recently posted to twitter “Maybe all these Dist::Zilla users can add something to their repos that tells people what to do with them to build them.” after encountering another repo (not on CPAN) that I wanted to use but had no instructions on what to do with it.

If I want to do a drive-by patch—something I think I can do in under five minutes—ease of process is paramount. That’s what GitHub has done for us. If I have to install Dist::Zilla again (like for the Perl v5.18.2 I just installed), I’ve used up my five minutes for thinking about the problem and I’m on to the next thing. It’s not because I don’t want to contribute, but that the developer has intentionally made it difficult to contribute.

Ovid’s particular problem is with Adam Kennedy’s SVN repo. Those who have been around long enough know that Adam had a really nice setup with existing technology. Subversion was good because it fixed a few very annoying things with CVS, but sharing was still hard enough that SourceForge couldn’t figure it out. Adam made it so anyone could get an account and in ways that were convenient to them. As I recall, you could do it by text message. He solved the problem of getting the account and fetching the sources without waiting for a human. But, git killed most interest in Subversion and people forget what Adam had started with (even if he was a git holdout).

With dzil, which I don’t use just because I don’t, easy is not the case. I have to replicate a development environment before I can start. It becomes something that’s blocking my to-do list instead of filling a spare moment when I take a break. Installation from my local CPAN took six minutes, and that’s just the base package without the plugins I don’t know that I need yet. dzil people counter that I can still use `prove` without all that, but what if my patch is something that’s not code? More than a couple of times I couldn’t discover where the text inside a release was inside the repo and I suspected it was a plugin problem. I’m not going to chase down those problems, and I might not even report it because by that time I realized I’ve spent too much time on the problem. You may not like that attitude, but I assert that’s what most people have.

I had talked to Rik about this at some workshop and he said he’d realized that he’d have to accept reports with test cases on the released distribution instead of a patch on the repo and he was fine with that. That always feels weird to me and I hate when people do that to do instead of just sending a pull request.

It gets to be a religious war, and if you aren’t part of the clique, you’re sometimes told explicitly to go away, in the same way people divided themselves into the “Modern Perl” camp and everyone else. Other people start to evaluate you based on which flag you fly; I’ve known really good programmers who were denied jobs because they hadn’t read Modern Perl when they were the sort who could have written it. It’s the same problem with the blind adherents to Perl Best Practices. Moose is a bit of the same way. I was interested in it early on, and I won’t out the person who promised—not just told, but promised—that it would be three self contained files. Now it’s a fractured and confusing landscape of variants and extra plugins.

If this had been the state of Perl when I started, I probably would not have kept at it. I didn’t have to choose sides. The dirty secret of Open Source (the capitalized version) that Ovid notes is that people are selfish and work when it’s economic for them. If you want people to contribute, you have to make it easy for them, or at least easy enough that they’ve contributed before they realized they spent too much time on it. The more you make contributors into a clone of yourself, the fewer people you have available as collaborators.

Don’t look at that CPAN module!

Can’t use CPAN modules? If you can’t, you have company, whether you want it or not. CPAN Dependency Hell is a real problem, and it’s much worse if you’re an experienced Perl programmer. Management might want to clamp down on third-party, unsupported code. Lawyers might be afraid. I don’t want to get into that bit though. I’ll concede, for the purposes of this post, that these people can’t use any CPAN module.

So, they have to do something that a widely-used and widely-recommended CPAN module already does. For instance, they want to parse comma separate values, or they want to extract some text from HTML. They know they can’t use CPAN modules, so they don’t bother looking at CPAN. But, they don’t know how to do it themselves. They think about it for seven minutes. They don’t figure it out. What’s next?

Naturally, they go to Perlmonks or Stackoverflow and ask if there’s an easy way to do the same thing that the module does. That makes you a lazy Defective unworthy of their job. Instead, they are Help Vampires who shift their paid time to your unpaid time.

Some examples, just from Stackoverflow:

Their reasons vary. Some people think everything has to live in one file, even if they are using core modules. That’s fine. Copy the source out of the CPAN module into a single file. It’s actually not that hard to install every pure Perl thing that they want to use, concatenate all of the Perl module files you use, and include that in your program. They can fake %INC to think that the modules are already loaded. That’s not even counting things like PAR or Carton.

That would require some Perl knowledge, though, and some people avoid that when they can. They want the next problem to be just as hard, if not harder, than the current one. If they don’t want to copy the source, they can at least read it. If they can’t use the module, they can look at how the experts have done it and do the same thing. But, that requires the ability to read code, and maybe do something that doesn’t involve typing.

Just because they can’t use that source doesn’t mean they can’t mine it for ideas. However, there’s this fantastical thinking that there is some quick hack that will do the same thing. Surely every module is over-engineered (some are, truly), overly-cautious, and even too correct. All that code in those modules doesn’t do anything. Perl can do anything in one statement, can’t it? Why do these module authors work so hard and write so many tests?

It’s much better to ask people who don’t do it correctly (module or not) to guess at a solution. So, they ask, and then they wait for the unsatisfactory answers to come in. There are the tired experts and experienced practitioners worn out by these questions, who just tell them to use the modules. There are the newbies who guess at solutions that cover only the 3% of the problem they’ve ever encountered. Half of those think that input data never change, so their special case guesses are rock solid. They’re content to punt the problem to the next person, using the IWBHWIB (“I won’t be here when it breaks”). They’ve already got their next job, with adequate references, before anyone notices.

There’s another sort of defective programmer, the NIH variety, and I don’t mean those who work at the National Institute of Health. These are the “not invented here” types. Sometimes these Defectives just can’t accept that anyone is smarter than them. They get wrapped up in the ego competition or some notion of purity. Everything has to work just so. But, it’s never the best code that wins: it’s the good enough code that gets the job done at the right time. With appropriate use of interfaces, they could use CPAN modules at first and replace them when they have time.

Missing the big picture

chromatic is a bit harsh on Seda Özses’s article “Very simple login using Perl, jQuery, Ajax, JSON and MySQL” on IBM developerWorks, and his tone encouraged a lot of other people to be equally harsh. This has always been one of the biggest problems for technical communities: they forget that a person is on the other side. Not only that, they forget that a person is also on their side. I think chromatic could have modulated his tone and formed a very useful post had he actually explained his changes in the code.

Now, to be fair, the original article, which I did not see, was worse than the current one according to the gossip. And, I mean gossip. What is this? Middle school? I saw that Dave Cross tweeted about the bad article, then whoever controls The Perl Foundation twitter account retweeted it with five other people. If anyone wrote to Seda first with a patch, please speak up, but if your tweet isn’t “I wrote to so and so with a patch and they told me to get stuffed”, Twitter isn’t always the best route. Sure, Kevin Smith may have Southwest sucking up to him now, but if you followed the whole story, it was only for him.

chromatic gracefully slides over the fact that Seda actually updated the article because that belies his intent of public shaming. He’s still on the attack because no bad code can be allowed to live. And, to be fair to chromatic, he says “It’s okay to write baby Perl, but it’s baffingly irresponsible to publish said baby Perl as an example from which you expect other novices to learn.” I say “It’s lame to tell someone their fly is down by screaming it to the entire room”.

Here’s the code that Seda Özses presented when chromatic started his post (and is not the original or current code in the article):

#!/usr/bin/perl
use CGI qw(:all);
use DBI;
use DBD::mysql;

# read the CGI params
$CGI = new CGI();
$username = $CGI->param("username");
$password = $CGI->param("password");

# connect to the database
$DBH = DBI->connect("DBI:mysql:database=mydb;host=localhost;port=2009",
	"mydbusername", "mydbpassword")
	or die($DBI::errstr);

# check the username and password in the database
$query = qq{SELECT id FROM users WHERE username=? && password=?};
$query = $DBH->prepare($query);
$query->execute($username,$password);
$ref = $query->fetchrow_hashref() || 0;
$userID = $ref->{"id"};

# create a JSON string according to the database result
$JSON = ($userID) ?
	qq{{"success" : "login is successful", "userid" : "$userID"}} 	:
	qq{{"error" : "username or password is wrong"}};

# return JSON string
print qq{Content-type: application/json; charset=utf8\n\n};
print $JSON;

What’s chromatic’s beef here? He doesn’t say because he falls into the trap that many über-coders do: they think that code speaks for itself. It doesn’t, which is why I contend that so many people can’t read your code. chromatic doesn’t care as much about teaching anyone to do better as making his point, so he becomes the guy that makes you think all Perlers are assholes.

I’ll explain some of it though:

  • Seda doesn’t use strict or warnings. These are handy development aids to keep you on track. I don’t use warnings in production code either, though, since I can easily turn them on while I develop. Debate among yourself on that one, though.
  • Seda imports :all from CGI.pm, but doesn’t use anything that she imported. So, there’s a little cut-n-paste cargo-culting there. It’s not that big of a deal though.
  • Seda loads the DBD::mysql, although DBI will do that for her. You only need to load the modules you directly use. You don’t have to directly load the modules that other modules use.
  • Seda doesn’t validate the user input.
  • $ref might not be a reference, so the $ref->{"id"} might blow up.
  • Seda doesn’t use taint-checking, a knee-jerk prophylactic that’s a favorite among people who just want to be snide.
  • Seda uses the indirect object form, new CGI. This was the documented style up through CGI 3.45, released in August 2009. Perl 5.10.1 came with CGI 3.43. That documented style didn’t change until Perl 5.12.0 included CGI 3.48.
  • chromatic asserts that you can’t make a simple CGI response without using the CGI.pm module.
  • chromatic doesn’t like the style.

And here’s chromatic’s code, which I think is equally bad:

#!/usr/bin/perl -T

use strict;
use warnings;

use CGI;
use DBI;

my $q        = CGI->new;
my $username = $q->param("username");
my $password = $q->param("password");

my $dbh = DBI->connect(
	'DBI:mysql:database=mydb;host=localhost;port=2009',
	'mydbusername', 'mydbpassword'
) or die $DBI::errstr;

my $sth = $dbh->prepare(
	'SELECT id FROM users WHERE username=? and password=?;'
);
$sth->execute($username, $password);

my $json;

if (my ($id) = $sth->fetchrow_array)
{
	$json = qq|{"success" : "login is successful", "userid" : "$id"}|;
}
else
{
	$json = qq|{"error" : "username or password is wrong"}|;
}

print $q->header(
	-type    => 'application/json',
	-charset =>  'utf-8',
), $json;

So what’s wrong with chromatic’s code? First, because of his position, he’s set himself up as a bigger target. He can’t complain about someone else’s code then make a bunch of mistakes himself.

  • chromatic has turned on taint-checking, but then does nothing with it. He doesn’t scrub the PATH environment variable, untaint the CGI input, or use the taint features of DBI. He doesn’t do most of the stuff I go through in my security chapter of Mastering Perl.
  • chromatic still uses CGI.pm. People do that because it’s in the Standard Library. It’s an ugly, monolithic waste of cycles and memory in this case though. Something like CGI::Simple would do the job nicely without the bloat.
  • While we’re both being pedantic, chromatic leaves hard-coded configuration inside the script. He should get the username, password, and non-standard port from non-code sources, such as a configuration file.
  • Instead of returning a valid response on a failure to connect to the database, chromatic just die‘s. The user will just see some sort of “500 Internal Server Error” page. Really? In 2011, from the author of Modern Perl?
  • chromatic uses the DBI error in his die message, potentially exposing infrastructure details that an attacker might use later. The user doesn’t need to know that level of error. And, DBI already has the RaiseError feature to do this. You want the error message to go into the log file and not make it out to the user. Where is that STDERR really going?
  • chromatic turns on strict and warnings. Although that adds no value to this code, if someone develops it further, those pragmas can help can help.
  • chromatic loads only the modules he directly uses, and doesn’t import anything.
  • chromatic violates the DRY principle. How many times does he have to type $json to assign to it once? Seda’s version was better, even though she needed to improve her conditional. I would have used do, myself.
  • chromatic insists that she use the CGI.pm module to make a CGI response, but he lets himself make a JSON data structure by hand. What’s in $id? chromatic didn’t encode that for JSON, so he can output an invalid object there. Hard-coding message formats is also quite a bad practice, but chromatic lets that slide too. Wrap that in an interface that all of your applications can use.

Again, to be fair, chromatic says in a comment “I wanted to make the minimal responsible changes to the program to improve its modernity. I also prefer to have connection information outside of my programs, but that’s also a larger change.” I don’t think he accomplished that, but he doesn’t let himself because he focuses on the least useful parts of the whole mess.

But, there’s no real improvement here. It’s the same code with the same problems, structurally. chromatic’s real beef is that someone without a lot of Perl experience used baby Perl in an article. That is, unless you are an expert, you shouldn’t be writing about Perl, at least in his mind. There’s no place for compassion or humanity: you either get it exactly right the first time, or he will publicly shame you, although passive-aggressively because he won’t use your name (it doesn’t matter because you aren’t a real person anyway).

It’s amazingly difficult to present useful code in a tutorial, and chromatic knows that. It’s a side issue to him though. If the Perl authors all wrote complete and correct programs, with good style, good error checking and handling, proper configuration, proper logging, and everything else a robust program needs, no matter the language, we wouldn’t be able to illustrate the concept that we want to point out.

I’m not defending the code that Seda posts, but I understand the situation. chromatic understands it to. An author’s job is to show enough to illustrate the point without causing unnecessary distractions. The reader’s job, at least in the mind of authors, is to learn the key points and integrate that with other knowledge. Tutorials don’t present production-ready programs just for that reason. Authors expect (and respect) that their readers aren’t idiots, even if most of them are. Would you read any article that went through every special and edge case just to show you how to output some JSON?

And, to be honest, if I were starting out in Perl today and ran into people like chromatic, I probably would find some other language to use. I wrote a lot of dumb Perl. It showed up in The Perl Journal and at The Perl Conference. I still remember Tom Phoenix coming up to me, when I knew no one else in the community, and saying “That’s really neat, but what if you did this”. I met a lot of welcoming and nice people, and I decided I want to keep meeting them so I convinced a lot of people to form NY.pm, then Perl Mongers.

It’s precedence, not context

Ovid blames Perl for a newbie-level mistake in a Twitter post.
Marcel goes on to misread the problem as one of context. Ovid posts a fix that uses parentheses to solve the precedence problem. This is chiefly a problem of code reading skills.

Here’s the code:

$ perl -MData::Dumper -e 'sub boo { 1,2,3 } my @x = (boo()||5,8,7); print Dumper \@x'

The output is:

$VAR1 = [
          3,
          8,
          7
        ];

Ovid doesn’t say much about what he thinks should happen, but it shouldn’t be hard for any person who actually knows Perl (and programming), to figure it out. If you understand precedence, you know which order things will occur.

First, you have to break it down into what happens when. Many experienced people often skip this step because they think their experience should allow them to skip the basics. They try to take in complex expressions all at once and figure out what they do. That’s where people get confused. They ignore the few simple rules of code reading.

Perl figures out the right side of the assignment operator first, so you have to figure out this expression, which is in list context because of the assignment to @x, an array:

(boo()||5,8,7)

In list context, the comma operator separates the elements of the list. There are only a few operators lower in precedence, and || isn’t one of them. The list is then going to be the results of these three expressions:

boo()||5
8
7

This is not what some people (maybe Ovid) don’t expect because they parse it as the choice between two lists:

boo()
(5,8,7)

Most of the misunderstanding comes from thinking the comma is just a way to separate items instead of thinking about it as an operator that has precedence like other operators. Some of the rest of the misunderstanding comes from perceptual narrowing; people are primed to think about lists so they forget what they should know and substitute new rules that only deals with lists.

Change the comma to a different, unfamiliar character, such as ‡, and show it to a programmer who understands precedence and I assert the confusion disappears because the programmer doesn’t insert his misconceptions about the comma into reading the code:

bar() || 5 ‡ 8 ‡ 7

Once you understand the precedence, the last two expressions of the list, 8 and 7, are easy. The first one is easy too. If you looked at it by itself you shouldn’t have a problem with it. You call boo() in scalar context because || is a scalar argument. If it returns a true value, use it. Otherwise, use 5. That’s easy enough too. You might recognize the process better if you saw it with a different scalar operator, such as +:

bar() + 5

The definition for the subroutine is just boo { 1,2,3 }. In scalar context, that is the final element in the series because that’s what the comma operator in scalar context does. Two things are lacking here in most people’s analysis: the comma is an operator and it responds to context. This is almost excusable as a gotcha, but it’s such a well known gotcha that a practicing Perl programmer should know it. This isn’t some obscure corner of the language. It’s the very basics of how the language works.

The perlop documentation is quite clear. The comma operator in scalar context evaluates its left expression and discards it (so, there may be side effects), then evaluates its rightmost element, in this case three, and returns it. There’s even an example in the perlfaq4’s “What’s the difference between a list and an array” that tells you exactly that, and it does that because so many people make this same mistake.

Experienced programmers often charge ahead where they’d do well to read the documentation. It doesn’t matter what you think it should do; it only matters what it does. Intuition is fine when it works out, but it’s not an excuse for a lack of knowledge or education. Intuition is a fool’s game; it only has hope when everyone thinks the same, and nobody does.

The basics matter quite a bit. Don’t get lazy.

Don’t blame your misunderstanding of precedence

Ovid blames Perl for a newbie-level mistake in a Twitter post.
Marcel goes on to misread the problem as one of context. Ovid posts a fix that uses parentheses to solve the precedence problem.

This is chiefly a problem of code reading skills. Here’s the code:

$ perl -MData::Dumper -e 'sub boo { 1,2,3 } my @x = (boo()||5,8,7); print Dumper \@x'

The output is:

$VAR1 = [
          3,
          8,
          7
        ];

Ovid doesn’t say much about what he thinks should happen, but it shouldn’t be hard for any person who actually knows Perl (and programming), to figure it out. If you understand precedence, you know which order things will occur.

First, you have to break it down into what happens when. Many experienced people often skip this step because they think their experience should allow them to skip the basics. They try to take in complex expressions all at once and figure out what they do. That’s where people get confused. They ignore the few simple rules of code reading.

Perl figures out the right side of the assignment operator first, so you have to figure out this expression, which is in list context because of the assignment to @x, an array:

(boo()||5,8,7)

In list context, the comma operator separates the elements of the list. There are only a few operators lower in precedence, and || isn’t one of them. The list is then going to be the results of these three expressions:

boo()||5
8
7

This is not what some people (maybe Ovid) did not expect because they parsed it as the choice between two lists:

boo()
(5,8,7)

Most of the misunderstanding comes from thinking the comma is just a way to separate items instead of thinking about it as an operator that has precedence like other operators. Change the comma to a different, unfamiliar character, such as ‡, and show it to a programmer who understands precedence and I assert the confusion disappears because the programmer doesn’t insert his misconceptions about the comma into reading the code:

bar() || 5 ‡ 8 ‡ 7

Once you understand the precedence, the last two expressions of the list, 8 and 7, are easy. The first one is easy too. If you looked at it by itself you shouldn’t have a problem with it. You call boo() in scalar context because || is a scalar argument. If it returns a true value, use it. Otherwise, use 5. That’s easy enough too. You might recognize the process better if you saw it with a different scalar operator, such as +:

bar() + 5

The definition for the subroutine is just boo { 1,2,3 }. In scalar context, that is the final element in the series because that’s what the comma operator in scalar context does. Two things are lacking here in most people’s analysis: the comma is an operator and it responds to context. This is almost excusable as a gotcha, but it’s such a well known gotcha that a practicing Perl programmer should know it. This isn’t some obscure corner of the language. It’s the very basics of how the language works.

The perlop documentation is quite clear. The comma operator in scalar context evaluates its left expression and discards it (so, there may be side effects), then evaluates its rightmost element, in this case three, and returns it. There’s even an example in the perlfaq4’s “What’s the difference between a list and an array” that tells you exactly that, and it does that because so many people make this same mistake.

Experienced programmers often charge ahead where they’d do well to read the documentation. It doesn’t matter what you think it should do; it only matters what it does. Intuition is fine when it works out, but it’s not an excuse for a lack of knowledge or education. Intuition is a fool’s game; it only has hope when everyone thinks the same, and nobody does.

Practically Extinct Rubbish Language

Carter Shanklin tweets Maintaining a script I wrote in PERL (aka Practically Extinct Rubbish Language). Not fun.

It might be more fun if you didn’t write crap in the first place. But, that’s okay, because it’s perfectly okay for the Defective Perler to blame the tool for his shortcomings. We wouldn’t have jobs otherwise!

Cut and paste another branch when you need it

People get all upset about long if-elsif chains, but if they’d just use a proper code editor with cut-n-paste and search, it’s really not all that bad. With split panes, multiple windows, and a big monitor, I can see hundreds of lines of code at once, so what’s your problem? Only got 24 rows? Get with the times, weirdo! Get a bigger monitor! Or maybe get some glasses if you can’t read the font size.

if( $hash{caseA} ) {
    some_sub( $hash{caseA} );
} elsif ( $hash{caseA} ) {
    some_sub( $hash{caseA} );
} elsif ( $hash{caseB} ) {
    some_sub( $hash{caseB} );
} elsif ( $hash{caseC} ) {
    some_sub( $hash{caseC} );
} elsif ( $hash{caseD} ) {
    some_sub( $hash{caseD} );
} elsif ( $hash{caseE} ) {
    some_sub( $hash{caseE} );
} elsif ( $hash{caseF} ) {
    some_sub( $hash{caseF} );
} elsif ( $hash{caseG} ) {
    some_sub( $hash{caseG} );
} elsif ( $hash{caseH} ) {
    some_sub( $hash{caseH} );
} elsif ( $hash{caseI} ) {
    some_sub( $hash{caseI} );
} elsif ( $hash{caseJ} ) {
    some_sub( $hash{caseJ} );
} elsif ( $hash{caseK} ) {
    some_sub( $hash{caseK} );
} elsif ( $hash{caseL} ) {
    some_sub( $hash{caseL} );
} elsif ( $hash{caseM} ) {
    some_sub( $hash{caseM} );
} elsif ( $hash{caseN} ) {
    some_sub( $hash{caseN} );
} elsif ( $hash{caseO} ) {
    some_sub( $hash{caseO} );
} elsif ( $hash{caseP} ) {
    some_sub( $hash{caseP} );
} elsif ( $hash{caseQ} ) {
    some_sub( $hash{caseQ} );
} elsif ( $hash{caseR} ) {
    some_sub( $hash{caseR} );
} elsif ( $hash{caseS} ) {
    some_sub( $hash{caseS} );
} elsif ( $hash{caseT} ) {
    some_sub( $hash{caseT} );
} elsif ( $hash{caseU} ) {
    some_sub( $hash{caseU} );
} elsif ( $hash{caseV} ) {
    some_sub( $hash{caseV} );
} elsif ( $hash{caseW} ) {
    some_sub( $hash{caseW} );
} elsif ( $hash{caseX} ) {
    some_sub( $hash{caseX} );
} elsif ( $hash{caseY} ) {
    some_sub( $hash{caseY} );
} elsif ( $hash{caseZ} ) {
    some_sub( $hash{caseZ} );
} else {
    print "No way!\n"
}

I could use something like a dispatch table or refactor the code, but that’s really a lot more work than adding another branch every time I have a new case, even if it does act like all of the other cases. No one has complained about it so far, so don’t rock the boat, you know? Besides, I probably won’t have to look at the code for another six months, so it’s someone else’s problem. Let them figure it out if they care so much about it. If they were so hot in Perl they’d just write some more Perl to generate that code.

print "if( \$hash\{caseA\} ) {\n";
print "    some_sub( \$hash\{caseA\} );\n";

foreach ( 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' ) {
    $a = uc $_;
    print "\} elsif ( \$hash\{case$a\} ) {\n";
    print "    some_sub( \$hash\{case$a\} )\;\n";
}

print "\} else \{\n";	
print "    print \"No way\!\\n\"\n";
print "\}\n";
7ads6x98y