Planet Perl is an aggregation of Perl blogs from around the world. Its an often interesting, occasionally amusing and usually Perl related view of a small part of the Perl community. Posts are filtered on perl related keywords. The list of contributors changes periodically. You may also enjoy Planet Parrot or Planet Perl Six for more focus on their respective topics.
Planet Perl provides its aggregated feeds in Atom, RSS 2.0, and RSS 1.0, and its blogroll in FOAF and OPML
There is life on other planets. A heck of a lot, considering the community of Planet sites forming. It's the Big Bang all over again!
This site is powered by Python (via planetplanet) and maintained by Robert and Ask.
Read more of this story at use Perl.
Read more of this story at use Perl.
For a party game, needed to repeatedly pick random letters from the (ASCII) alphabet. I had my laptop, so I just did it. A friend, a Java programmer, was amazed. My favorite part was when he said "you're going to type out all 26 letters, aren't you?" Um, no I'm not.
perl -le "print ['a' .. 'z']->[rand(26)] while <STDIN>"
For those not quite getting it, here's the shortest I could think of in Java (without getting to the point of obfuscation and with the caveat that they're functionally equivalent, though internally they handle things in a moderately different way):
import java.io.*;
class Pick {
public static void main(String[] args) {
String letters = "abcdefghijklmnopqrstuvwxyz";
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
while (true) {
try {
br.readLine();
}
catch (IOException e) {
System.out.println("Caught exception trying to readline: " + e);
System.exit(1);
}
int rand = (int)(Math.random() * 26);
System.out.println(letters.charAt(rand));
}
}
}
Of course, then you have to compile it and run it as separate steps.
Can anyone post any common code snippets which express themselves more naturally in Java than Perl? (Libraries which can be readily duplicated in Perl don't count)
Read more of this story at use Perl.
The Perl 6 design team met by phone on 07 May 2008. Larry, Allison, Patrick, Will, Jerry, Nicholas, Jesse, and chromatic attended.
Allison:
c:
Allison:
c:
Patrick:
c:
Jesse:
Larry:
nofat rulePatrick:
Jerry:
Patrick:
Jerry:
c:
Nicholas:
state implementation of any languageJesse:
Patrick:
Jesse:
Nicholas:
Jesse:
Will:
Jerry:
Patrick:
Nicholas:
Jerry:
Jesse:
Nicholas:
Allison:
Jesse:
Allison:
Will:
Patrick:
Allison:
Will:
Allison:
Jerry:
Patrick:
Will:
Patrick:
Jerry:
Patrick:
Larry:
Patrick:
Larry:
c:
Will:
Jesse:
I just uploaded String::TT to the CPAN. It makes working with
heredocs much nicer. Instead of making a hideous mess with a heredoc:
sub mk_htaccess { my ($site, $users) = @_; my $user_list = join ' ', grep { !/\n/ } keys %$users; my $site_name = do { $_ = "". $site->name; $_ =~ s/\"//g; $_ }; my $htpasswd_file = "/path/to/". $site->url. "/.htpasswd"; my $file = <<"HERE"; AuthType Basic AuthName "$site_name preview" AuthUserFile $htpasswd_file Require user $user_list HERE ... }
You can use a nice-n-clean TT template:
use String::TT qw/tt strip/; sub mk_htaccess { my ($site, $users) = @_; my $file = strip tt q{ AuthType Basic AuthName "[% site.name | remove('"') %] preview" AuthUserFile /path/to/[% site.url %]/.htpasswd Require user [% users.keys.join(' ') %] }; ... }
Notice how the indenting flows with your program, and that you don't need to create useless variables to interpolate. I like it.
See the manpage for more details.
|
Order The Origin of Consciousness in the Breakdown of the Bicameral Mind with kickback no kickback |
Human consciousness (which Jaynes describes and defines in considerable detail) is a relatively recent development, dating back at most only about 3,000 years or so.
There is the shocking part of the theory. Most people probably imagine consciousness arising much, much earlier, perhaps before language. Jaynes disagrees. In his theory, language, and in particular its mediation of thought through the use of metaphors, is an essential prerequisite for consciousness. And his date for the development of consciousness means that human consciousness would postdate several other important developments, such as metalworking, large-scale agriculture, complex hierarchical social structures, and even writing. Jaynes thinks that the development of consciousness is a historical event and is attested to by written history. He tries to examine the historical record to find evidence not only of preconscious culture, but of the tremendous upheavals that both caused and were the result of the arrival of consciousness.
If preconscious humans farmed, built temples and granaries, and kept records, they must have had some sort of organizing behavior that sufficed in place of consciousness. Jaynes believes that prior to the development of consciousness, humans had a very different mentality. When you or I need to make a decision, we construct a mental narrative, in which we imagine ourselves trying several courses of action, and attempt to predict the possible consequences. Jaynes claims that Bronze Age humans did not do this. What then?
Instead, says Jaynes, the two halves of the brain were less well-integrated in preconscious humans than they are today. The preconscious mentality was "bicameral", with the two halves of the brain operating more independently, and sometimes at odds with each other. The left hemisphere, as today, was usually dominant. Faced with a difficult decision, preconscious human would wait, possibly undergoing (and perhaps even encouraging) an increasingly agitated physical state, until they heard the voice of a god directing them what to do. These hallucinated voices were generated by the right hemisphere of the brain, and projected internally into the left hemisphere.
For example, when the Iliad says that the goddess Athena spoke to Achilles, and commanded and physically restrained him from killing Agamemnon, it is not fabulating: Achilles' right brain hallucinated the voice of the Goddess and restrained him.
In Jaynes' view, there is a large amount of varied literary, anthropological, and neurological evidence supporting this admittedly bizarre hypothesis. For example, he compares the language used in the Biblical Book of Amos (bicameral) with that in Ecclesiastes (conscious). He finds many examples of records from the right period of history bewailing the loss of the guidance of the gods, the stilling of their voices, and the measures that people took, involving seers and prophets, to try to bring the guiding voices back.
Jaynes speculates that mental states such as schizophrenia, which are frequently accompanied by irresistible auditorily hallucinated commands, may be throwbacks to the older, "bicameral" mental state.
Whether you find the theory amazingly brilliant or amazingly stupid, I urge to to withhold judgment until you have read the book. It is a fat book, and there is a mass of fascinating detail. As I implied, it's either a work of profound genius or of profound crackpottery, and I'm not sure which. (Yaakov Sloman tells me that the response to Wittgenstein's Tractatus Logico-Philosophicus was similarly ambivalent when it was new. I think the consensus is now on the genius side.) Either way, it is quite fascinating. There needs to be some theory to account for the historical development of consciousness, and as far as I know, this is the only one on offer.
Anyway, I did not mean to get into this in so much detail. The reason I brought this up is that because of my continuing interest in Jaynes' theory, and how it is viewed by later scholars, I am reading Muses, Madmen, and Prophets: Rethinking the History, Science, and Meaning of Auditory Hallucination by Daniel B. Smith. I am not very far into it yet, but Smith has many interesting things to say about auditory hallucinations, their relationship to obsessive-compulsive disorder, and other matters.
On page 37 Smith mentions a paper, which as he says, has a wonderful title: "Involuntary Masturbation as a Manifestation of Stroke-Related Alien Hand Syndrome". Isn't that just awesome? It gets you coming and going, like a one-two punch. First there's the involuntary masturbation, and while you're still reeling from that it follows up with "alien hand syndrome".
To save you the trouble of reading the paper, I will summarize. The patient is a 72-year-old male. He has lesions in his right frontal lobe. He is experiencing "alien hand syndrome", where his hand seems to be under someone else's control, grabbing objects, like the TV remote control, or grabbing pieces of chicken off his plate and feeding them to him, when what he wanted to do was feed himself with the fork in his right hand. "During his hospital stay, the patient expressed frustration and dismay when he realized that he was masturbating publicly and with his inability to voluntarily release his grasp of objects in the left hand."
Reaction time tests of his hands revealed that when the left hand was under his conscious control, it suffered from a reaction time delay, but when it was under the alien's control, it didn't.
Whee, freaky.
Take a look at this block of code and see if you can guess what it does.
END: {
print "Exiting...\n";
}
print "s = $s\n";
BEGIN: {
$s = 'Hello from the BEGIN phase';
}
You'd expect that the BEGIN block sets $s at compile time, so then that gets printed in the print statement, and then the END block executes at the end of the program. Yes, it would, except that those aren't BEGIN & END blocks. They're normal blocks that just happen to be named BEGIN and END.
Credit Randy Lauen for bringing this to my attention. Today at my day job he found a horrifying code construct, one that I'd been using (incorrectly) for quite some time now. You see, these two blocks are not the same:
BEGIN {
# Here's some code that gets executed at compile time
}
BEGIN: {
# This code gets executed at run time, because the colon
# after BEGIN means it's just a plain old label
}
Randy had been tracking down a bug for over an hour and couldn't figure out why his variables weren't getting set at compile time. Turns out the BEGIN block he was modifying was actually a not-special-at-all BEGIN: block. And he found 40+ instances of the spurious colon in the codebase. Of course, INIT, CHECK, UNITCHECK and END are also rendered non-special by a colon as well.
Most of the time, this is invisible. People tend to put their BEGIN blocks at the top of the program, so the difference between a real BEGIN block and a block that happens to be named BEGIN are not noticed. Still, it's a a bug waiting to happen. Sounds like an ideal check for Perl::Critic policy, no? Mike O'Regan jumped to it, and submitted a patch to Perl::Critic, so those of you using Perl::Critic (and if you're not, you should be) can get warned of using colons on those specially-named blocks once a new version is released, shortly I hope.
I'm not the only one to have been doing this. Google Code Search turns up over 100 instances, including some in the core Perl tests (heck, maybe from me!), as well as mailman and POE.
Check your codebases. If you're using ack, it's just:
$ ack --perl '^(BEGIN|END|INIT|CHECK|UNITCHECK):\s*{'
Have you fallen prey to this? Let me know.
Er, no it's not. This brief (and snarky) blog post pretty much sums up my feelings.
I've already been pretty critical of Arc, but pretty much ignored it after that. I've taken another peek and, once again, I'm still terribly impressed. Paul Graham has made arc code smaller for those projects he wants to focus on. He does this with decent libraries (well, decent if you don't mind 'ASCII-only' and layout controlled via HTML tables).
Welcome to the Intarweb, circa 1999.
I probably wouldn't be this harsh, but Graham has spent so long talking about "superior" programming languages that he pretty much had to knock this out of the park or tone his rhetoric down.
Occasionally you may run across an odd warning like this:
Use of uninitialized value in subroutine entry at X line Y
where the code at that line is a call to an XS subroutine (let’s call it xsub()) and you’re certain that the arguments you’re passing are not undefined.
Somewhere, deep in the XS/C code, an undefined value is being used. But where? And why is perl reporting that line?
Perl is reporting the last line of perl code that was executed at the same or higher level in the stack. So other perl code, such as a callback, may have been executed between entering xsub() and the warning being generated, but that perl code must have returned before the warning was triggered.
Assuming XS/C code is large and complex, like mod_perl, how can you locate the code that’s triggering the warning?
Here’s a trick I’ve used a few times over the years:
$SUB{__WARN__} = sub {
CORE::dump if $_[0] =~ /uninitialized value in subroutine entry/;
warn @_;
}
That make the program abort and generate a core dump file at the point the warning is generated. You can then use a debugger, or Devel::CoreStack, to report the C call stack at the time. It’s a savage but effective technique.
If the XS/C code was compiled with options to keep debug info (i.e., -g) then that’ll show you exactly where in the XS/C code the undefined value is being used. If not, then it’ll at least show you the name of the XS/C function and the overall call stack.
(The dump function is a curious vestige of old ways. You could use kill(9, $$). I’m not sure about the portability of either, for this purpose, beyond unix-like systems.)
I suggested the technique to Graham Barr recently and it proved effective in tracking down the source of that warning in a very large mod_perl application. The warning pointed the finger at a $r->internal_redirect($uri) call. The actual cause was a PerlInitHandler returning undef. (The handler was an old version of DashProfiler::start_sample_period_all_profiles.)
Anyway, it dawned on me this morning that I should update the technique. It doesn’t have to be so savage. On modern systems you don’t need to shoot the process dead to get a C stack trace.
A few approaches came to mind:
kill(17, $$) to send a SIGSTOP to the process to give time for the debugger to attach and for you to investigate the state of the live process.I think the second of those would be most useful most of the time.
Hopefully this will be useful to someone.

So, Irish voters will soon be voting in a state-wide referendum on the upcoming Treaty of Lisbon — the latest set of amendments to how the European Union is run.
Since ratification will require changes to the Irish constitution, we get to vote on these intricacies where most EU inhabitants do not. Unfortunately this means it’s not particularly “sexy” — it’s a pretty obtuse and boring set of issues, and deciding which way to vote is not easy, with such snore-worthy stuff at stake.
One of the organisations campaigning for a “no” vote in the referendum is called Libertas. Aileen forwarded on a very interesting article by Chekov Feeney on Indymedia Ireland about them, which is well worth a read if you’re interested in Irish politics and the international reach of US lobbying. Here’s some snippets:
Declan Ganley, president of Libertas, happens to be president of Rivada Networks, a US defence contractor (they supply emergency communications networks to the US intelligence community).
[...]
On Sunday April 20th, Libertas announced that Ulick McEvaddy was “joining the No To Lisbon Campaign” and publicised the event with a photo-opportunity of the two ‘entrepreneurs’ in front of the Libertas Campaign bus. McEvaddy is the first member of the Irish business and political elite to join the Libertas campaign since it emerged under the stewardship of Declan Ganley.
What’s particularly interesting about this is that McEvaddy is the CEO of Omega Air, a US defence contractor (they supply cargo planes and inflight refuelling services to the US military). [...] According to the [ US Air Force's Integrator Magazine ], “industry insiders say [McEvaddy's] company has even approached U.S. intelligence agencies about tanking services for detainee transfers, to reduce dependence on foreign air fields.” In other words, offering to provide inflight refuelling services to rendition flights so that they wouldn’t have to stop over at foreign airports such as Shannon on their way to “interrogate” suspects. A very accommodating offer indeed.
McEvaddy was also the figure who got himself appointed to the board of Knock airport with a view to opening it up to US military flights.
Nice guys, then.
The article goes on, and on, and on, detailing some shady transactions involving these guys and their US military/intelligence connections, the “astroturf” nature of the Libertas organisation, and the odd behaviour of the Libertas campaign in general.
It comes to this conclusion:
This article has examined the reality behing the Libertas campaign, the connections of its two high-profile backers, the implausibility of its message, the peculiar nature of its campaign and some of the underlying strategic differences at play. The conclusion is that the evidence suggests that Libertas is most likely to serve primarily as a vehicle for advancing US strategic interests.
As many of you know (all, like, 12 subscribers or whatever there are here), I took a job with Metaweb, who run Freebase, in January this year. So since then I’ve been in charge of the Freebase blog and haven’t been blogging here.
I’ve been wondering what to do with this domain, and I’ve decided to keep it running and to use it for personal/unofficial notes on Freebase-related topics. While the Freebase blog is a sort of official channel, I’ll use freebasing.org for less formal topics.
So, I suppose a disclaimer is in order here: Views and opinions posted on this blog do not necessarily represent those of my employer.
It seems tough to find a well-written SQL formatter out there which is both accurate and open-source. I looked at SQL::Tidy as an inspiration, but it's tokenizer is bad and there are plenty of issues with the code. A quick bit of hacking has gotten it to the point where it will take this:
SELECT me.import_id, me.timestamp, DATE_FORMAT ( timestamp, '%Y-%m-%d' ) as day
FROM import me LEFT JOIN import_error import_errors ON ( me.import_id =
import_errors.import_id AND import_errors.type IN (
'X::IMPORTER::VALIDATION::BRANDTITLEMISSING')) WHERE ( ( ( timestamp >=
'2008-05-01T00:00:00' ) AND ( timestamp <= '2008-05-06T13:12:30' ))
) GROUP BY me.import_id
And turn it into this:
SELECT me.import_id, me.timestamp, DATE_FORMAT ( timestamp, '%Y-%m-%d' )
as day
FROM import me LEFT
JOIN import_error import_errors
ON ( me.import_id = import_errors.import_id
AND import_errors.type
IN (
'X::IMPORTER::VALIDATION::BRANDTITLEMISSING'
)
)
WHERE ( ( ( timestamp >= '2008-05-01T00:00:00' )
AND ( timestamp <= '2008-05-06T13:12:30' )
)
) GROUP BY me.import_id
That's pretty ugly, but at least it makes it a lot easier to read auto-generated SQL that's all on one line. Still, it's only for quick cleanup. The tokenizer needs a lot of work before we can even think of a reasonable reformatter.
Amusingly, though, I accidentally ran Perl::Tidy over it.
SELECT me . import_id, me . timestamp,
DATE_FORMAT(timestamp, '%Y-%m-%d')
as day FROM import me LEFT JOIN import_error import_errors ON(me
. import_id
= import_errors
. import_id AND import_errors
. type IN('X::IMPORTER::VALIDATION::BRANDTITLEMISSING')) WHERE((
(timestamp >= '2008-05-01T00:00:00')
AND(timestamp <= '2008-05-06T13:12:30'))
) GROUP BY me
. import_id
OK, not great, but not significantly worse than the SQL::Tidy.
Parrot r27355 was fun to write.
One of the persistent error messages Parrot emits for compiler writers is
Null PMC access in invoke(). If you've had your hands deep in the
guts of Parrot, you know what that means -- you tried to call a Sub PMC when
you don't have a Sub PMC, you have no PMC. (If you don't know what that means,
this entry is for you.)
Sometimes this means that there's a problem in Parrot. We've fixed almost all of those problems though, so the error usually comes from elsewhere. If you're writing a compiler, or running a compiler built on Parrot, the error usually means "You tried to call a function that doesn't exist."
Parrot's optimizer does something interesting at the end of compilation time. You've probably heard that Parrot's compiler, IMCC, translates PIR into PBC. That is, it turns source code into bytecode, which Parrot can either serialize t to disk or execute immediately. That bytecode is just a chunk of linear data in memory. It's not really a data structure. (Okay, it's a C array, but that doesn't make it a data structure.)
After IMCC has finished building a standalone chunk of bytecode, it performs a constant fixup phase. The notable part of this phase is that it edits the bytecode in place to replace all named invocations of functions known at fixup time with offset invocations.
The previous code looks something like:
invoke known_function
null # padding
null # padding
If IMCC has already seen known_function by this time, the
direct invocation of known_function can continue. There's no
runtime lookup necessary; all functions already compiled and ready are
available in the bytecode.
If IMCC hasn't seen that function, runtime lookup is necessary, and so this function replaces the bytecode earlier with the equivalent of:
.local pmc func
func = find_name 'unknown_function'
invoke func
(I've simplified what actually happens slightly, because the concepts are more important than the details. Hopefully you see why the padding is necessary. If not, just imagine trying to splice additional opcodes into what may presumably be a lengthy C array -- like I said, barely a data structure.)
The problem with this second form occurs when find_name returns
a NULL PMC, which it can legitimately do. In that case, the
invoke opcode tries to invoke a NULL PMC and fails, and Parrot
throws an exception saying "There's nothing here to invoke." There's the error
message.
It's clear why that happens, but it's not useful. It would be more
useful to see the name of the function you tried to invoke in the
error message. Unfortunately, by the time Parrot calls the invoke
op, that name is long gone.
My first idea was to rewrite the dynamic lookup form into something resembling:
.local pmc func
func = find_name 'unknown_function'
if defined func goto call_it
die "Can't invoke 'unknown_function'"
call_it:
invoke func
Unfortunately, I didn't have the space in the bytecode stream to insert that many ops, and I had no desire to move chunks of memory around in that C array. I could have added more padding after an invocation, but to be fair I'm only mostly sure that it exists there in the first place.
I had room for one op with a destination PMC and a string constant argument.
I added an experimental op called find_sub_not_null which does the
same thing as find_name but throws an exception which includes
the requested name if Parrot can't find a PMC of that name.
This isn't entirely an ideal situation. It's a special case op, and I prefer to remove ops where possible. It's also nearly code duplication, though it's effectively three lines of code in an op, which isn't awful. I still want to be able to perform these kinds of transformations in PBC itself, but we need a different way to generate PBC and perform op-level transformations in PIR before we can do this effectively.
There are always tradeoffs, though. Doing this check in C is slightly faster than doing it in PIR. The standard Perl 5 rule of optimization applies even in Parrot -- the fewer ops, the mostly faster you can go. As well, I was able to improve the warning message today, rather than at some point in the future when we have better PBC optimization possibilities.
After all, I can always remove this op in the future.
I've just come back from the London Python Meetup. There was an informal survey, and I was the person who had the least Python experience (although I have written a little app). Around 50 people turned up and there was free pizza and beer.
There were some lightning talks, including one on Google App Engine. Another talk was a relevation someone has bought a bunch of servers for platform testing. The lightning talks were a bit hit-and-miss and would have been much better if they hadn't tried to have slides.
The main talk was Jacob Kaplan-Moss giving details about the history of Django, mostly little anecdotes. I was surprised to hear that PyCon had almost 1,100 people this year and that in the Django talk, half of the attendees (200 people) had learnt Python in order to use Django. That's some killer app.
Perl people as are pretty pragmatic, using the best tool for the job and learning about everything. However almost every talk tonight contained a dig about Perl, it was really dishearting. Python people: grow up and pay attention to what other people are doing - you might learn something.
For the social part we went to Tin-Tan for cheap cocktails (happy hour all night on Monday, yay!) and good mexican food. And lots of discussions / chatting.
Oh, and I really do hate mobile devices with internet connection. Imagine you're sitting in a nice place drinking some cocktails, when suddenly somebody pulls out some gadget and shows you an RT ticket that has been open for 10 months :-). On the other hand, that's a good method to make me fix it. Which I just did...
You see, the great thing about posting vim stuff here is that people (Smylers, Aristotle, etc.) immediately post corrections. I appreciate that :)
Problem: when in the MySQL client, you can type 'edit' and edit your last SQL statement (can I go back further? I thought so, but I can't recall how). However, I want my syntax highlighting, but the filetype isn't set. After a bit of testing, I found out that the filename is always /var/tmp/sql* (on my system). So the following addition to my .vimrc fixes this:
" this is for MySQL's 'edit' command while in the client
au! BufRead,BufNewFile /var/tmp/sql* :call SetMySQL()
function! SetMySQL()
set ft=sql
SQLSetType mysql
endfunction
Perl's big problem is one of perception. The other day, a job candidate asked me "You're moving your web apps from PHP to Perl? Shouldn't it be the other way around?" Why did he think that? The candidate knew no Perl, and only a bit of PHP, so had no technical reason to believe that PHP was better than Perl. So why did he think Perl was subservient to PHP in the web arena?
What I suspect is that Perl is just less visible, and not just in the sense of crap like the TIOBE index where it equates hits for "Foo programming" popularity of the Foo language. I'm talking about in the real way people see the world of programming.
I'm certain that PHP has become a de facto choice for basic web apps because it's just How You Do It these days. You see enough PHP in the context of the web, it starts to sink in.
Why is Ruby on Rails so popular? Is it better than Perl on Catalyst? Or is it just that people hear about Rails more? I suspect the latter, because perception is reality. When people perceive Perl as being dead, or not as powerful as other tools, it might as well be.
There are three goals I'd like to address:
The three actions to take: Decentralize, diversify and colonize.
Perl has a great infrastructure. We have the CPAN. We have the various sites of perl.org. We have Perl Mongers and perlmonks, and so on. Unfortunately, we've grown complacent. It's time to start looking elsewhere for infrastructure and community.
Standardization on tools makes sense if the tools are the best possible tools available. In many cases, the infrastructure already in place in the Perl community may not be the best there is.
Is the Mailman installation running on lists.perl.org the best mailing list solution for you when you need to start a mailing list? If not, then consider other options. Starting a list at groups.google.com is trivial, for example.
Is rt.cpan.org the best bug tracking solution for your modules or projects? If not, then look at other options. Many of my modules, such as ack and WWW::Mechanize are hosted at Google Code, where I use the bug tracking system there.
Is use.perl.org the best place to host your blog? Maybe you should post somewhere else. Certainly that was the case with me. I found that use.perl.org wasn't the news site I wanted it to be, so I started Perlbuzz as an alternative, literally doubling the number of news sites devoted to Perl.
Note that "alternative" does not me "replacement". It's possible to have two similar but diferent projects, websites or whatever that both fill a need. If, say, Perlbuzz replaced use.perl.org, we'd still only have one news site, which is no better a situation than we started with.
Some of the alternatives might not be community-based. Google Code, is not run by altruistic volunteers. Commercial interests will usually have more time and money to spend on providing useful infrastructure. That's no knock on the volunteers, but merely taking advantage of what the commercial interest has to offer.
(I know at some point someone will say "At some point decentralization is harmful." I agree, but we're nowhere near that point. Such a problem to have!)
The mantra "There's more than one way to do it" is at the core of Perl the language, but this is not always the case in the Perl community. We must always remember the guiding principle of TMTOWTDI.
The first place to diversify is with people. I suspect, but cannot prove, that the Perl community's size is a net loss as time goes by. We must constantly be trying to bring new people into the fold, to take advantage of their innovations, and to make what we do more fun.
Second, we must be willing to launch new projects, even if they're similar to existing projects. It could be a module, or a website, or an entire application. The knee-jerk resistance to diversity often sounds like this:
I've never understood this fear. It sounds like the resistance is based on the premise of "I wouldn't want to do that project, so you shouldn't either." It's the refrain of someone with a closed mind, unable or unwilling to imagine what could be. In the case of similar modules, the refrain usually goes "It's too hard to find the module you want anyway," but that's not a problem of the module, but of the way things are found in CPAN itself. (And if you'd like to address that little bit of decentralization, please take a look at my Rethinking CPAN mailing list.)
Ultimately, fear of the new is counter to the principle of TMTOWTDI. Indeed, plurality is at the very heart of open source, and it's additive, not subtractive. Template Toolkit need not take away from Mason. Perlbuzz need not take away from use.perl.org. Perl didn't take away from awk and shell. In any of these cases, if the new solution does take away from another, then the other solution was an inferior one to begin with.
Decentralizing and diversifying do two things. First, it opens our minds to alternatives that we may not have considered. It makes us more likely to find solutions that are better than what we started with. Second, it helps with colonization, the ultimate goal.
The other day, Tim O'Reilly posted on his Twitter feed a link to a cool article about life in the universe which mentioned Von Neumann probes, theoretical space craft that can replicate themselves using materials found in their travels. A single probe could travel some distance, replicate a thousand cloned probes, which would then launch in a thousand different directions, repeating the cycle.
That's what we need to do: Colonize the mindshare of the world to let them know that Perl is still viable, and a hell of a lot of fun. These new Perl lovers will spread the love as well, and the cycle will continue. In terms of action, it means simply "helping make more people aware of Perl as a cool & useful language." Fortunately, it's a case where we can think globally and act locally, even passively.
The biggest effect I see you, the reader, as being able to have is by spreading out Perl's web footprint. I want more places for people to find out about Perl, rather than a few big ones.
This colonization approach goes hand-in-hand with decentralization. Take my ack project, for example. ack has a number of footprints out there:
That's three times places for people to stumble across Perl, to see it mentioned. A separate home page also makes it more likely to get linked from somewhere else, as when Daring Fireball linked to ack and thousands of people came to see about ack. That's thousands of people who went and saw "Huh, here's a tool in Perl."
Of course, a few thousand visitors isn't going to change any mindshare. That's why it's not just me that needs to work on this. Fortunately, decentralizing and diversifying makes colonization easy.
Our Google footprint is pretty bad, anyway. When I search for "perl" in Google, the top five hits are:
Is this the best impression that we have to give the world?
Adam Kennedy's Strawberry Perl is a marvel of the principles I've talked about. Strawberry decentralizes, as it uses none of the core perl.org infrastructure, and it diversifies and colonizes by giving Windows users an alternative to ActivePerl, which for many users is not robust enough. When people talk about Strawberry Perl, it helps with mindshare as well.
Adam is a great example of someone who has set out to make improvements to a part of Perl, and implemented it without worrying about permission or duplication, helping Perl as a whole along the way. I thank him for it. I suspect his Perl On a Stick project will have similar results.
The strategy of decentralize/diversify/colonize takes actions at all different levels. You don't have to go create your own Perl distribution, or even write any code. Here are some other ideas to get you started.
I'd like to hear your ideas. How can we expand Perl's reach? What have you done to help decentralize, diversify and colonize? How can we keep Perl fun and exciting?
Two comments I don't need to hear: "This will never work" and "This is a waste of time." If that's what you have to contribute, trust that I've taken your insight into account already, and save yourself some typing. Alternative courses of action, however, are more than welcome. Also unnecessary: "None of this would be a problem if not for Perl 6."
Thanks to a number of people who have helped discuss these ideas with me as I put them together. These include Pete Krawczyk, Jeffrey Thalhammer, Ricard Signes, Liz Cortell, David Hand, Jesse Vincent and Elliot Shank. I'm sure I've forgotten some, so I apologize if I left you off the list.
The minicpan tool in CPAN::Mini lets you keep a copy of the most recent revisions of each module on the CPAN. Having a mini-CPAN is a great tool for anyone with a laptop, or who wants to look at the CPAN as a whole, or who wants to create a mini-mirror of CPAN to support a large installation without having to hit the net for each module install. An entire mini-CPAN only takes up about a gigabyte of drive space.
Ricardo Signes, CPAN::Mini's author, wrote to tell me:
CPAN::Mini 0.569 includes an obvious optimization: instead of reconnecting to your remote mirror for every file that might need updating, `minicpan` will now keep one HTTP connection open for the entire update. While I can't give numbers that reflect the most common cases of usage, a run that checks every file and finds no updates goes, on my laptop, from about two minutes to about twenty seconds -- about 1/6 the time! It also puts less load on the remote server, making it a friendlier way to keep a local mirror.
Also, Adam Kennedy has just posted about a major upgrade to CPAN::Mini::Extract, a tool to make it easy to get individual files from tarballs, that speeds up extraction:
By shifting expansion to a one-shot extraction to a temp file, and then opening tarballs once from the temp file, I managed to get a two to three times speed up for file extraction. Combined with CPAN::Mini pipelining, this makes CPAN::Mini::Extract massively faster (a 200%-300% overall speed up).
Selena Deckelmann has come back from BarCampPortland with copies of every Post-It on the topic selection board. The topic selection board at an unconference like a BarCamp is where people write on a Post-It a topic they'd like to see presented, and put it on a board for all to see. Whichever topics people vote for are the topics that are presented.
Scanning through the photoset on Flickr is fascinating, as these often are. Topics range from Pirates Paying Artists to WordPress as CMS to How to lie with statistics to Should we replace Congress with a wiki?
Also fascinating to see how widespread Twitter has become, with half the Post-Its leaving @usernames as contact information.
Makes me want to start up a Bar Camp Chicago. And move to Portland.
You know, I would really like to be able to hide outer scopes in Perl. There are plenty of times that I've worked with bad code and I would love to quickly and easily see what variables are embedded in a chunk of code which are defined in another scope. I figured out one way to do this with a primitive refactoring tool I'm working on. I shove the code into a different namespace, wrap it in a subroutine to prevent it from being executed (the code will croak() if there is a BEGIN block), and then eval it with strict and warnings. The error messages are trivial to parse and I can figure out which variables are not defined there.
This leads to my next problem: which variables are defined after that chunk of code which are dependent on that chunk of code. Trying to figure out the "extract method" refactoring means I have nastiness like this:
my ( $foo, $bar );
($foo, $bar, $baz ) = $self->_extracted_method($baz);
That raises another issue. The code must now always work in list context (let's hope you don't have wantarray embedded in the extracted code) and if there is an embedded return (some languages only allow return at the end of a method/subroutine), then this could also screw things up.
The more I play with this, the more I understand why Adam Kennedy struggled with a refactoring editor.
However, if I go for an 80% solution and I write tests (which, as you know, I do), then I could execute a refactoring and then automatically run all tests for the refactored code.
According to statistics by LaPerla, the freshest 25% of CPAN is newer than how old?
Read more of this story at use Perl.
Read more of this story at use Perl.
It's nice and simple, requires no external modules or CPAN downloads, and works pretty well for most situations where simple config files are needed.sub parse_config {
my ($self, $file) = @_;
open(my $fh, $file) || die "open($file): $!";
my @lines = <$fh>;
close $fh;
chomp(@lines);
my $config = $self->{config} = {};
my $section;
for (@lines) {
s/^\s*//;
s/\s*$//;
next unless /\S/;
next if /^#/;
if (/^ \[ (.*) \] $/x) {
$section = $config->{uc$1} = {};
}
elsif (/^ (\w+) \s* = \s* (.*) $/x) {
die "key=value pair outside of a section" unless $section;
$section->{lc$1} = $2;
}
else {
die "invalid line in $file: $_";
}
}
}
In an interesting interview, Donald Knuth disses unit tests.
[The] idea of immediate compilation and "unit tests" appeals to me only rarely, when I’m feeling my way in a totally unknown environment and need feedback about what works and what doesn’t. Otherwise, lots of time is wasted on activities that I simply never need to perform or even think about. Nothing needs to be "mocked up."
He's also not fond of XP:
Let me just say that almost everything I’ve ever heard associated with the term "extreme programming" sounds like exactly the wrong way to go...with one exception. The exception is the idea of working in teams and reading each other’s code. That idea is crucial, and it might even mask out all the terrible aspects of extreme programming that alarm me.
Oh, and reusable code is also something he takes a swipe at:
I also must confess to a strong bias against the fashion for reusable code. To me, "re-editable code" is much, much better than an untouchable black box or toolkit. I could go on and on about this. If you’re totally convinced that reusable code is wonderful, I probably won’t be able to sway you anyway, but you’ll never convince me that reusable code isn’t mostly a menace.
So, let's see. He doesn't like unit tests, XP, or reusable code. These are three things that I suspect many reading this might be in favor of. In fact, if it were just about anyone else saying this, the