Aristotle's Journal Aristotle's use Perl Journal en-us use Perl; is Copyright 1998-2006, Chris Nandor. Stories, comments, journals, and other submissions posted on use Perl; are Copyright their respective owners. 2012-01-25T02:04:07+00:00 pudge Technology hourly 1 1970-01-01T00:00+00:00 Aristotle's Journal Applying ideas from new languages in your old standbys <p>I just wrote the following Perl&#160;5 code. Consider the bolded bits, and think about what the code would have looked like if I had tried to write it in any equally DRY way using <code>map</code>.</p><p> <code>sub new { <br>&#160; &#160; my $class = shift; <br>&#160; &#160; my $self = bless { @_ }, $class; <br>&#160; &#160; my $builder = $self-&gt;builder; <br>&#160; &#160; my $content = $self-&gt;content; <br> <br>&#160; &#160; <b>my ( @gather, @take );</b> <br> <br>&#160; &#160; <b>for my $r (</b> XML::Builder::Util::is_raw_array( $content ) ? @$content : $content <b>) { <br>&#160; &#160; &#160; &#160; @take = $r;</b> <br> <br>&#160; &#160; &#160; &#160; if ( not Scalar::Util::blessed $r ) { <br>&#160; &#160; &#160; &#160; &#160; &#160; <b>@take =</b> $builder-&gt;render( @_ ) <b>if</b> XML::Builder::Util::is_raw_array $r; <br>&#160; &#160; &#160; &#160; &#160; &#160; <b>next;</b> <br>&#160; &#160; &#160; &#160; } <br> <br>&#160; &#160; &#160; &#160; if ( not $r-&gt;isa( $builder-&gt;fragment_class ) ) { <br>&#160; &#160; &#160; &#160; &#160; &#160; <b>@take =</b> $builder-&gt;stringify( $r ); <br>&#160; &#160; &#160; &#160; &#160; &#160; <b>next;</b> <br>&#160; &#160; &#160; &#160; } <br> <br>&#160; &#160; &#160; &#160; <b>next</b> if $builder == $r-&gt;builder; <br> <br>&#160; &#160; &#160; &#160; Carp::croak( 'Cannot merge XML::Builder fragments built with different namespace maps' ) <br>&#160; &#160; &#160; &#160; &#160; &#160; if $r-&gt;depends_ns_scope; <br> <br>&#160; &#160; &#160; &#160; <b>@take =</b> $r-&gt;flatten; <br> <br>&#160; &#160; &#160; &#160; my ( $self_enc, $r_enc ) = map { lc $_-&gt;encoding } $builder, $r-&gt;builder; <br>&#160; &#160; &#160; &#160; <b>next</b> <br>&#160; &#160; &#160; &#160; &#160; &#160; if $self_enc eq $r_enc <br>&#160; &#160; &#160; &#160; &#160; &#160; # be more permissive: ASCII is one-way compatible with UTF-8 and Latin-1 <br>&#160; &#160; &#160; &#160; &#160; &#160; or 'us-ascii' eq $r_enc and grep { $_ eq $self_enc } 'utf-8', 'iso-8859-1'; <br> <br>&#160; &#160; &#160; &#160; Carp::croak( <br>&#160; &#160; &#160; &#160; &#160; &#160; 'Cannot merge XML::Builder fragments with incompatible encodings' <br>&#160; &#160; &#160; &#160; &#160; &#160; . " (have $self_enc, fragment has $r_enc)" <br>&#160; &#160; &#160; &#160; ); <br>&#160; &#160; <b>} <br>&#160; &#160; continue { <br>&#160; &#160; &#160; &#160; push @gather, @take; <br>&#160; &#160; }</b> <br> <br>&#160; &#160; $self-&gt;{'content'} = \@gather; <br> <br>&#160; &#160; return $self; <br>}</code> </p><p>I don&#8217;t know whether this way of writing the code would have occurred to me if I hadn&#8217;t seen the construct in Perl&#160;6. I suspect not.</p> Aristotle 2009-12-04T10:35:19+00:00 journal PSA: There is no such thing as a &#8220;RESTful URL&#8221; <p>Thank you for your attention.</p> Aristotle 2009-10-09T19:10:18+00:00 journal use.perl, now with even more brokenness :-( <p>Requesting <a href=";content_type=atom">;content_type=atom</a> now comes back with a 500 error and an empty body&#8230; and I can&#8217;t find any other way to read use.perl&#8217;s full journal section with an aggregator. Or even with a browser &#8211; if you leave off the <code>content_type</code> parameter, you&#8217;ll see the page comes back empty.</p><p>(There&#8217;s <a href=";content_type=atom">;content_type=atom</a>, but that contains links only to the most recently updated journals, with static IDs, so it&#8217;s of little use as a way of tracking the site.)</p> Aristotle 2009-09-17T23:28:05+00:00 journal Sudden involuntary cosmopolitan <p>[I wrote the following over the course of 2 days, about two weeks ago.]</p><dl> <dd> <i>For want of a nail the shoe was lost.<br>For want of a shoe the horse was lost.<br>For want of a horse the rider was lost.<br>For want of a rider the battle was lost.<br>For want of a battle the kingdom was lost.<br>And all for the want of a horseshoe nail.</i> </dd></dl><p>Two days ago [<i>on Aug 6</i>], I checked out from the hotel where I&#8217;d been staying for YAPC. Unfortunately, I tried to to pay with my credit card.</p><p>Well, it has all the trappings of a regular credit card (or almost&#8230;), but it&#8217;s actually a debit card. I have no interest in taking out loans, but I wanted to be able to order things online from outside the German web, and Germany-typical payment modalidities are unknown elsewhere (whereas credit cards are somewhat unusual in Germany, in turn). So when my bank sent me an offer for one of those cards, I thought it was just perfect. I have since used this card some two dozen times, and with a single exception it always worked, both online and off. As a matter of fact, I used it on the day of my Lisbon-bound air journey.</p><p>That&#8217;s how I came to have a have a credit card that&#8217;s not a <em>credit</em> card.</p><p>Now, since I was travelling to a foreign country, I figured that having the hotel money on a credit card would be the safest bet for paying. Unfortunately it appears that unbeknownst to me, <em>sometimes</em> you need a PIN to pay with this sort of card. I&#8217;d received the PIN in the mail when I got the card, but I&#8217;d never needed it, so I hadn&#8217;t bothered to memorise the PIN.</p><p>And of course, it came up at the hotel. We tried a bunch of things, including me phoning home and asking the folks if they could scare up the slip with the PIN. They found one, except that number didn&#8217;t work.</p><p>Ironically, the MasterCard for my regular German debit account would have worked just fine (of course I do know the PIN for <em>that</em>) &#8211; so if I&#8217;d simply done nothing, I wouldn&#8217;t have had any trouble. But of course I didn&#8217;t have enough money in that account: it was all on the credit card account.</p><p>Heck, if I&#8217;d carried it all as cash that would have worked brilliantly.</p><p>Now, I went to check out at 15:00 &#8211; 50 minutes before the check-in counter at the airport would close, in turn the usual 30 minutes before the flight, which was at 16:20. The airport is barely over 5 taxi minutes away from the hotel, so 50 minutes seemed to be plenty early. In the end, when I started to get antsy and mentioned my flight, the hotel manager let me go without paying then and there.</p><p>But you know what&#8217;s coming, or else the story wouldn&#8217;t be so freakishly long: the credit card hold-up took long enough that I missed the check-in by 3 minutes. I should have called the airline to tell them I&#8217;d be in late &#8211; they could have held the check-in open a bit longer if I&#8217;d done that, and none of this would have happened. But that didn&#8217;t cross my mind; besides, I&#8217;ve been late a minute or two late for check-in at my home airport, and they always managed to get me on the flight anyway, so I thought that as long as I was close enough, I&#8217;d make it.</p><p>So I arrive at the airport and the clerks at the check-in counter tell me I&#8217;m late, and that there is absolutely no way that they can re-open check-in for me. I need to run over to the Air Berlin ticket office. Now I&#8217;m seriously worried. The flight is taking off in 26 minutes. Maybe I can make some kind of luggage arrangement so I can get on the plane and have the luggage trail me later; or something. I run over to the office&#8230; to be greeted by a sign that says &#8220;be back soon&#8221;.</p><p>Two minutes pass. Three minutes. Five. Eight. Twelve. Twenty now, the flight is going in 10 minutes &#8211; and no one&#8217;s at the office! Finally, some 30 minutes later, a lady shows up. I tell her what&#8217;s going on and she tells me there&#8217;s nothing to be done. My only option is to re-book the flight for next day. It&#8217;ll be a bunch extra, of course. Just great. Still, what can I do? So I tell her to book it; she asks for my transaction number and sets to work.</p><p>Then I think to myself, wait a minute, I shouldn&#8217;t be so impulsive. Let&#8217;s see if another airline has a flight out sooner and/or cheaper. So I tell her to abort the procedure. She asks, is that really what I want? Am I sure? Well, so I say, yes I am sure. OK, she says, and she does it. Now this transaction number is void, she announces; if I decide to take tomorrow&#8217;s flight, I&#8217;ll have to book a completely new ticket. How much? Nearly twice as much. This is the point in our program where I barely hold myself back from cursing &#8211; couldn&#8217;t she have told me that 30 seconds earlier?!?!?</p><p>Because now I have a <em>serious</em> problem: the credit card does not have that much money on it. I do have the money, but it&#8217;s in a regular German debit account &#8211; and since it takes 2&#8211;4 days for withdrawals from such accounts to go through, that money is non-existent for the purposes of booking a flight on the spot.</p><p>What to do?!</p><p>I do what I was going to: look around for the prices at other airlines.</p><p>Among the many other things Lisbon airport appears to lack is a central ticket office. </p><p>That means I hit up half a dozen airline ticket offices, one by one, asking for flights to my airport. It&#8217;s 16:30 so I&#8217;m under time pressure: the offices all close soon, including the Air Berlin one, which closes at 17:30. I get the same picture everywhere: significantly more expensive than the ticket offering I already have, or no flights to my airport, or all seats booked solid for a week, or whatever.</p><p>And now?!</p><p>Ah! I paid a small cash advance at the hotel. If they&#8217;ll let me have it back, that money plus the credit card would cover the flight &#8211; with a 20&#8364; allowance left over. I won&#8217;t have enough cash on me to pay the long-term parking lot ticket at the airport at home (which comes to 40&#8364;), but heck, even if I can&#8217;t get at my car right away, at least I&#8217;ll be back in my town. So I call and ask. The poor manager sighs and says OK.</p><p>The taxi to the hotel and back eats 7&#8364; out of my allowance. Ugh. If I run out of money, I&#8217;ll be screwed&#8230; so far it looks like it&#8217;ll suffice, and I can only hope.</p><p>Triumphantly I return to the Air Berlin office 5 minutes before it closes. I smile at the lady and tell her the good news. She smiles back and says she can only take either all cash, or charge it all against a credit card, but not both. That&#8217;s even though the Air Berlin office at my home airport <em>could</em> and <em>did</em> book a flight in that exact way just days before.</p><p>Of course, without the PIN, the airport ATM won&#8217;t let me convert the credit card allowance into cash either.</p><p>At this point, I&#8217;m feeling pretty defeated.</p><p>At least I am left with some 90&#8364; in cash, so I&#8217;m not completely stranded in this airport. Unlike the story of another time where I came very close &#8211; an altogether separate level of unpleasantness &#8211;, I have enough fuel to communicate and get around town a bit, which gives me the leeway I need to hatch a plan. And I should probably have enough left at the end that paying the parking lot fee back home shouldn&#8217;t be a problem either.</p><p>So I relax a lot. Of course I&#8217;m still worried, since I don&#8217;t have an actual solution for how I&#8217;ll book my flight, just the means to implement one. I assume I&#8217;ll hit the social meeting, where I will hopefully find people who will be able to help me in whatever way they will. I was previously disappointed that I was going to have to miss the meet since it was announced so late and my return flight was long booked at that point. &#8220;I guess that solves that&#8221;, I think to myself.</p><p>I don&#8217;t know where the meeting will be, though. So the plan will have to involve going online somehow, to check the conference wiki. Since there is no free wireless at the airport, this implies going back to the venue where I&#8217;ll be able to use the conference WLAN, which is still operative because there are tutorial sessions still running over the next few days. So the venue is my next destination.</p><p>With the drop of my stress levels, exhaustion kicks in, so I look for seats in a quiet spot in the airport. I want to sit for a while to unwind a little and collect myself before launching into the next attack.</p><p>Lisbon airport does not seem to have any quiet spots.</p><p>I wander aimlessly&#8230; and run into Roz, who is at the airport way early for her flight. The hotel wouldn&#8217;t have her any more. So I sit down and tell her the story so far. We chat for a while, then her check-in counter opens. I tag along as she goes to checks in her luggage.</p><p>Near the counter, we run into evdb, whom I didn&#8217;t know personally, and probably would have missed if I wasn&#8217;t in Roz&#8217;s company. We tell him the story again, and to my astonishment he immediately asks in what way he can help &#8211; lend me cash, book my flight, whatever. I&#8217;m speechless at first, and of course immensely relieved and grateful. Maybe things will be OK after all.</p><p>The first thing we try is that evdb asks the counter, then some ticket offices, for flights; he has a few tacks I wasn&#8217;t aware of. Unfortunately, covering more airlines does not change the results: no tickets, booked solid, etc.</p><p>Next angle of attack: having him book my flight online. However, try as we might we cannot go online at the airport: more than half a dozen wireless ISPs offer connectivity, but none of their payment procedures work.</p><p>At that point, the two of them are running out of time. As a last resort gesture, evdb withdraws a fistful of cash and hands it to me &#8211; we&#8217;ll be in touch by email for paying it back &#8211;, before they leave to board their flight. I thank him and we say goodbyes.</p><p>Now I am really good cash-wise. I expect to need very little of his money, but I&#8217;ll be able to absorb surprise expenses without breaking a sweat. So I calm down a little more at the same time as my worry also increases back due to the fact that my situation is not yet sorted out after all.</p><p>After a short rest, I re-embark on my initial plan to find the meet. The part that follows here is largely boring &#8211; in the really good sense of the word. I take a taxi to the conference venue, connect to the wireless there, check the wiki, take notes, hit the metro, make the trip to the destination as planned, and find the pub where the meeting is taking place, all without a hitch. This stretch of the story passes completely uneventfully &#8211; what a welcome change <em>that</em> is.</p><p>So I arrive at the meet-up, and of course conversation turns to my story immediately, despite my not wanting to turn it that way myself, at least not right away &#8211; I want to <em>ask</em> for help, not beg for it or demand it. As soon as I&#8217;ve explained the situation, Nobull offers to help me out with cash or a flight booking.</p><p>Now the problem is, how do we book the flight? There is no free wireless around, only several closed networks. Jos&#233; mentions that one of the nearby caf&#233;s has an open access point, so we wander over to mooch some access. Since the signal is weak, we wander a little closer, but soon, the network disappears entirely. Our guess is the bartender or someone else in the shop saw us milling about with a laptop and turned off the access point. Bummer. So we walk back to the Irish Pub, where there is a strong <a href="">PT-WIFI</a> signal, and eventually we find one of the Portuguese mongers has an account with them, though no laptop. Another local monger produces his laptop, and a connection is eventually established, so I can finally get online to book the flight, leaving the payment details for Nobull to enter &#8211; who uses his wife&#8217;s credit card.</p><p>That &#8220;booking confirmed&#8221; screen is a truly international effort.</p><p>And with all that, my journey back is finally secured and I relax.</p><p>After socialising for a few hours, I take a taxi to the airport to sit out the rest night and the following noon. It&#8217;s nearly 2am and I don&#8217;t care where I&#8217;ll stay anyway, just as long as I know I&#8217;m going to get out of this hell hole OK. I&#8217;ll deal just fine with any inconvenience in the meantime.</p><p>I only manage to get a few hours of fitful sleep of course, and those are while sat in a hard chair&#8230; but I end up less exhausted than I should nominally be. In the morning, I hit the pay phones and call home to tell some very worried folks the good news.</p><p>There are wall plugs where I&#8217;m spending the night, so I can mooch electricity off the airport to feed my laptop battery. I won&#8217;t have to worry about conserving battery charge &#8211; more or less the only bright spot in this entire ordeal.</p><p>After wiling away the hours, the time has finally come: the check-in counter opens. Inwardly jubilant, I stroll over and hand the clerk my passport.</p><p>&#8230; Who looks up at me and says there is a problem, and would I please head back to the ticket office to sort it out? I curse. I was hoping never to have to see that lady&#8217;s friendly smile and her maddeningly intact throat again.</p><p>Turns out it&#8217;s in fact two problems.</p><p>Nr. 1: originally, the ticket payment had been refused by my bank because it fell into an untimely moment. Since I booked my flight relatively late, I had called Air Berlin and asked them whether I could send the money by wire transfer, because if they tried to re-withdraw, it might get there too late. (As previously mentioned, German wire transfers take several days to complete, because the banks like to sit on that money so they can keep it warm.) The lady on the phone OKed this. But it appears that even though the ticket was then considered covered &#8211; after all, they let me take my inbound flight! &#8211;, <em>somehow</em> they subsequently lost track of the money. Don&#8217;t ask me how they managed that.</p><p>Nr. 2: there was a problem with Nobull&#8217;s credit card &#8211; so he&#8217;ll have to provide written and signed confirmation of the booking in some form (fax acceptable).</p><p>Without these problems being fixed, they&#8217;re not going to let me on the plane &#8211; but of course, they <em>very well <strong>will</strong> </em> ding me for the full price of the flight I booked and which I am not being allowed onto.</p><p>Unfortunately I don&#8217;t have Nobull&#8217;s cell number. He had put his home phone on file when we booked the flight, so I ask the Air Berlin clerk on the phone to try that one. No luck: no one is home, only a mailbox answers.</p><p>I have 15 minutes to sort out the situation before the check-in closes &#8211; if I don&#8217;t get it fixed in that time, I miss the flight.</p><p>Some options, those are. I know what it will come down to, and surely so do you as you&#8217;re reading this.</p><p>I head back to the pay phones and call my folks to tell them the &#8211; decidedly worse this time around &#8211; news. They mention that they&#8217;ve called the colleague and told him what happened, and that he offered to sort out any situation I might need help with. Which is something I was hoping to avoid &#8211; he&#8217;s bailed me out of more than enough situations already. But it&#8217;s no longer a matter of choice.</p><p>So I call him and give him the run-down. He tells me to call back in half an hour. I do. He tells me he has called Air Berlin and tried the entire battery of approaches in the book &#8211; all unsuccessfully. They simply will. Not. Budge. He tells me to call back again later.</p><p>The next time I call, he is at the airport back home, and speaking to the Air Berlin clerks there. He booked <em>another</em> flight with Air Berlin &#8211; unfortunately! They seem to be the only option for getting out of Lisbon right now. They&#8217;ve offered him to rebook today&#8217;s missed flight, which at least means it won&#8217;t be much extra. When the clerk asks him to pay the difference in cash, he has the presence of mind to ask whether that will <em>really</em> suffice &#8211; is the ticket actually covered? Good thing too, because she calls the headquarters in Berlin and finds out that no, the rest of the money was still pending. In other words, if he had relied on the legendary diligence and help of the Air Berlin staff, I would have been slated for another day stuck in Lisbon airport.</p><p>But by all appearances, at this point, things are finally sorted.</p><p>I sit at the airport a while longer and notice that I don&#8217;t manage to make much progress through my reading material: I keep dozing. So I throw fiscal caution to the wind and decide to see if I can find a room in a hotel. It would also be nice to have an opportunity to shower and brush my teeth. But after calling several hotels I know to be cheap, I find they are all booked solid. So for another night, sleeping in the airport like a bum it is.</p><p>This time, I&#8217;m not particularly relaxed. I can only imagine what twist of fate is going to await me tomorrow when I try to board my flight. The hours pass a lot more slowly this time. By noon, the sleep deprivation is catching up with me; I fall asleep whenever I sit down. I discover a restaurant hidden away on a third floor in the airport, where there are soft chairs &#8211; even couches! The tendons in my neck are eternally grateful.</p><p>I don&#8217;t sleep all that much or all that well anyway, of course.</p><p>Which is just as well, because it means this time I am at the check-in counter very early. With quite a bit of trepidation, I hand my passport to the clerk&#8230; who checks me in without any hesitation. I can barely believe it&#8230; I am going home! It&#8217;s all I can do not to do a few fist-pumps.</p><p>The rest of the journey is, finally and thankfully, completely uneventful.</p><p>Thanks, Air Berlin. I made my own mistakes to be sure, but it was your being deeply true to German customer service tradition that really granted me an opportunity to properly enjoy the hell out of this bloody airport &#8211; to the tune of half a thousand bucks up in air (quite literally). Double the trip price for double the hell &#8211; what more could one ask for?</p> Aristotle 2009-08-18T17:57:48+00:00 journal On Delight <p>A while ago, I wrote <a href=";cid=69689">a rather pessimistic comment</a> [edited and expanded for this post]:</p><dl> <dd> <p>I often see utterances along the lines that we in the Perl community need to recognise how bad we are at design and that we should get in touch with willing real designers who can do real design for us. I think that this precise utterance points to the reason why Perl applications don&#8217;t have good design.</p><p>Why would real designers even feel motivated to participate in a community that has no specific appreciation for what they are doing, nor, therefore, any ability to provide useful feedback? Anyone with highly specialised skills has this experience, and we as programmers do too: it&#8217;s frustrating to develop software for people who cannot appreciate the coolness of particular interesting bits, have no idea what feedback would be useful, and don&#8217;t know what they want, or what they need, or how to communicate it. &#8220;Real designers&#8221; would feel alien in the Perl community.</p><p>Paul Graham once wrote about American car companies that because the executives had no taste themselves, the cars produced by their companies ended up ugly. It&#8217;s not that the executives designed the cars, but they didn&#8217;t know how to tell who to hire to make tastefully designed cars nor did they know to appreciate the work any designer might have done, or how to build the culture and structure to foster design and designers. Microsoft has the same problem.</p><p>Perl does not have a culture of appreciating design. This &#8220;we need real designers so they can put a good coat of painting on our crud&#8221; mentality is perfectly symptomatic of that.</p><p>I appear to be one of the few Perl programmers who have any direct appreciation of (if not particularly great skill at) design; it sure feels lonely out here sometimes. In contrast, both PHP and Rails are infested with these people. I have seen reams of postings about typography out of the Ruby people I follow, f.ex., and not a single thing about it from a Perl person, if memory serves. Even the Python community seems to have some awareness, even if less than the others.</p><p>That means whatever full-fledged web apps these people write, they&#8217;re going to look at least passable, out of the box; even if the developers themselves are not great designers, they&#8217;ll know to build their apps from things that give them good defaults, and they&#8217;ll be able to recognise better designs or truly good detail tweaks when they come along (and good design is <em>all</em> about the details).</p><p>In his essay about <a href="">Why software sucks</a>, Scott Berkun includes a translation table for what people mean when they say something sucks, and the entry of interest there is &#8220;It&#8217;s so ugly I want to vomit just so I have something prettier to look at&#8221;. I think a huge reason for the marketing problems that Perl is having is the simple fact that a lot of Perl stuff <em>looks ugly</em>.</p><p>The few widely-known Perl things that don&#8217;t, don&#8217;t advertise Perl as a primary ingredient of theirs. The only exception I can readily think of are the SixApart applications, but I bet that even then, only MovableType is widely associated with Perl &#8211; and MT&#8217;s sun has long set.</p><p>Perl is associated with ugliness. I seriously think that this sets an expectation that is part of why the ridiculous &#8220;executable line noise LOL&#8221; meme persists across the Reddits and the Twitters of the world. There is a huge number of people who write stuff like that even though they have never actually written any Perl code, or even <em>seen</em> any &#8211; except maybe for the various iconic JAPHs, quines, golfs and monstrous regular expressions. And their direct experience of Perl apps and sites is that they tend to look nauseatingly ugly. Is it any wonder that they&#8217;ll readily pick up and propagate the &#8220;random punctuation LOL&#8221; meme? Bad design is definitely an inherent aspect of Perl&#8217;s marketing problem.</p><p>Frankly, I feel pessimistic about the prospects of the Perl community in this respect. You can&#8217;t suddenly imbue a culture with a sense for design when it has never known the first thing about design nor had any appreciation for it.</p></dd></dl><p>Imagine, then, my delight at discovering Phillip Smith&#8217;s <a href="">article about the typography of the TPF logo and his suggestions for consistent branding in the Perl community</a>: this is exactly the sort of thing I was talking about. Is exactly the sort of thing we need.</p><p>Thanks, Phillip.</p> Aristotle 2009-08-15T21:00:53+00:00 journal I think I heard something like this from Nick <p> <cite> <a href="">Zed Shaw</a><nobr> <wbr></nobr></cite>:</p><blockquote><div><p>Quite frankly, we&#8217;re dying off because the companies who use our software do not give back. The irony of the situation is that, in order to improve my motivation to do open source, I have to charge for it.</p></div> </blockquote> Aristotle 2009-07-15T01:41:11+00:00 journal Polite fiction and the elephant in the room <p> <cite> <a href="">Rafa&#235;l Garcia-Suarez</a><nobr> <wbr></nobr></cite>:</p><blockquote><div><p>Who&#8217;s trying to be realistic by attempting to release software with some quality expectations, notably by making the upgrade process seamless and introducing as little bugs as possible?</p></div> </blockquote><p>The problem with that argument is that 5.10.0 <em>did</em> contain regressions.</p><p>That the rarity of releases is justified by the high quality standards being adhered to is, unfortunately, merely polite fiction. What it all boils down to is simple:</p><p> <em>Shit happens.</em> </p><p>No matter how much we&#8217;d like for it to be otherwise, <em>shit happens</em>. The release process needs to be reality-based: it needs to deal with the very real fact that <em>shit happens</em> and <em>has in fact already happened</em>. The only realistic way to deal with that is for releases to be easy enough that the shit that <em>does</em> invariably happen can be dealt with soon enough, so that it won&#8217;t cause too many people too much trouble.</p><p>And the best way to make releases easy is to make them frequently.</p><p>And the simplest (though not only) way to ensure that is to put them on a schedule.</p><p>That is all.</p><p> <cite> <a href="">Rafa&#235;l Garcia-Suarez</a><nobr> <wbr></nobr></cite>:</p><blockquote><div><p>Whenever I hear someone saying <i>we should have regular releases</i>, I hear <i>we should release when Venus enters Pisces</i>.</p></div> </blockquote><p>There is your misunderstanding, then: what they are really saying is <i>we need a reliable plan for dealing with accidents</i>. If you can make that happen without sticking to scheduled releases, that&#8217;s fine too.</p><p>Yes: releasing often does not affect the quality of software by itself. But releasing it rarely almost certainly <em>will</em> &#8211; negatively. Because <em>shit happens</em>.</p><p>I don&#8217;t like it either! But that&#8217;s reality for you. I doesn&#8217;t care what you like. It just is.</p> Aristotle 2009-07-05T15:41:32+00:00 journal Anyone want to adopt Getopt::Auto? <p>I took over <a href="">this module</a> from Simon Cozens nearly 5 years ago when <a href="">he gave away his modules</a>, but never got around to any of my ideas for it. What was I thinking? In hindsight, I don&#8217;t get around to my own ideas for comparable lengths of time; I should never have applied for it.</p><p>I did start a refactor at some point that&#8217;s still sitting in my projects directory, but in all honesty I didn&#8217;t get very far before losing steam. I even delayed writing this note, thinking I&#8217;d get around to making my changes available, but they&#8217;re insubstantial and unfinished and it really doesn&#8217;t matter any more. There&#8217;s a patch in the RT queue that&#8217;s been sitting there for years. I didn&#8217;t think the patch too great, but it would have been better to apply it and release than leave it in limbo waiting for a reformulation that I&#8217;d never get around to.</p><p>It is time to admit defeat. I&#8217;ve been a bad steward.</p><p>Is there anyone reading this who cares about the module? Want to take it over?</p> Aristotle 2009-06-18T01:25:00+00:00 journal I think this module should exist <p>The following should all do (almost!) the same:</p><blockquote><div><p> <tt>pipe( 'zcat' ) | [ tar =&gt; x =&gt; '-O' ]<br>Pipe-&gt;new( 'zcat' ) | Pipe-&gt;new( tar =&gt; x =&gt; '-O' )<br>Pipe-&gt;new( 'zcat' )-&gt;std( Pipe-&gt;new( tar =&gt; x =&gt; '-O' ) )<br>Pipe-&gt;new( 'zcat' )-&gt;stdout( stdin =&gt; Pipe-&gt;new( tar =&gt; x =&gt; '-O' ) )<br>Pipe-&gt;new( 'zcat' )-&gt;pair( stdout =&gt; stdin =&gt; Pipe-&gt;new( tar =&gt; x =&gt; '-O' ) )</tt></p></div> </blockquote><p>Thoughts? (Aside from module naming.)</p><p>(Rationale: setting up complex pipes manually with IO::Pipe/<code>socketpair</code> + <code>fork</code> results in brain-melting code. Shell shows, however, that pipes can easily be described declaratively. Why is there, 20 years after the inception of Perl, no module that provides a piping API congruent with shell syntax?)</p> Aristotle 2009-02-12T06:53:32+00:00 journal A better quoting-aware word-splitting regex <p>Anyone who has read Jeff Friedl&#8217;s book can write a decent basic word splitter regex off the cuff:</p><blockquote><div><p> <tt>m{ \G [ ]* (<br>&nbsp; &nbsp; " [^"\\]* (?: \\. [^"\\]* )* "<br>&nbsp; &nbsp; |<br>&nbsp; &nbsp; [^ ]+<br>) }gsx</tt></p></div> </blockquote><p>I have written and used this many times. What has always bugged me about it, however, is that it captures the delimiters along with the content, so afterwards you have to do something like this to the captured value:</p><blockquote><div><p> <tt>s!\A"(.*)"\z!$1!;</tt></p></div> </blockquote><p>This is&#8230; not pretty.</p><p>Of course, you could use two captures:</p><blockquote><div><p> <tt>m{ \G [ ]* (?:<br>&nbsp; &nbsp; " ( [^"\\]* (?: \\. [^"\\]* )* ) "<br>&nbsp; &nbsp; |<br>&nbsp; &nbsp; ( [^ ]+ )<br>) }gsx</tt></p></div> </blockquote><p>But then you need to check which of the two captures has the value &#8211; is it in <code>$1</code> or <code>$2</code>? So this is still inelegant. The pattern has already done all the work of examining the string &#8211; why can&#8217;t it provide its results in an invariant form?</p><p>The problem is that the presence of the trailing quote must be dependent on the presence of a leading quote, so you must keep the quotes inside the alternation, so there is no way to avoid having either two distinct captures that exclude the quotes or a single broad capture that includes them.</p><p>Except, of course, you don&#8217;t have to and there is. True enough: when you rely on the matcher to pick an alternation implicitly, the quotes must be included in the alternation. But by using an extended regular expression feature (that has been marked experimental for a decade &#8211; what&#8217;s up with that?), namely conditional matches, you can make the match of the trailing quote conditional on the leading quote independently of an alternation.</p><blockquote><div><p> <tt>m{ \G [ ]*<br>&nbsp; &nbsp; &nbsp;(")?<br>&nbsp; &nbsp; &nbsp;( (?(1)<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;[^"\\]* (?: \\. [^"\\]* )*<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;|<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;[^ ]+<br>&nbsp; &nbsp; &nbsp;) )<br>&nbsp; &nbsp; &nbsp;(?(1)")<br>}gsx</tt></p></div> </blockquote><p>And of course you can (and must, in this case) use a conditional match in the middle to explicitly specify which of the cases to pick, depending on the presence of the leading quote.</p><p>This way, you can match surrounding delimiters in a captured alternation for some of its cases but not others, without having to include the delimiters in the capture.</p><p>Note that the interesting capture is now <code>$2</code>, not <code>$1</code> &#8211; we need the first capture for the quote, since conditional matches can only use captured groups as conditionals. However, the matched word is <em>always</em> in <code>$2</code>, regardless of whether quotes were involved. Furthermore, <code>$1</code> has now turned into a true boolean flag, whereas previously this information had to be inferred (however easily).</p><p>This pleases me.</p> Aristotle 2009-01-31T15:47:15+00:00 journal I&#8217;ve heard something like this somewhere before&#8230; <p> <cite> <a href="">Reginald Braithwaite</a><nobr> <wbr></nobr></cite>:</p><blockquote><div><p>Also, I reject the argument that <code>responds_to?</code> checking is buggy because some people write <code>method_missing</code> magic that breaks it. I reject the argument because I reject as buggy any code such that object <code>o</code> responds to method <code>m</code> but <code>o.responds_to?(:m) =&gt; false</code>. <strong>If you implement your own <code>method_missing</code> for a class, you should almost always implement your own <code>responds_to?</code> as well.</strong> </p></div> </blockquote> Aristotle 2009-01-14T05:36:30+00:00 journal Sound familiar? <p> <cite> <a href="">Paul Haahr</a><nobr> <wbr></nobr></cite>:</p><blockquote><div><p>Exceptions are also used to implement the <code>break</code> and <code>return</code> control flow constructs</p></div> </blockquote> Aristotle 2008-12-23T18:10:51+00:00 journal Perl&#160;Buzz mentions xkcd again <p>Wildly hit-or-miss weblog Perl&#160;Buzz has had to <a href="">mention xkcd again</a>. Why can&#8217;t you people leave the guy alone?</p><p>(SCNR.)</p> Aristotle 2008-12-20T06:12:09+00:00 journal A simple `unfold` in Perl <p>For many years, a very simple issue about writing an <code>unfold</code> in Perl&#160;5 has stymied me.</p><p>(An <code>unfold</code> is the opposite of <code>fold</code>, of course. The latter is better known to Perl programmers as <code>reduce</code> from List::Util, which is a function that takes a block and a list and returns a single value by repeatedly applying the block to successive values. An <code>unfold</code> does the opposite: it takes a block and a single value and then repeatedly applies the block to the value, accumulating the return values to eventually return them as a list.)</p><p>A good <code>unfold</code> as I want it will allow the same thing as <code>map</code> does: for any iteration to return any number of elements, including zero. Also, the elements returned from an iteration must be passed out of the block as its return value for that iteration &#8211; otherwise you might as well write a conventional loop instead.</p><p>But then you have a problem: how do you know when the block is finished generating output? There is no possible return value you could use for this. The Python implementations of this function that I&#8217;ve seen invariably expect an exception to be thrown to signal the end of the iteration, which struck me as profoundly un-Perlish. But how else do you signal the termination condition out of band?</p><p>I could think of no sensible solution given the combination of constraints that I adopted. Until an hour ago, that is, when a flash of inspiration struck me.</p><p>I&#8217;ll let the code speak for itself.</p><blockquote><div><p> <tt>sub induce (&amp;$) {<br>&nbsp; &nbsp; my ( $c, $v ) = @_;<br>&nbsp; &nbsp; my @r;<br>&nbsp; &nbsp; for ( $v ) { push @r, $c-&gt;() while defined }<br>&nbsp; &nbsp; @r;<br>}</tt></p></div> </blockquote><p>In this version, the initial scalar value is made available to the block in <code>$_</code> (somewhat cleverly done via <code>for</code>, which is necessary because <code>local $_</code> has lots of subtle pitfals), and the block is called repeatedly as long as <code>$_</code> is defined. The block therefore signals the detection of its exit condition by undefining <code>$_</code>.</p><p>It&#8217;s that simple. It&#8217;s so simple I cannot believe it took me so many years to think of it, but there it is.</p><p>Here is a silly example:</p><blockquote><div><p> <tt>my @reversed = induce { @$_ ? pop @$_ : undef $_ } [ 1<nobr> <wbr></nobr>.. 10 ];</tt></p></div> </blockquote><p>Of course, the eagle-eyed will immediately notice a problem: this produces 11 return values, the last one being the <code>undef</code> that got returned from <code>undef $_</code>. The fact that <code>induce</code> will collect the value of the final iteration rather than throw it away was a conscious design decision: there are two cases for the return value of the final iteration, either it is useful or not. What happens if it it&#8217;s not useful, but would be collected? Then you have to suppress it, which is easy. What happens if it&#8217;s useful, but would be thrown away? Then you have to arrange for the block to remember state so it can return the useful value first and then signal the termination condition on its next invocation. Since it is so much easier to suppress useless values that would be kept than to retain useful values that would be dropped, I chose to have <code>induce</code> keep the final iteration&#8217;s value.</p><p>There are various ways to arrange the suppression in the above example. One of them is to check more eagerly for whether another iteration will be necessary:</p><blockquote><div><p> <tt>my @reversed = induce { my @l = @$_ ? pop @$_ : (); @$_ or undef $_; @l } [ 1<nobr> <wbr></nobr>.. 10 ];</tt></p></div> </blockquote><p>That is clearly extremely awkward. A simpler (and insignificantly less efficient) approach is to suppress the value returned by the <code>undef</code> function:</p><blockquote><div><p> <tt>my @reversed = induce { @$_ ? pop @$_ : ( undef $_, return ) } [ 1<nobr> <wbr></nobr>.. 10 ];</tt></p></div> </blockquote><p>That&#8217;s much better, but still not pretty. In particular, that <code>return</code> can be awkward to place due to precedence rules. In the above example it requires those annoying parens. (Try it: the code compiles but breaks without them.) Instead, we&#8217;ll take a page from Javascript and write another function:</p><blockquote><div><p> <tt>sub void {}</tt></p></div> </blockquote><p>No, seriously. The point of this function is, well, to take any arguments you pass it, throw them away, do nothing, and return nothing &#8211; most importantly, to return an empty list in list context.</p><p>I admit that writing this, err, function greatly amused me. But the end result is quite satisfying:</p><blockquote><div><p> <tt>my @reversed = induce { @$_ ? pop @$_ : void undef $_ } [ 1<nobr> <wbr></nobr>.. 10 ];</tt></p></div> </blockquote><p>So these two functions, <code>induce</code> (named like this instead of <code>unfold</code>, of course, to contrast with List::Util&#8217;s <code>reduce</code>) and <code>void</code>, will probably start appearing in my small scripts from now on.</p> Aristotle 2008-11-08T16:49:41+00:00 journal Identifier clash <p> <a href="">Pip</a> <a href="">pip</a>, ol&#8217; bean!</p> Aristotle 2008-10-29T00:25:03+00:00 journal Installing a perl with a minimal, collapsed directory layout <p>I just started getting onto the one-perl-per-app train. By default, <code>Configure</code> wants to set up an installation with a deeply nested directory layout so as much as possible can be shared across installs. I don&#8217;t care about that &#8211; having completely separate installs is quite affordable these days. So all that hierarchy is merely annoying and serves no useful purpose, and I would prefer to simply have all modules in<nobr> <wbr></nobr><code>./lib</code> and all XS components in<nobr> <wbr></nobr><code>./archlib</code> below the root directory of the installation, without any further nesting for different Perl versions, system architectures and packaging authorities.</p><p>But figuring out exactly how to get Perl&#8217;s <code>Configure</code> to give me I want took almost two hours of fiddling and research (and the final hint came from a rather tangential archived mailing list post).</p><p>So I thought I would jot the recipe down here:</p><blockquote><div><p> <tt>PREFIX=$HOME/perl/5.10.0&nbsp; # pick any root directory you like<br>sh Configure -des \<br>&nbsp; &nbsp; -Dprefix=$PREFIX \<br>&nbsp; &nbsp; -Dinc_version_list=none \<br>&nbsp; &nbsp; -Dprivlib=$PREFIX/lib \<br>&nbsp; &nbsp; -Darchlib=$PREFIX/archlib \<br>&nbsp; &nbsp; -Dsitearch=$PREFIX/archlib \<br>&nbsp; &nbsp; -Dsitelib=$PREFIX/lib</tt></p></div> </blockquote><p>The maddening part was to figure out that <code>inc_version_list</code> must be <code>none</code>, otherwise the <code>sitearch</code> and <code>sitelib</code> settings will be ignored and <code>Configure</code> will generate the default deeply nested layout for them.</p><p>I have to say that Perl requires rather a lot of work to beat it into submission to my preferences&#8230;</p> Aristotle 2008-10-28T10:09:50+00:00 journal Alternate realities <p> <cite> <a href="">Lukas Smith</a><nobr> <wbr></nobr></cite>:</p><blockquote><div><p>As such we have decided to go with &#8220;<code>\</code>&#8221; as the new namespace separator [<i>in PHP</i>] instead of the current &#8220;<code>::</code>&#8221;</p></div> </blockquote><p>I&#8217;m sure that makes a lot of sense when the other options you seriously considered include &#8220;<code>:)</code>&#8221; and &#8220;<code>:&gt;</code>&#8221;.</p><p> <b>[Update:</b> I wrote this before seeing <a href="/~Ovid/journal/37744">Ovid&#8217;s posting about the same matter</a>.<b>]</b> </p> Aristotle 2008-10-26T19:35:11+00:00 journal Deposition <p> <cite> <a href=";">Elyse M. Grasso</a><nobr> <wbr></nobr></cite>:</p><blockquote><div><p>We are re-architecting [our commercial application]. This would be a good time for us to transition to perl6.</p><p>As far as I can tell from the various faqs and wikis, the existing functionality in rakudo should support most of our needs for the initial release. [&#8230;] Is it practical now to deploy a Perl6/Parrot runtime with a (possibly precompiled) version of our product?</p></div> </blockquote><p>Seeing this mail made my day.</p> Aristotle 2008-10-06T19:35:02+00:00 journal The Little Prince and the Programmer <p>[<i>By <cite>Olivier Danvy</cite>, via Dave Long</i>]</p><p>On the next planet, in a building, there was a computer room.</p><p>The little prince: &#8220;Good evening.&#8221;</p><p>The programmer, glued to his computer terminal: &#8220;Sh! I&#8217;m programming!&#8221;</p><p>The little prince: &#8220;What is programming?&#8221;</p><p>The programmer, his eyes still on the screen: &#8220;Mmmmh&#8230; you&#8217;re interrupting me! What are you doing here?&#8221;</p><p>The little prince: &#8220;What is programming?&#8221;</p><p>The programmer, blinking and turning his head towards the little prince: &#8220;It is to make a machine do something. You write a program specifying what to do and the machine runs it.&#8221;</p><p>The little prince: &#8220;So what do you want the machine to do?&#8221;</p><p>The programmer: &#8220;To run my program. I am making a program to run my program, meta-circularly.&#8221;</p><p>And the programmer returned to his meta-circular program.</p><p>The little prince pondered.</p><p>The little prince: &#8220;Why do you want a program to run itself since the machine already runs it?&#8221;</p><p>The programmer, looking at him again and frowning: &#8220;Ah. You&#8217;re still there. It is because I am building a tower of computation.&#8221;</p><p>The little prince: &#8220;How tall will your tower be?&#8221;</p><p>The programmer: &#8220;Infinite.&#8221;</p><p>The little prince looked up.</p><p>The programmer: &#8220;But in practice one only needs three levels.&#8221;</p><p>The little prince looked down again.</p><p>The programmer, resuming his task with a concentrated appearance: &#8220;And I am working on it.&#8221;</p><p>&#8220;What a strange person. Every time I ask him what he wants, he tells me what he is doing,&#8221; the little prince said to himself.</p><p>&#8220;So he must always be doing what he wants,&#8221; he thought continuing his journey: &#8220;a programmer must be very happy.&#8221;</p> Aristotle 2008-09-24T12:02:12+00:00 journal Breaking Out of the Perl Echo Chamber: A Call to Action <p>I just picked up yet another useless distraction, and I am going to ask you to do the same. Bear with me here:</p><p> <a href="">StackOverflow</a> just opened for business. It&#8217;s essentially an advanced form of Perl Monks&#8217; Q&amp;A section, except open to questions for any language &#8211; in fact, any programming and program<em>mer</em> topic at all. (<a href="">Joel Spolksy explains the concept</a>; take a look at that post if this is the first time you are hearing about StackOverflow.)</p><p>The site does not have a concept of forums; questions can simply be tagged, and you can filter by tag.</p><p>Each tag also has a feed.</p><p>You can see where I am going with this.</p><p>Here&#8217;s the thing: the site has pretty solid traffic already. If it really takes off, a large community of programmers from all walks of coding will wind up there. The Perl community core can either be present there, representing Perl as the excellent language we know it to be, or we can leave Perl&#8217;s image to the typical mix of novices, too-clever-by-half coders and CPAN non-users &#8211; likely also managing to let Perl look moribund in the same stroke, thus single-handedly reinforcing every common preconception about the language.</p><p>This would be an effort along the same lines as PerlBuzz, the claim-your-journal-on-Technorati meme, doing something about the low usefulness of Google searches for [<a href="">perl blogs</a>] and so on: letting our enthusiasm ring outside the echo chamber.</p><p>So let&#8217;s do it. Please <a href="">subscribe to</a> <a href="">the questions tagged &#8220;perl&#8221; at StackOverflow</a> and take a peek whenever the fancy strikes you and you have a few minutes to spare. Thank you to everyone who responds.</p> Aristotle 2008-09-15T19:46:39+00:00 journal This is me gloating (about our proper block scoping) <p> <cite> <a href="">Erik Meijer</a><nobr> <wbr></nobr></cite>:</p><blockquote><div><p>The semantics and differences in variable capture mechanisms for closures between various programming languages is a fascinating topic. One of my favorite examples is^H^Hwas Ruby&#8217;s scoping mechanism for blocks. The problem is acknowledged and eloquently explained on page 105 in the second edition of the <a href="">pickaxe</a> book. I am particularly amused by the proposed <a href="">pragmatic</a> solution; keep your code small so that it is easy to spot any potential problem, and use some form of <a href=""> Hungarian notation</a> to put local variables and block parameters in a different lexical namespace. Fortunately, this issue <a href="">is fundamentally fixed</a> in <a href="">Ruby 1.9</a> </p></div> </blockquote><p>He then goes on to explain how C# and Javascript made this very same mistake in slightly different ways. (Python, of course, has phenomenally worse mistakes in its closure capture semantics.)</p><p>Perl&#160;5 got this right from the start.</p> Aristotle 2008-08-31T23:13:07+00:00 journal 15 minutes <p> <cite> <a href="">Tim Bunce</a><nobr> <wbr></nobr></cite>:</p><blockquote><div><p>So, now I have <a href="">my own TV channel</a>.</p></div> </blockquote><p> <a href="">Subscribed</a>!</p> Aristotle 2008-08-07T05:38:10+00:00 journal Stringifying URI objects to relative URIs by default <p>You only need a trivial subclass of <a href="">URI::WithBase</a>:</p><blockquote><div><p> <tt>{<br>&nbsp; &nbsp; package URI::WithBase::Rel;<br>&nbsp; &nbsp; our @ISA = qw( URI::WithBase );<br>&nbsp; &nbsp; use overload '""' =&gt; sub { shift-&gt;rel-&gt;as_string(@_) }, fallback =&gt; 1;<br>}</tt></p></div> </blockquote><p>If you stringify objects of this class URI::WithBase::Rel, the stringified URI will always come out relative to the base URI of the object. (If you happen to need a non-relative URI out of it, you can call the <code>as_string</code> method explicitly.)</p><p>Next, if you happen to be using Catalyst, put something like this in your <tt></tt>:</p><blockquote><div><p> <tt>sub uri_for {<br>&nbsp; &nbsp; my $c = shift;<br>&nbsp; &nbsp; return URI::WithBase::Rel-&gt;new( $c-&gt;NEXT::uri_for( @_ ), $c-&gt;req-&gt;uri );<br>}</tt></p></div> </blockquote><p>Now all the unadorned <code>uri_for</code> calls in your templates will produce nice relative URIs.</p><p>(The alternative would be to install <a href="">URI::SmartURI</a> and <a href="">Catalyst::Plugin::SmartURI</a>&#8230; but for this very simple use case of &#8220;relative URIs nearly all the time,&#8221; the amount of code in those modules alone, much less the huge pile of code they pull in via prerequisites, seems completely unjustifiable.)</p> Aristotle 2008-08-05T19:11:35+00:00 journal Help get Perl onto Google App Engine <p> <cite> <a href="">Stephen Adkins</a><nobr> <wbr></nobr></cite>:</p><blockquote><div><p>I have been promoting <a href="">the idea of a community project to get Perl supported on Google App Engine</a>.</p><p>I have made various contacts with people inside Google, and it seems that the core App Engine team is busy with plenty of other things. However, this does not stop the community from getting something started. We will likely attract perl advocates within Google as we go along, even if they are not on the App Engine team. I would like to see the effort advance until the point where it becomes a simple matter for the App Engine team to embrace Perl.</p><p>I have started a project for all people interested in following this effort or contributing toward it.</p><p> <b> <a href=""></a> </b> </p><p>Please visit the website, sign up for the mailing list, spread the word, and start contributing.</p></div> </blockquote> Aristotle 2008-06-21T12:27:46+00:00 journal Clean encapsulation for old URI redirects in Catalyst apps <p>At work, I develop and maintain a web application that I recently ported to Catalyst. One of the problems that showed up after the transition was broken links. The few most important ones were fixed at the source, but we have quite a few of them as literal strings in user content that sits in the database (possible but not easy to edit programmatically), and of course there are links on external sites (foremost being Google, of course) that we don&#8217;t control. So I needed to find a way to recognise the old URIs and redirect them to wherever they map to in the new structure &#8211; basically, a job for mod_rewrite.</p><p>However, I run the app standalone using <a href="">Catalyst::Engine::HTTP::Prefork</a> (andyg++), so mod_rewrite itself is out. There is an Apache reverse proxy in front, but I don&#8217;t want these redirects to depend on any particular deployment configuration, and I want them to be easily testable. So they need to be part of the application.</p><p>Where do I put them?</p><p>At first I did this in the regular way, creating some new controllers and writing actions in them. Catalyst&#8217;s Chained dispatch provides quite detailed control over URI structures, so this mostly worked. But it turned out to be quite clumsy: it took a lot of code for a relatively simple task, cluttered up the Catalyst dispatch table, and didn&#8217;t <em>really</em> work right. This is because the URI structure of the old app was slightly polymorphic in a way that could only awkwardly be made to fit into Catalyst, and dispatch became sensitive to the load order of controllers.</p><p>No good.</p><p>I puzzled over how to handle this, shuffling actions around in a number of ways before inspiration struck me. See, I have an <code>e404</code> action in my root controller. The <code>default</code> action forwards there unconditionally, and many other places in the application do so conditionally, eg. when the request URI maps to a database record that doesn&#8217;t exist. Now you can already see where this is going: rather than just showing the 404 page, as this action used to do, it now runs a bunch of pattern matches against <code>$c-&gt;req-&gt;path</code> to see if the URI corresponds to the old apps&#8217; structure. If it does, bits and pieces of the URI are captured and used to construct a new-style URI using <code>$c-&gt;uri_for</code>, and this is sent to the client as a permanent redirect. Basically, the 404 handler now makes a last-ditch attempt to salvage the request.</p><p>This works very well: all of the logic is isolated in a single spot where it is invisible to Catalyst&#8217;s dispatcher. It&#8217;s a minor design decision but nevertheless pleases me as it turned out so very right.</p> Aristotle 2008-06-15T05:42:24+00:00 journal Jim Weirich, author of Rake, on &#8220;DSLs&#8221; <p> <cite> <a href="">InfoQ</a><nobr> <wbr></nobr></cite>:</p><blockquote><div><p>I feel the term has probably been overused. I like to consider a Domain Specific Language to be something that talks about [a] problem domain, such as configuring workflow to solve some business problems or [&#8230;] something that is business related in the problem space. And I see a lot of us using Domain Specific Languages to solve programmer problems, which are really problems in the solution space.</p><p>[&#8230;]</p><p>What I want to use a DSL for is I want to be able to take a problem that a user comes to me and [&#8230;] create a DSL that describes it. I don&#8217;t think that the business person needs to be able to write the DSL, but I should be able to write it in the DSL, show it to a business person and they can say: &#8220;Yes, that&#8217;s what I want&#8221; and it is something that is immediately obvious to them that it solves their problem domain. It&#8217;s Domain Specific Language and if it is not addressing a domain, the term DSL seems kind of loose for me.</p><p>[&#8230;]</p><p>So I think you can have libraries with good interfaces but that doesn&#8217;t necessarily make them DSLs, certainly dropping parenthesis off a library call doesn&#8217;t make it a DSL.</p></div> </blockquote><p>What a refreshing perspective.</p><p>This is the first time I have heard someone give a definition of &#8220;DSL&#8221; that is restrictive enough to have actual meaning. Later on he also gives a similarly reasonable definition of BDD, Behaviour-Driven Development. (His first statement? There&#8217;s no difference between BDD and TDD.)</p><p>[The transcript is kinda hidden; you need to click <i>show all</i> at the bottom of the inset frame to read it.]</p> Aristotle 2008-04-23T02:13:58+00:00 journal Derivative? What does that mean? <p>Here is something I personally never really figured out:</p><p>When does derivative code cease to be derivative?</p><p>Several times I have taken code written by other people and put it through so many cycles of refactoring that, even though each of the intermediate steps is clearly derivative from the previous one, the result bears no resemblance to the original <em>whatsoever</em>. Is it still derivative? The original code served as a springboard, and for that certainly continues to deserve credit. But both the computational expression of the problem in question and the understanding that led to that particular expression is entirely my own. The credit I owe in that case seems like the credit a successful pupil might owe to a teacher: the work is still the pupil&#x2019;s. I <em>learned</em> from the code &#x2013; by scrapping it piecemeal, in a gradual process of transformation, in which the result feels like mine.</p><p>Who has copyright on that work?</p><p>When does imitation end and inspiration begin?</p> Aristotle 2008-04-09T20:22:00+00:00 journal RFC: DateTime::Weekdays <p>I just went to set up a cron script to automatically mail the date of our next Perlmonger meeting to our mailing list, and was boggled to find that <a href="">DateTime</a> includes no way to do things like finding the next Tuesday past a given date, nor is there anything for that on CPAN. Instead you are apparently expected to copy-paste some slightly fiddly code from <a href="">an FAQ</a> and tweak it.</p><p>Sorry, that&#8217;s just not acceptable. So I whipped this up:</p><blockquote><div><p> <tt>#!/usr/bin/perl<br>package DateTime::Weekdays;<br>use strict;<br>use DateTime ();<br>BEGIN { our @ISA = qw( DateTime ) }<br> <br>sub set_day_of_week {<br>&nbsp; &nbsp; my $self = shift;<br>&nbsp; &nbsp; my %a = @_;<br> <br>&nbsp; &nbsp; my ( $dow, $wanted_dow ) = ( $self-&gt;day_of_week(), $a{ dow } );<br> <br>&nbsp; &nbsp; if( $a{past} ) { $_ = -$_ for $dow, $wanted_dow }<br> <br>&nbsp; &nbsp; my $delta = ( $wanted_dow - $dow + 7 ) % 7;<br> <br>&nbsp; &nbsp; return $self if $delta == 0 and not $a{always_differ};<br> <br>&nbsp; &nbsp; $delta += 7 if $a{always_differ};<br>&nbsp; &nbsp; $delta = -$delta if $a{past};<br> <br>&nbsp; &nbsp; return $self-&gt;add( days =&gt; $delta );<br>};<br> <br>sub next&nbsp; &nbsp;{ $_[0]-&gt;set_day_of_week( dow =&gt; $_[1] ) }<br>sub prev&nbsp; &nbsp;{ $_[0]-&gt;set_day_of_week( dow =&gt; $_[1], past =&gt; 1 ) }<br>sub coming { $_[0]-&gt;set_day_of_week( dow =&gt; $_[1], always_differ =&gt; 1 ) }<br>sub last&nbsp; &nbsp;{ $_[0]-&gt;set_day_of_week( dow =&gt; $_[1], always_differ =&gt; 1, past =&gt; 1 ) }<br> <br>1;<br> <br>__END__<br> <br>=head1 NAME<br> <br>DateTime::Weekdays - find nearby DateTimes by weekday<br> <br>=head1 SYNOPSIS<br> <br> use DateTime::Weekdays;<br> my $dt = DateTime::Weekdays-&gt;now;<br> my ( $Thu, $Fri ) = ( 4, 5 );<br> <br> print 'First Friday of this month: ',<br>&nbsp; &nbsp;$dt-&gt;clone-&gt;truncate( to =&gt; 'month' )<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;-&gt;next( $Fri );<br> <br> print 'Last Friday of this month: ',<br>&nbsp; &nbsp;$dt-&gt;clone-&gt;truncate( to =&gt; 'month' )<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;-&gt;add( months =&gt; 1 )<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;-&gt;subtract( days =&gt; 1 )<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;-&gt;prev( $Fri );<br> <br> print 'The Friday before today: ',<br>&nbsp; &nbsp;$dt-&gt;clone-&gt;last( $Fri );<br> <br> print ' heretics meeting this month: ',<br>&nbsp; &nbsp;$dt-&gt;clone-&gt;truncate( to =&gt; 'month' )<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;-&gt;coming( $Thu );<br> <br>=head1 DESCRIPTION<br> <br>A subclass of L&lt;DateTime&gt; that includes a few methods for weekday-based<br>calculations.<br> <br>=head1 INTERFACE<br> <br>The workhorse method in this class is C&lt;set_day_of_week&gt;. You are not expected<br>to call this directly; instead, use the convenience methods provided. They all<br>expect a single parameter: a weekday number.<br> <br>=head2 next<br> <br>The nearest future date with the given weekday. May be the current date<br>itself.<br> <br>=head2 prev<br> <br>The nearest past date with the given weekday. May be the current date itself.<br> <br>=head2 coming<br> <br>The next future date with the given weekday. This is always at least one day<br>in the future.<br> <br>=head2 last<br> <br>The most recent past date with the given weekday. This is always at least one<br>day in the past.<br> <br>=head2 set_day_of_week<br> <br>This method implements the calculation. It accepts the following named<br>parameters:<br> <br>=over 4<br> <br>=item C&lt;dow&gt;<br> <br>The desired weekday number in DateTime convention (ie. a value in the range of<br>1..7 meaning Monday..Sunday). This parameter is requirement.<br> <br>=item C&lt;past&gt;<br> <br>A boolean specifying whether to look for a date with the desired weekday is in<br>the past, relative to the starting date.<br> <br>=item C&lt;always_differ&gt;<br> <br>A boolean specifying whether the date returned must always be in the future<br>(or past).<br> <br>F.ex., if the date in question is a Friday and you ask for a Friday in the<br>future, then normally the date itself will be returned with no changes. If<br>you set this parameter, however, then the Friday in the following week will<br>be returned.<br> <br>=back<br> <br>=head1 AUTHOR<br> <br>Aristotle Pagaltzis L&lt;;<br> <br>=head1 COPYRIGHT AND LICENCE<br> <br>Copyright (c) 2008, Aristotle Pagaltzis. All rights reserved.<br> <br>This module is free software; you can redistribute it and/or modify it under<br>the same terms as Perl itself. See L&lt;perlartistic&gt;.<br> <br>=head1 DISCLAIMER OF WARRANTY<br> <br>BECAUSE THIS SOFTWARE IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE<br>SOFTWARE, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE<br>STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE<br>SOFTWARE "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED,<br>INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND<br>FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND<br>PERFORMANCE OF THE SOFTWARE IS WITH YOU. SHOULD THE SOFTWARE PROVE DEFECTIVE,<br>YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR, OR CORRECTION.<br> <br>IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY<br>COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE<br>SOFTWARE AS PERMITTED BY THE ABOVE LICENCE, BE LIABLE TO YOU FOR DAMAGES,<br>INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING<br>OUT OF THE USE OR INABILITY TO USE THE SOFTWARE (INCLUDING BUT NOT LIMITED TO<br>LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR<br>THIRD PARTIES OR A FAILURE OF THE SOFTWARE TO OPERATE WITH ANY OTHER<br>SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE<br>POSSIBILITY OF SUCH DAMAGES.</tt></p></div> </blockquote><p>Look good? Anyone willing to write a test suite for me?<nobr> <wbr></nobr><tt>:-P</tt> </p> Aristotle 2008-04-01T12:02:12+00:00 journal Polymorphism != Inheritance (or, Paging chromatic) <p> <cite> <a href="">Reginald Braithwaite</a><nobr> <wbr></nobr></cite>:</p><blockquote><div><p>In other words, whenever you declare that Child IS-A Person, the compiler writes &#8220;Child HAS-A Person&#8221; and &#8220;Child BEHAVES-LIKE-A Person&#8221; and &#8220;Child DELEGATES-TO Person&#8221; for you.</p></div> </blockquote> Aristotle 2008-03-12T16:13:43+00:00 journal The 7th circle <p> <cite> <a href="">Kaare Rasmussen</a><nobr> <wbr></nobr></cite>:</p><blockquote><div><p>My last visit to SOAP in Perl land was some 5-6 years ago when the World was young and Perl land was unstructured as Hell. (Actually I don&#8217;t know if Hell is unstructured or not, perhaps it&#8217;s written in Java. That would make sense, I guess).</p></div> </blockquote><p>Hell is both: an enterprise system written in Java that uses a 3,000-class hierarchy to abstract away the generation of obfuscated Perl code which actually does the job. Savour the taste.</p> Aristotle 2008-02-26T03:52:04+00:00 journal