|
このページは大阪弁化フィルタによって翻訳生成されたんですわ。 |
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.
I posted this to perl5porters without response, and
also filed a bug report without a response. I figure it's
either a bug no one has time to think about or I'm just an idiot (and
I figure it might be a coin flip between those two). I'm trying to
figure out when() using smart-matching combined with
logical operators for the next edition of Learning Perl.
For a little background: perlsyn notes that there are exceptions
to smart matching in when():
Right after that bit in perlsyn, it talks about &&.
If EXPR is ... && ... or ... and ... , the test is applied recursively to both arguments. If both arguments pass the test, then the argument is treated as boolean.
That last sentence is weird. If both arguments pass the test, it's treated as boolean. What does that imply if one or both don't pass the test? And, is "test" just "one of the previously listed exceptions".
So, the example I was playing with wanted to check if a value was in an array and also the key of a hash:
given( $foo ) {
when( @n && %n ) { ... }
}
Since neither of the arguments are one of the exceptions, I expected both arguments to be treated as a smart match:
given( $foo ) {
when( $_ ~~ @n && $_ ~~ %n ) { ... }
}
Now, nothing in the docs say that should be the case, so before I do too much work in going through the other odd situations I found, I figure I'll check if it's a doc problem first or if I'm being a lunkhead.
Here's what I think the docs are trying to say, and once we get this right (which might mean correcting my thinking and documenting it better) I can think about the rest of it:
If EXPR is ... && ... or ... and ..., and both of the arguments are one of the listed exceptions, Perl treats both arguments as booleans and performs no smart matching. If only one of the arguments is one of the exceptions, Perl treats that argument as a boolean and performs a smart match with the other argument. If neither argument is one of the exceptions, Perl performs a separate smart match with each argument.
However, I don't know how to square the statement with a test in
t/op/switch.t. I think the test comment might be wrong. Should
there be a smart match that fails in the first when(), which is
why it moves onto the second? If so, the comment should say
something like "((1 == 1) && \"bar\") used smart match and fails
like it should".
# t/op/switch.t
{
my $ok = 1;
given("foo") {
when((1 == 1) && "bar") {
$ok = 0;
}
when((1 == 1) && $_ eq "foo") {
$ok = 2;
}
}
is($ok, 2, "((1 == 1) && \"bar\") not smartmatched");
}
I'll happily add some more tests once I know what the answers should be. :)
Here's a program I was playing with, and the output I got that led me to all of this.
use 5.010;
my @n = qw(0 Barney Wilma);
my %n = map { $_, 1 } @n;
$\ = "\n\t";
for( '', qw(0 1 Barney) )
{
my $n = $_;
say "\nProcessing [$n]...";
when( %n )
{ say "1. In \%n"; continue } # $_ ~~ %n
when( @n )
{ say "2. In \@n"; continue } # $_ ~~ @n
when( @n && %n )
{ say "3. @n && %n" } # $_ ~~ @n && $_ ~~ %n ???
}
__DATA__
Processing []...
Processing [0]...
1. In %n
2. In @n
Processing [1]...
Processing [Barney]...
1. In %n
2. In @n
Here's my machine and perl info:
macbookpro_brian[2845]$ perl5.10.0 -V
Summary of my perl5 (revision 5 version 10 subversion 0) configuration:
Platform:
osname=darwin, osvers=8.10.1, archname=darwin-2level
uname='darwin alexandria2-10.nyc.access.net 8.10.1 darwin kernel version 8.10.1: wed may 23 16:33:00 pdt 2007; root:xnu-792.22.5~1release_i386 i386 i386 '
config_args=''
hint=recommended, useposix=true, d_sigaction=define
useithreads=undef, usemultiplicity=undef
useperlio=define, d_sfio=undef, uselargefiles=define, usesocks=undef
use64bitint=undef, use64bitall=undef, uselongdouble=undef
usemymalloc=n, bincompat5005=undef
Compiler:
cc='cc', ccflags ='-fno-common -DPERL_DARWIN -no-cpp-precomp -fno-strict-aliasing -pipe -I/usr/local/include -I/opt/local/include',
optimize='-O3',
cppflags='-no-cpp-precomp -fno-common -DPERL_DARWIN -no-cpp-precomp -fno-strict-aliasing -pipe -I/usr/local/include -I/opt/local/include'
ccversion='', gccversion='4.0.1 (Apple Computer, Inc. build 5363)', gccosandvers=''
intsize=4, longsize=4, ptrsize=4, doublesize=8, byteorder=1234
d_longlong=define, longlongsize=8, d_longdbl=define, longdblsize=16
ivtype='long', ivsize=4, nvtype='double', nvsize=8, Off_t='off_t', lseeksize=8
alignbytes=8, prototype=define
Linker and Libraries:
ld='env MACOSX_DEPLOYMENT_TARGET=10.3 cc', ldflags =' -L/usr/local/lib -L/opt/local/lib'
libpth=/usr/local/lib /opt/local/lib /usr/lib
libs=-ldbm -ldl -lm -lc
perllibs=-ldl -lm -lc
libc=/usr/lib/libc.dylib, so=dylib, useshrplib=false, libperl=libperl.a
gnulibc_version=''
Dynamic Linking:
dlsrc=dl_dlopen.xs, dlext=bundle, d_dlsymun=undef, ccdlflags=' '
cccdlflags=' ', lddlflags=' -bundle -undefined dynamic_lookup -L/usr/local/lib -L/opt/local/lib'
Characteristics of this binary (from libperl):
Compile-time options: PERL_DONT_CREATE_GVSV PERL_MALLOC_WRAP
USE_LARGE_FILES USE_PERLIO
Locally applied patches:
RC2
Built under darwin
Compiled at Dec 2 2007 12:18:58
@INC:
/usr/local/perls/perl-5.10.0-rc2/lib/5.10.0/darwin-2level
/usr/local/perls/perl-5.10.0-rc2/lib/5.10.0
/usr/local/perls/perl-5.10.0-rc2/lib/site_perl/5.10.0/darwin-2level
/usr/local/perls/perl-5.10.0-rc2/lib/site_perl/5.10.0
.
Webster's came up with nothing. Nothing but "corniculate", anyway, which didn't appear to be related. At that point we had exhausted our meager resources. That's what things were like in those days.
The episode stuck with me, though, and a few years later when I became the possessor of the First Edition of the Oxford English Dictionary, I tried there. No luck. Some time afterwards, I upgraded to the Second Edition. Still no luck.
|
Order The Lyre of Orpheus with kickback no kickback |
"Oh, she's a foul-mouthed, cornaptious slut, but underneath she is all untouched wonderment.""Aha," I said. "So this is what they were reading that time."
More years went by, the oceans rose and receded, the continents shifted a bit, and the Internet crawled out of the sea. I returned to the problem of "cornaptious". I tried a Google book search. It found one use only, from The Lyre of Orpheus. The trail was still cold.
But wait! It also had a suggestion: "Did you mean: carnaptious", asked Google.
Ho! Fifty-six hits for "carnaptious", all from books about Scots and Irish. And the OED does list "carnaptious". "Sc. and Irish dial." it says. It means bad-tempered or quarrelsome. Had Davies spelled it correctly, we would have found it right away, because "carnaptious" does appear in Webster's Second.
So that's that then. A twenty-year-old spelling error cleared up by Google Books.
Jan Dubois posted about Microsoft's 3rd Annual Scripting Games, where you can win prizes for writing Perl programs to solve programming problems like "find a word that you can make out of a phone number," and "count the number of characters in a given text file."
I'd enter if there were interesting prizes, but all the prizes for Windows-only software packages. :-(
Something you may not know about chromatic is that he's a Perl 6 core developer. It's something to consider when reading his (now several) opinion pieces on Ruby, Rails and/or its users.
— Daniel Berger, comment on "What's a Ruby DSL and what isn't?"
I'm also the guy who argued within O'Reilly for publishing more Ruby books in 2003 and 2004, the editor who took a chance and published Curt Hibbs's seminal tutorial on Rails in January 2005, and someone who watched technology features, trends, and people very carefully as part of my job -- a profession I've been in full-time since 2002.
I don't particularly care if people use Ruby. It's a decent language. I use it myself sometimes.
I don't particularly care if people use Rails. It's a decent framework. I've used it myself a few times. I don't use it anymore, but those reasons are technical, largely immaterial, and highly uninteresting even to me.
I care when people publish untruths. I care when people lie about technical matters. I care when people are so busy giving themselves very vigorous self-handshakes in public that the poison an entire conversation about important issues such as design considerations, software maintainability, and language features.
Maybe this is a hobby horse I shouldn't ride. Maybe I'm not the guy to say "Uh, there's no substance to the code your emperor just wrote." If there's really something to internal DSLs -- if there's really some special magic in Ruby that makes it more possible to write them than in any other language -- I'd like to know. So far, I haven't seen it, and thus I conclude that writing so-called internal DSLs is to Ruby what golf is to Perl -- a stupid fad with a tiny core of interesting truth that gets buried in the silly cooler-than-thou-and-especially-thy-non-Ruby-language posing that passes for conversation in certain parts of the Ruby community these days.
I emphasized the phrase certain parts because some of the smartest, most capable, most humble, and most pleasant people I know consider themselves part of the Ruby community. This includes Chad Fowler, David Black, Dave Thomas, Piers Cawley, and Charles Nutter. That's an extremely non-exhaustive list, by the way.
Yet when Chad Fowler said from the stage that Rubyists have a reputation for being "arrogant bastards", RailsConf 2007 laughed and applauded. I know that none of the people I mentioned in the previous paragraph were amused.
If the fact that I'm a Parrot committer and that I'm on the Perl 6 design team means that no one should listen to my opinions, I think you may have missed the point. You shouldn't listen to my opinions because I'm occasionally bitter, often subtle, usually over the top, and always trying foremost to entertain myself.
That doesn't, in itself, make my opinions wrong. Only facts could do that.
In the index for "Catalyst" book, you'll find no entry for model, controller, action, dispatch, or ActionClass. These are some of the most fundamental concepts in Catalyst.
Many technical books suffer most because of those elements that are outside of the author's direct control: weird layouts, weird typographical conventions, and lousy indices. Knowing that, I'd never base my criticism of a technical book on the failings of its index. The failings of "Catalyst's" index, however, are telling about the failings of the book as a whole.
If it were only the index that lacked this information, it would be a minor problem. Unfortunately, there really is no comprehensive explanation of any of these topics. Though there is an entry for "View," it is explained in as little detail as the rest of these concepts.
Pages 6 and 7 provide about one paragraph each for the concepts of MVC, Model, View, and Controller. From there on, the book focuses on implementing specific tasks without explaining much of the concepts that are used to do so. Actions aren't so much explained as implied to be subroutines with attributes. While nearly all of dispatch in a simple Catalyst application is determined by a few named and attribute-laden subroutines, these are not even presented in a bullet list, let alone explained in any detail.
Instead of starting with an explanation of how the fundamentals works, Rockway works through specific, practical examples of application implementation. These provide a demonstration of quite a few of the things that one can do with Catalyst, and occasionally some explanation of why it works. The explanations are not systematic, however, and concepts are presented out of order rather than in a logical progression. By the end of chapter two, the first chapter with any specific examples, the reader is installing and using Template Toolkit, SQLite, and DBIx::Class. Views are defined as bits of code that produce output based on the content of the "stash" on page 20, but the stash itself is not given any explanation until five pages later. This kind of confusing presentation prevails through most of the book, and is sometimes rendered more confusing by the inclusion of huge code samples, some of which dominate three consecutive pages.
Some concepts are well explained. The section on chained dispatch was clear and concise, though basic actions and dispatch remained unexplained. The section on the REST ActionClass was clear, and I found it to be the most interesting section of the book.
The book desperately needs reorganization, both by inclusion of an explanation of the fundamental concepts used in Catalyst design and by reordering the presentation of material to present concepts in a logical order with no long gaps between items that belong together. Finally, a few segments could probably be dropped entirely, or at least moved further back into the book. Explanations of FormBuilder and BindLex, for example, might have been interesting and useful for the creation of serious web applications, but they are instead presented without much explanation in chapter three, where they just serve to further confuse the subject at hand.
The Catalyst::Manual documents seem to provide much of the same information, and at no cost. They also include some of the concepts that are missing from the book. Although I think a second edition of "Catalyst," or a second book on the topic, could be a very useful book to introduce new users to Catalyst, I don't think this book has much value beyond that of the existing free documentation.
Read more of this story at use Perl.
If you're like me, your attention span on a three-day weekend is too short to wade through all of Implementing an Internal DSL in Ruby. I've decided to be helpful to all of the confused people in the world who just don't see the special magic that goes in to writing an internal DSL. Here's the condensed version which helpfully fits on an index card. (That way, you can palm it at the next Rails meetup if you need a cheat sheet.)
If this doesn't sound very spectacular, you're absolutely right. I thought it important to take a break before the final, magical step which turns bog-standard rote programming into unicorn-and-glitter wonderful tooth-whitening DSL creation.
Congratulations! Unlike the poor slobs who merely declare classes and methods, give them meaningful names, and occasionally pass around first-class functions, YU WRITED AN DSL!! GRATE JOBZES!!!
You may be hearing murmurs about this, so by my reckoning it falls on me to document it. R.E.M. have taken a stand by releasing the videos from their new album under Artistic License 2.0. The Artistic License 2.0 is a product of the Perl Foundation, and is the license under which Perl is released.
Normally creators have turned to Creative Commons for movies & music, so it's interesting to see R.E.M. buck the trend and use Artistic License. This can only help the visibility of open source. I'm glad that of all the licenses out there, they chose the one I love. Maybe there'll be a point in the future when Artistic 2.0 is used everywhere, that it will be automatic for the people who create music to think of TPF's license.
(Please send complaints about R.E.M. references to the dead letter office /dev/null.)
Read more of this story at use Perl.
I know it's on the CPAN, I just can't find it yet. I want a module which does something like this:
use Number::Properties ':all';
my @odds = grep { odd $_ } @num_list;
my @evens = grep { even $_ } @num_list;
my @primes = grep { prime $_ } @num_list;
my @negatives = grep { negative ...
You get the idea.
Is it out there?
Here are a couple of interviews for your reading enjoyment. Patrick Michaud talks about Perl 6 in advance of FOSDEM '08, a conference in Brussels. The interview is a bit old, pre-dating the naming of Rakudo.
In the second interview, Richard Dice talks about his 14 years with Perl, and current news about the Perl Foundation.
You know, I'm guessing there's other good content in $foo perl magazin, but since Richard's interview is the only thing in English, it's going to have to stay at the guessing stage.
"I practice annoyance driven development. I set my threshold of annoyance low such that everytime I feel frustrated by a technical limitation, I notice consciously. My intent is not to find technology endlessly frustrating (though that happens sometimes), but so that I can identify the next most important thing to fix."
-- chromatic, in "What You Test Changes How You Test"
I felt pretty good after cataloging what I want from a mobile phone, so I think it'll be a good idea to do the same thing for productivity/todo software.
The Features I Want
only one required field
If I have to write more than a phrase indicating what to do, you lose. Seriously, that's it, game over. The best kind of quick data entry is a sequence of lines. Speaking of which...
good quick entry
For example, OmniFocus has a hotkey to a window that lets you enter just the fields you want, then hit Cmd-S, and all the tasks go into your inbox. My quick entry is just description and due date. When I'm in the middle of something else, I can hit the hotkey, type in some thoughts, hit Cmd-S and be back to what I was doing before.
dependencies
I want to be able to say, easily, "don't do X until Y is done." Then, I don't want to have to see X until Y is done. This is pretty basic GTD stuff: don't worry about stuff you can't do. Of course now and then I'll want to review things, but... well, if you can't see something at all just because it's blocked, you lose.
a good printed version
Well, really this is something like "offline operation with a cheap and rugged handheld electronic device." Until Hell freezes over, though, paper will do. I'd love a good index card system, even if I only used it sometimes for doing spatial organization or for sneaking into my pocket when going to a boring event. OmniFocus prints checklists, which are just barely tolerable. I saw a tip on printing index cards from it, but it turned out to be printing checklists onto index cards. WTF? I can just fold a normal letter-sized page!
tagging
I waffled on whether or not to call this need "tagging." OmniFocus has context, which mostly fills this need. Each task is part of zero or one parent task (and the topmost tasks are projects) and has zero or one context. You can view your tasks as a tree based on hierarchy or as a flat list of tasks in contexts. (I'm simplifying, but not all that much for the point I'm trying to make.) The point is this: because you only get one context, you have to pick your contexts very carefully. If I make a "offline" context for things I can do when I have no IP connection, now I have to decide whether "write letter to Aunt Jo" goes into "offline" or "writing" or "communicate." If it could go in all three, then I could see not only things to do offline or things to write, but the intersection: things I can write while I'm offline.
Maybe one-context-per-task makes the UI simpler, but given the amount of explanation that OmniFocus requires from the start, I think the power that real tagging gives would be worth it.
a good review feature
Sometimes, when I felt like I was getting into a rut, I'd flip through my index cards, put them in a new order, toss some out, realize that some were done, and so on. I don't quite know what a good review feature would look like in software, but here's a guess: it show me all my tasks not reviewed since some date. I'd click a big "mark reviewed" button next to each task to update the date and hide the task. I'd update other tasks as needed. Eventually, the list would be empty and I'd go eat a taco.
OmniFocus has a task review system, but so far I have found it nearly impenetrable. I finally (I think) understand it, but it just isn't simple enough. I think it's a failing of OmniFocus's "you only get two views" system. Sure, you can save modifications on those two views (those saved variants are called perspectives) but they're still just those two views. I want a reviewing view. I bet I'd like a tag cloud, too. Gosh, a drag-and-drop spatial view would be sublime! Especially if I could have more than one spatial view... ooh!
Well, I'm digressing.
a good todo view
In other words, "show me what I should do right now." Sure, one option is just "show everything not blocked in the tags I selected," and that's fine. I want it to make it really easy to select or deselect tags, to sort by priority (maybe) or by due date or by "blocking the most other stuff." OmniFocus's perspectives can make it easy to save some of these views, but making them really easy to generate on the fly would be even better.
work offline
I know I already said I wanted paper, but sometimes I have my computer and plenty of power, but no network. With OmniFocus, this is pretty simple: it just works. It's a datafile on my computer. With something mostly web-based like Hiveminder, it's a much bigger problem. Access via my phone is only a really mediocre form of offline access.
Fortunately, this is much less of a concern for me these days, because I have an EVDO modem that works well along my entire commute route.
access to my list from elsewhere
Yes, this is yet another entry that's kind of like "work offline." What if I'm online, but not at my computer? With OmniFocus, I'm hosed. Maybe I could use VNC to get back to my desktop, but that's unlikely and stupid. With Hiveminder, I can just log into the web service.
reminders
I want to get email or Growl notices when a task it getting close to due. Like, I really want to be reminded to "file your taxes" by around April 1. I'd want to be able to say when I get reminders per-task, including both the lead time and frequency or aggressiveness of each task's reminders. Of course, I only want to see those fields on a task when I ask for them. Remember: easy entry is a must.
slippage tracking
When I close a task, I would love to see something like:
You entered this task on January 1 with a due date of February 1. You pushed back the deadline six times, finally leaving it at November 1. You marked this task complete today, December 14.
Then, obviously, being able to view a timeline of aggregated task slippage based on tags would be fantastic. Am I waxing pipedreamy here? Maybe, but this is something I've always wanted. It was pretty easy to tell from my index cards: the most worn cards were the ones I put off the longest.
high visibility
In OmniFocus, I can see how many tasks are overdue in the dock. That's nice. I'd like it even better if I could tweak just what I saw there -- like, exclude tasks tagged -- I mean in the context -- "goofing off." I don't know what would be better, but I'm sure something would. I bet a really good Dashboard widget could be done, although I really hate that Dashboard widgets are mostly stuck, well, on the dashboard.
The Features I Don't Care About
collaboration
Maybe this could be a great bonus, but getting my own ducks in a row is hard enough. Sharing ducks terrifies me and makes me think that either I'd obsess over someone else's problems or look like a disorganized jerk to someone important.
repeating tasks
Again, maybe someday I'll have use for this, but for now most things would be noise.
calendar sync
Another bonus feature: since my current piece of crap phone has no useful calendar sync, I barely use iCal anymore. I really want to, but until I have a new phone, this is back burnered. Anyway, if I get what I really want (a real offline mode) then this is more about getting a good integrated view than about just having any view at all.
Conclusions?
OmniFocus is great, but it turns out that there's a lot of stuff that I think I want that it doesn't do. Maybe that means that I don't really want those things, but I doubt it. I think I'll give Hiveminder another try, after all.
I just hope I'm not wasting my time! I have sixteen overdue tasks.
(But monads in the form of a parser combinator like Parsec can implement external DSLs, even an external DSL like Perl 6...) Monads and DSLs are both applications of metaprogramming because each of the two effectively transforms minimal code into a complete form for execution. In fact, as monad tutorials instruct, Haskell's "do-notation" is syntax sugar, i.e. a DSL, for using monads...
— Art Vandalay, of metaprogramming, monads, DSLs
Most of the rest of the post is useful, but the paragraph from which I took this quote goes (warning: intentional pun) way off the rails.
To start, it's difficult to describe an internal language feature as a domain-specific language, unless that feature is useful when independent of the host language. Perl regular expressions qualify as a DSL, but scoping declarators do not.
To nitpick, what precisely is the domain to which Perl 6 is specific? "General purpose programming languages"? The general is the enemy of the specific, or at least the enemy general.
I'm not sure it's useful to talk about the way that monads transform code into "a complete form for execution" in this context. That line of reasoning eventually equates syntax with DSLishness, and as you can see, that "eventually" has a very short period. While I agree that metaprogramming offers the opportunity for code transformation (though not always to the degree found in homoiconic languages, as Art mentions), effectively all code transformations are part of either an algorithm (in the metaprogramming sense) or the compiler (in the syntactic sugar sense). In the context of how compilers and languages work, that transformation is highly appropriate. In the context of language syntax alone, it's tautological -- the compiler supports the language's syntax. That's what compilers do.
This is my quibble with the quoted paragraph, and especially the parenthetical note. A parser -- Parsec -- can parse text, including programming languages.
That's what parsers do, just like subroutines let you name operations in your program after actions in the problem domain, just like namespaces and classes let you categorize, group, model, and name entities in your program after entities and concepts in your problem domain, and language features allow you to describe your intent algorithmically in a precise and organized fashion.
You can exploit corner cases in the language's parser, but as long as you depend on the host language's compiler to transform your code into the proper form for its execution model, you're writing APIs.That's what (post-structured programming) programming languages are for. There is no shame in that. Of course, there should be shame in gratuitous parser abuse, especially when the result is barely-literate pidgin that barely nods to the language of the problem domain. Then again, I care about maintainability and correctness and screechingly obvious code, not "Hey test fixture User is some description do end!"
(For the ultimate in fun, ask yourself "From what domain did the Haskell designers take the word monad and, if monads are truly a DSL in Haskell (rather than an intrinsic language idiom), what does that imply about their problem domain?")
Since hdp and schwern both recommended that I specify elements in Bermuda Island files as a list of key/value pairs, I've gone ahead and tried it out. It much easier to read now. I have this:
---
package: Some::Class
island: dummy
attributes:
name:
type: string
version?:
type: integer
some_val?:
type: integer
if: $instance->some_val > 1
elements:
- personal_email:
type: string
method: email('personal')
- work_email:
type: string
method: email('work')
Turning into this:
{
"elements" : [
{
"value" : "victoria@example.com",
"name" : "personal_email"
},
{
"value" : "victoria@some_company.com",
"name" : "work_email"
}
],
"name" : "dummy",
"attributes" : {
"version" : 3,
"name" : "Victoria",
"some_val" : 4
}
}
Obviously that's just the built-in JSON serialization.
Unless I get radically more free time soon, I likely won't be able to do much more for a couple of weeks.
I also note that if I make attributes an ordered list of k/v pairs, then the attribute and element syntax becomes identical, with the exception that attributes are not allowed to have '*' or '+' quantifiers. This would be very, very handy. However, attributes in XML are, by definition, not ordered, so imposing an order sounds wrong. On the other hand, imposing an order, even as a convenient side-effect, shouldn't hurt consumers of the serialized data structures and might help poorly coded consumers. Also, simply making it easier to write Island files by normalizing the syntax is very appealing.
Update: Yuck. I need to clean up my JSON and YAML even more. That should read:
{
"elements" : [
"personal_email" : "victoria@example.com",
"work_email" : "victoria@some_company.com"
],
"name" : "dummy",
"attributes" : {
"version" : 3,
"name" : "Victoria",
"some_val" : 4
}
}
Read more of this story at use Perl.
Stonehenge is offering open-enrollment Perl classes in downtown Chicago in April. These are the full Stonehenge courses in a slightly compressed-time format that we offer to the public only a couple times a year:
We can also take purchase orders and alternate payment methods for any of there. We can also offer team discounts for people from the same group paying at the same time. Write to me (brian@stonehenge.com) with any questions about the classes.
Josh McAdams has announced the open call for participation for YAPC::NA in Chicago, June 16-18th, 2008. Talks can be 20, 45, 70 or 95 minutes long, plus the call for lightning talks will open later. The YAPC::NA website has details.
Across the Atlantic in Braga, Portugal, Jos? Alves de Castro has announced the call for participation for the Portuguese Perl Workshop 2008, June 6 & 7th.
Jim Brandt announced over in the Perl Foundation's blog that rt.cpan.org got some much-needed speedups. Even though TPF was considering forking over some dough for hardware upgrades, Jesse Vincent and his team managed to speed things up with software only.
Once in a while I hear people talking about using thousands or tens of thousands of tables in one MySQL database and often how it “doesn’t work”. There are two things to say to them:
I’ll elaborate a little on both …
In most cases when extraordinarily many tables are brought up the “correct answer” is: Fix your schema to not duplicate the same table layout for each customer/user/site/…!
Once in a while though there are good reasons to have way too many lots of tables. Even “takes too long to fix the application now” can be a good enough answer sometimes.
My use case was to use the many tables as an extra “index” for a situation that a regular index couldn’t cover. Tens of thousands of tables, here we come.
… which is easy!
In your startup script (/etc/init.d/mysql with the MySQL RPMs), add “ulimit -n 30000” somewhere near the top to allow mysqld to have 30000 open files (and sockets).
In your mysql configuration, add something like the following - adjust the number as appropriate.
set-variable = table_cache=12000
This will let MySQL keep up to 12000 tables open. The default limit is much too low and the system will spend all its time closing and opening the files. The other few thousand handles are free for database connections or whatever. You surely can tune the numbers more, but I haven’t needed to be more specific yet. MySQL uses two filehandles per open table (for MyISAM, it depends on the table type…)
One curious thing you’ll run into is that MySQL can take forever (read: hours!) flushing thousands of tables that have been changed if you do it with a simple “flush tables” or when you are shutting down mysql. That’s of course not “hmn, how curious” but rather insanely frustrating if you were going for a quick restart. This occasionally seems to happen with InnoDB tables too, but in particular with our large MyISAM system (we use fulltext indexes, hence MyISAM) this is a big issue.
With MyISAM tables, you also have a lot of cleaning up to do if the system crashes one way or another with thousands of tables open.
There’s an easy-ish solution to both these problems, though! Just flush the tables individually before the shutdown command and on a regular basis to mitigate the issue if it crashes. Remember the system or MySQL can crash for all sorts of reasons. Recently we had a motherboard with a BIOS or hardware bug that made it unstable after adding more than 16GB memory. On another box, one of the memory sticks went bad so suddenly it came up with 4GB memory less than previously. With MySQL carefully tuned to use all the memory (it was an InnoDB-only installation) it’d try using all 24GB memory and get killed by the kernel when it got above 20! Yeah, that one took some head scratching before I figured it out.
Below is a small program that’ll go through the database twice flushing all tables in all databases and then end with a regular “flush all”. We go through the database twice just in case the first flush took so long that a lot of tables got opened again. Two works for me, depending on your needs you might make it an option. Or a fancy version would check how many open tables are at the end of the run and go through it again if too many are open (and abort with an error if they open faster than they can get closed!). The final “flush tables” worked for me in getting everything closed just before the script exits (and the shutdown or whatever starts).
#!/usr/bin/perl
use strict;
use warnings;
use Getopt::Long qw(GetOptions);
use DBI;
my %args = (
verbose => 0,
user => $ENV{USER},
hostname => '127.0.0.1',
port => 3306
);
GetOptions(\%args,
'verbose!',
'user=s',
'password=s',
'hostname=s',
'port=i',
'database=s@'
);
my $dbh = DBI->connect("dbi:mysql:hostname=$args{hostname};port=$args{port}",
$args{user}, $args{password})
or die DBI->errstr;
my @dbs = $dbh->func('_ListDBs');
for my $db (@dbs) {
next if uc($db) eq 'INFORMATION_SCHEMA';
$dbh->do("use $db");
my $tables = $dbh->selectcol_arrayref("show tables");
for (1..2) {
for my $table (@$tables) {
print "flushing $db.$table\n";
$dbh->do("flush table $table");
}
}
}
print "flushing all\n";
$dbh->do("flush tables");
A million (alternately, five) years ago, when I was working at IQE, I got sick of trying to use any of our existing project tracking software to track my todo list. Instead, I started using index cards. I put the big chunks of todo into Project or Press Your Luck (the project-managing add-on to our internal helpdesk software), but all the task-level stuff became index cards instead of helpdesk tickets.
This worked really, really well. I could stick blanks in my pocket to catch random ideas. I could (as pictured) organize my cards on a pinboard in a lot of ways. Sometimes I made dependency trees, which was great when working with someone or making clear progress in a project. Sometimes I put them into a grid using the Eisenhower method. Sometimes I just put them in a rough todo order and made a stack on my desk to plow through them. It was pretty fantastic.
I felt pretty silly, carrying around these neon index cards everywhere, and my co-workers seemed to find them sort of bemusing. I think my boss thought I was nuts for not using, say, Exchange. I felt pretty silly, but not silly enough to stop using them. After all, they were so darn useful!
I took my index carding ways with me to Pobox, and at least briefly infected a coworker with the idea. Through the years, from IQE to Pobox, I kept trying to find a useful software alternative. I used Kwiki, OmniOutliner, a few bug tracking systems, Vim (with various plugins, including Viki and TVO), and probably a bunch of other things I can't remember. When Getting Things Done became the hot new thing, I immediately tried the software that was made for GTD. Well, not immediately. First, I spent some time rolling my eyes at people who seemed amazed at what had been obvious, to me, for years: simple categorized items are really useful.
Everything I tried was cumbersome and ugly, and left me unable to access my data when not at my computer. When the Omni Group said they were making a GTD app, I was pretty excited. They made a fantastic diagramming app, the first outliner I'd ever liked, a web browser that I actually paid to use, and project planning software that I wished I had a reason to use. Could their todo app miss the mark?
I got into the early semi-public beta, and once I figured out how to use it, I loved it. I loved it so much that pretty soon I threw away my stack of index cards. I got a lot of stuff done, and was pretty well able to ignore OmniFocus while doing it. I have already written about OmniFocus and how great it is.
One of the things I tried, and abandoned, while I was still on index cards was Hiveminder, the GTD-ish todo manager from Best Practical, the people who brought us Request Tracker. I don't use RT much, apart from rt.cpan.org, which I dislike, so I wasn't looking at Hiveminder as the next thing I'd love -- I just knew it was coming from people whom I knew and whose opinions I tended to respect. When I first used it, in (I think!) pretty early private beta, it was slow and didn't do absolute everything I wanted. For one thing, it couldn't, uh, print index cards. (I volunteered to rope a friend into making that work and never came through. I'm sorry, Jesse!)
Looking back, I think even then Hiveminder might've been able to do everything I now do with OmniFocus. It was just slow and I was really, really attached to my index cards.
I've kept a corner of my eye on Hiveminder, though, and it keeps getting cooler looking. I can point a mail client at its IMAP server and deal with my todo items as if they were messages. Since it's IMAP and things have proper UIDs, I can use OfflineIMAP to sync my todo lists to maildirs on my laptop, then move them around and sync them back... or so I believe. I can talk to Hiveminder over instant message and (via Twitter) SMS. I can email new tasks to Hiveminder, and I can empower other people to send new tasks to me.
Beyond all that, it is multiuser and allows groups and group project management, so I can create todo items for anyone working on a project to do.
What's keeping me from moving to Hiveminder? Well, there are a few things.
First of all, I paid $40 for OmniFocus! That's some serious money! If I use Hiveminder, I'm sure to want a pro account, which will run me $30, and I don't want to get into the habit of jumping from one system to another. That's really the least of my concerns, though.
Another concern is, well, the iPhone. I don't have one... but I probably will get one when my T-Mob account runs out in July. I'm sick of T-Mobile's lack of 3G, and I hate my current handset with great intensity. I'm looking forward to evaluating Android, but I think that once the SDK is out and Omni and other awesome Mac ISVs are making great software, iPhone will still be the winner. Speaking of Omni making iPhone software... it seems pretty darn obvious that they will make a OmniFocus for the iPhone, and that it will sync with my laptop. I used to want an iPhone before iPhone existed, because I knew OmniFocus would make an outliner for iPhone, and that could replace my index cards. Will there be a native Hiveminder app for iPhone? Well, I'm doubtful, although I can probably ask the friendly folks at BP.
Sure, sure: I can talk to Hiveminder from my iPhone with IMAP, email, IM, SMS, or HTTP. That's not enough for me, though! I know it isn't enough for Jesse Vincent at BP, either. A few conferences ago, he was one of a few people to contribute to a brief group of lightning talks that began "I think, but I cannot prove..." His talk was about the fact that in Web 2.0, we're putting all our data out in the cloud... and that when the cloud isn't accessible, we're hosed. I have a context in OmniFocus precisely for "things to do when there is no network!" I don't want to deal with having no access to my todo just because I'm in a fallout shelter deep underground or (somewhat more likely) on a bus in the middle of nowhere. I want my iPhone to have a synchronized copy of my data, ready to go.
Maybe the next generation of iPhone (or Android!) software will have a good mechanism for making this happen. WebKit's client-side database feature could be darn useful for that. The alternative, after all, is that I make a printout of my todos, making sure there's empty space for new items, and that I fall back to that when I lose my network. Then if I lose my network connection after two hours of using it, my printout is two hours out of date and I have to perform a two-way sync myself... by hand!
So, I am conflicted. I need to produce a list of priorities for my todo software. Then I can use that as a rubric for picking a winner in the battle for my mindshare. I will post it when it's done.
Read more of this story at use Perl.
Read more of this story at use Perl.
The Perl 6 design team met by phone on 13 February 2008. Larry, Allison, Jerry, Will, Richard, Patrick, and chromatic attended.
c:
Larry:
Undef, such as Object or Failureundef keyword remains; it returns the most general type of FailurePatrick:
Jerry:
Allison:
c:
Allison:
Will:
Richard:
Patrick:
copy opcode?Allison:
c:
Read more of this story at use Perl.
I clearly need to do some more work with the relocatable code, but so far, here's my Bermuda Changelog for today:
- Made XML, JSON and YAML serialization classes relocatable.
- Started adding exception tests for islands and increased validation.
- Renamed Bermuda::Base to Bermuda::Serialize.
- Made Bermuda::Serialize relocatable.
- Added 'fetch_trimmed_data' which deletes empty attribute and element
items. This makes JSON and YAML serialization much cleaner.
Not too shabby for a day's work. You won't need to write your own serialization code for XML, JSON or YAML. Previously it was easy to add, but you still had to add it.
As it stands now, the entire generated code base is relocatable without any dependency on Bermuda. I still need to work out the best way of making the dependencies for the generated code base clear, though.
Read more of this story at use Perl.
Today, in work, we wound up discussing this classic DailyWTF.com article — “Remember, the enterprisocity of an application is directly proportionate to the number of constants defined”:
public class SqlWords
{
public const string SELECT = " SELECT ";
public const string TOP = " TOP ";
public const string DISTINCT = " DISTINCT ";
/* etc. */
}
public class SqlQueries
{
public const string SELECT_ACTIVE_PRODCUTS =
SqlWords.SELECT +
SqlWords.STAR +
SqlWords.FROM +
SqlTables.PRODUCTS +
SqlWords.WHERE +
SqlColumns.PRODUCTS_ISACTIVE +
SqlWords.EQUALS +
SqlMisc.NUMBERS_ONE;
/* etc. */
}
This made me recall the legendary source code for the original Bourne shell, in Version 7 Unix. As this article notes:
Steve Bourne, at Bell Labs, worked on his version of shell starting from 1974 and this shell was released in 1978 as Bourne shell. Steve previously was involved with the development of Algol-68 compiler and he transferred general approach and some syntax sugar to his new project.
“Some syntax sugar” is an understatement. Here’s an example, from cmd.c:
LOCAL REGPTR syncase(esym)
REG INT esym;
{
skipnl();
IF wdval==esym
THEN return(0);
ELSE REG REGPTR r=getstak(REGTYPE);
r->regptr=0;
LOOP wdarg->argnxt=r->regptr;
r->regptr=wdarg;
IF wdval ORF ( word()!=')' ANDF wdval!='|' )
THEN synbad();
FI
IF wdval=='|'
THEN word();
ELSE break;
FI
POOL
r->regcom=cmd(0,NLFLG|MTFLG);
IF wdval==ECSYM
THEN r->regnxt=syncase(esym);
ELSE chksym(esym);
r->regnxt=0;
FI
return(r);
FI
}
Here are the #define macros Bourne used to “Algolify” the C compiler, in mac.h:
/*
* UNIX shell
*
* S. R. Bourne
* Bell Telephone Laboratories
*
*/
#define LOCAL static
#define PROC extern
#define TYPE typedef
#define STRUCT TYPE struct
#define UNION TYPE union
#define REG register
#define IF if(
#define THEN ){
#define ELSE } else {
#define ELIF } else if (
#define FI ;}
#define BEGIN {
#define END }
#define SWITCH switch(
#define IN ){
#define ENDSW }
#define FOR for(
#define WHILE while(
#define DO ){
#define OD ;}
#define REP do{
#define PER }while(
#define DONE );
#define LOOP for(;;){
#define POOL }
#define SKIP ;
#define DIV /
#define REM %
#define NEQ ^
#define ANDF &&
#define ORF ||
#define TRUE (-1)
#define FALSE 0
#define LOBYTE 0377
#define STRIP 0177
#define QUOTE 0200
#define EOF 0
#define NL '\n'
#define SP ' '
#define LQ '`'
#define RQ '\''
#define MINUS '-'
#define COLON ':'
#define MAX(a,b) ((a)>(b)?(a):(b))
Having said all that, the Bourne shell was an awesome achievement; many of the coding constructs we still use in modern Bash scripts, 30 years later, are identical to the original design.
In exactly two months - April 14th - I'm doing a scalability tutorial at the MySQL conference.
It'll, of course, be based on the material I've been throwing around since 2001, but once again it's greatly expanded, extended and improved! It should be lots of fun.
I've yet to hear anyone tell me that they wasted their time with this particular session and I don't think people are just being polite because I'm often told that people find it tremendously useful. So go sign up already. And don't pay any attention to Brian Aker and dormando's shiny memcached tutorial going on at the same time. I promise I'll cover memcached, too. :-) Speaking of Alan Kasindorf (aka Dormando) he had a most excellent post about how to run your systems recently!
O'Reilly sent me a discount code to hand out to "friends and colleagues". I'm not sure what they mean by that. Can I post it here? If you read my drivel, surely you must be a friend. If you are interested in the session in the first place, surely you are some kind of colleague. To be safe however I won't post it here. If you want 20% off for the MySQL Conference send me an email.
This reads like a joke, and it is tempting to dismiss it as a trite bit of foolishness. But it has rather interesting and deep connections to other related matters, such as the Grelling-Nelson paradox and Gödel's incompleteness theorem. I plan to write about that someday.
But today my purpose is only to argue that there are demonstrably uninteresting real numbers. I even have an example. Liouville's number L is uninteresting. It is defined as:

The only noteworthy mathematical property possessed by L is its transcendentality. But this is certainly not enough to qualify it as interesting, since nearly all real numbers are transcendental.
Liouville's theorem shows how to construct many transcendental numbers, but the construction generates many similar numbers. For example, you can replace the 10 with a 2, or the n! with floor(en) or any other fast-growing function. It appears that any potentially interesting property possessed by Liouville's number is also possessed by uncountably many other numbers. Its uninterestingness is identical to that of other transcendental numbers constructed by Liouville's method. L was neither the first nor the simplest number so constructed, so Liouville's number is not even of historical interest.
The argument in Berry's paradox fails for the real numbers: since the real numbers are not well-ordered, the set of uninteresting real numbers need have no smallest element, and in fact (by Berry's argument) does not. Liouville's number is not the smallest number of its type, nor the largest, nor anything else of interest.
If someone were to come along and prove that Liouville's number was the most uninteresting real number, that would be rather interesting, but it has not happened, nor is it likely.
It's a nasty hack, but I got sick and tired of always doing the following:
So I wrote something like this:
use List::MoreUtils 'uniq';
use Net::Domain 'hostname';
use Getopt::Long;
sub get_cat_host_port {
my ( $app_name, $user ) = @_;
$app_name .= "_server.pl";
$user ||= [getpwuid($<)]->[0];
# The sed bit is embarrassing but needed on a Cat restart
# when the shell quotes things :/
chomp(my @processes
= uniq `ps -ef|grep "$user.*$app_name"|grep -v grep|sed -e 's/"//g' -e 's/.* script//'`
);
if ( @processes > 1 ) {
require Data::Dumper;
$Data::Dumper::Indent = 1;
warn Data::Dumper->Dump( [\@processes], ['*processes'] );
die "Found more than one $app_name server for ($user)";
}
unless (@processes) {
die "Could not find any $app_name servers for ($user)";
}
local @ARGV = split /\s+/ => $processes[0];
local $SIG{__WARN__} = sub {
my $warning = shift;
return if $warning =~ /Unknown option/; # wish this was configurable
CORE::warn($warning);
};
GetOptions(
'port=i' => \my $port,
'host=s' => \my $host,
);
$port ||= 3000;
$host ||= hostname();
return ( $host, $port );
}
It's now tucked away in a tools library so we can reuse this.
JSON::XS is by far the best JSON module on the CPAN. However, it changed its API at version 2.01. If you have to maintain code which may be run on systems with either version one or two then this is a bit of a pain. This module takes the pain away without sacrificing performance.
For the benefit of other Technorati API users…
In a comment on this entry, Padraig Brady mentioned that his blog had mysteriously disappeared from the Irish Blogs Top 100 list.
I investigated, and found something odd — it seems Technorati has made a change to their bloginfo API, now listing weblogs with their ‘rank’, but without some of the important metadata, like ‘inboundblogs’, ‘inboundlinks’, and with a ‘lastupdate’ time set to the epoch (1970-01-01 00:00:00 GMT), in the API. Here’s an example:
<!-- generator="Technorati API version 1.0" -->
<!DOCTYPE tapi PUBLIC "-//Technorati, Inc.//DTD TAPI 0.02//EN"
"http://api.technorati.com/dtd/tapi-002.xml">
<tapi version="1.0">
<document>
<result>
<url>http://www.pixelbeat.org</url>
<weblog>
<name>P?draig Brady</name>
<url>http://www.pixelbeat.org</url>
<rssurl></rssurl>
<atomurl></atomurl>
<inboundblogs></inboundblogs>
<inboundlinks></inboundlinks>
<lastupdate>1970-01-01 00:00:00 GMT</lastupdate>
<rank>74830</rank>
</weblog>
</result>
</document>
</tapi>
Compare that with this lookup result, on my own blog:
<?xml version="1.0" encoding="utf-8"?>
<!-- generator="Technorati API version 1.0" -->
<!DOCTYPE tapi PUBLIC "-//Technorati, Inc.//DTD TAPI 0.02//EN"
"http://api.technorati.com/dtd/tapi-002.xml">
<tapi version="1.0">
<document>
<result>
<url>http://taint.org</url>
<weblog>
<name>taint.org: Justin Mason’s Weblog</name>
<url>http://taint.org</url>
<rssurl>http://taint.org/feed</rssurl>
<atomurl>http://taint.org/feed/atom</atomurl>
<inboundblogs>143</inboundblogs>
<inboundlinks>227</inboundlinks>
<lastupdate>2008-02-12 11:48:10 GMT</lastupdate>
<rank>43404</rank>
</weblog>
<inboundblogs>143</inboundblogs>
<inboundlinks>227</inboundlinks>
</result>
</document>
</tapi>
This bug had caused a number of blogs to be dropped from the list, since I was using “inboundblogs and inboundlinks == 0″ as an indication that a blog was not registered with Technorati.
It’s now worked around in my code, although a side-effect is that blogs which have this set will appear with question-marks in the ‘inboundblogs’ and ‘inboundlinks’ columns, and will perform poorly in the ‘ranked by inbound link count’ table (unsurprisingly).
I’ve posted a query to the support forum — let’s see what the story is.
A Fan of Freedom: Thoughts on the Biography of RMS is either the most ironic thing I've read this month, or very clever and intentional self-parody. Gone are the days when radical countercultural godlike heroes walked the expo floors of their SF conventions to buy buttons with witty slogans and shake the hand of that guy who played the Zorn in Star Trek.
I blame the manga.
Read more of this story at use Perl.
Andi Gutmans, founder of Zend which drives PHP, takes a swipe at Perl 6 in this interview:
So we are anticipating a long rollout cycle for PHP 6, and we did not want to take the same route that the Perl project did, with project contributors still working on Perl 6 I think six years later. People make fun of Microsoft, but take a look at Perl 6. . . .
Sure, PHP 6 may have a shorter release cycle than Perl 6 has, but at the end of it all, we'll have Perl 6, and you'll still have PHP.
Just sayin'.
xoxo,
Andy
Damn. I accepted a patch for aliased to have proper $SIG{__DIE__} handling, but it turned out to have a bug. I have to accept responsibility for this one since it is my module.
use Test::More tests => 1;
use aliased 'CGI'; # doesn't matter which module you use
ok 1, 'dummy test';
Foo->bar;
__END__
nofail......
1..1
ok 1 - dummy test
Can't locate object method "bar" via package "Foo" (perhaps you forgot to load "Foo"?) at nofail.t line 504.
ok
All tests successful.
Files=1, Tests=1, 0 wallclock secs ( 0.01 usr 0.00 sys + 0.21 cusr 0.01 csys = 0.23 CPU)
Result: PASS
This fails because Test::Builder has its own $SIG{__DIE__} handler and it checks to see if it's in an eval. If not, it sets Test_Died to true. When we get to Foo->bar, Test::Builder's $SIG{__DIE__} handler is wiped out and it can't tell itself that the test died. I hope to have a new version up shortly.
In other words, we once again have a problem with writing code with a global effect. I don't know how this one could be avoided in Test::Builder, but aliased clearly had this wrong.
In this case, the intent of the $SIG{__DIE__} handling code in aliased was not to alter anything about the handlers, but to preserve their semantics just as if you hadn't used aliased. This now brings me up to four modules which have caused various bugs or performance hits here because of global effects. This is not the largest code base I've worked on, but I'm surprised that we're having a much larger problem than I've seen in others.
This year’s Irish Blog Awards shortlists were posted yesterday. I maintain the Irish Blogs Technorati Top 100 list, so good sources of Irish blog URLs are always welcome; I took the shortlisted blogs and added them all.
Interestingly, straight in at number 2 went towleroad.com (warning: not worksafe!). It has a staggering Technorati rank of 1074 — way ahead of Donncha’s 5831 or Mulley’s 8678. I was pretty curious as to how an Irish blog could hit those heights without me having heard of it, so I took a look.
Let’s just say the content isn’t quite what you’d expect to find in a blog shortlisted for ‘Best News/Current Affairs Blog’ — a little bit short on Irish news, but heavy on pictures of naked guys getting off with each other. ;)
I took a quick glance, and I couldn’t spot any Irish content. WHOIS says the the publisher is LA-based. so I’m curious as to what qualified it as an “Irish blog”…
(by the way, I tried to leave a comment on the blog entry, but it appears Akismet is marking my comments as spam on a number of Wordpress-based blogs at the moment. Yes, I am aware of the irony. No, if SpamAssassin was a blog-spam filter, it wouldn’t do that ;)
Update: it’s sorted — they’re now gone. Also, it appears I’ve been removed from the Akismet blacklist, yay.
Dutch free knowledge and culture advocacy group ScriptumLibre.org has called for a worldwide boycott of Trend Micro products. Their chairman, Wiebe van der Worp, claims Trend Micro’s aggressive use of litigation is “well beyond the borders of decency”.
Also, this Linux.com feature has a great quote from Jim Zemlin, the executive director of the Linux Foundation:
“A company that files a patent claim against code coming from a widely adopted open source project vastly underestimates the self-inflicted damage to its customer and community relationships. In today’s world, all of our customers in the software industry are enjoying the benefits of a wide variety of open source projects that provide stability and vendor-neutral solutions to the most basic of their computing needs. I talk to those customers every day. They consider these claims short-sighted and those that assert them to be fearful of their ability to compete in today’s economy.”
Well said.
Read more of this story at use Perl.
Something people often want to do is add methods to a specific instance of a class. For example, if you have a web request, you might want to introspect it and add a "deserialize" method if it's a REST request with serialized data. However, you don't want other instances of the Request class to have that method. In this article, we'll see how such a thing is possible.
Let's start with a simple (and contrived) example:
package Foo; use Moose; has 'value' => (is => 'ro', isa => 'Any'); 1;
You can use the class like this:
my $foo = Foo->new( value => 'Hello' ); say $foo->value; # Hello
It sure would be nice to say $foo->say though. Let's write a
role that implements the say method:
package Say; use Moose::Role; sub say { say shift->value } 1;
Right now, this role doesn't do much:
$foo->can('say'); # undef $foo->does('Say'); # undef $foo->say; # dies
To make $foo do Say, we need to apply the Role. If we were
applying the role to every instance of the Foo class, we would say:
package Foo; with 'Say'; ...
Then Foo->new(value => '...')->say would work.
We only want to apply Say to a specific instance,
however. Fortunately, that is possible:
Say->meta->apply($foo); $foo->say; # Hello $foo->can('say'); # CODE(0xc0ffee) $foo->does('Say'); # true my $bar = Foo->new; $bar->can('say'); # no $bar->does('say'): # false
That's all there is to it. Applying Say to every instance is also
possible at runtime:
Say->meta->apply(Foo->meta);
Let's look at a more complex example:
package Integer; use Moose; has 'value' => (is => 'ro', isa => 'Int'); sub one_less { my $self = shift; return (blessed $self)->new( value => $self->value - 1 ) } sub multiply_by { my ($self, $other) = @_; return (blessed $self)->new( value => $self->value * $other->value ) } sub is_zero { shift->value == 0 } sub say { say $_[0]->value; $_[0] }
Here we have a basic Integer class. You can do things like:
my $two = Integer->new(value => 2)->say; # 2 my $four = $two->multiply_by($two)->say; # 4 my $zero = $two->one_less->one_less->say; # 0 say "is zero" if $zero->is_zero; # is zero
Straightforward. Now let's say we want to add a factorial method.
We again begin by creating a Factorial role:
package Factorial; use Moose::Role; requires 'is_zero'; requires 'multiply_by'; requires 'one_less'; sub factorial { my $number = shift; return Integer->new( value => 1 ) if $number->is_zero; return $number->multiply_by($number->one_less->factorial); }
Notice that this time we make sure we have all the methods that
factorial requires by using the requires keyword. This is
checked when we apply the role.
Let's do that:
my $ten = Integer->new(value => 10); eval { say $ten->factorial } or say "dies"; # dies Factorial->meta->apply($ten); $ten->does('Factorial'); $ten->factorial->say; # 362880 my $ten2 = Integer->new(value => 10); $ten2->factorial->say; # dies
It works just like you'd expect. However, we are doing something
tricky. Let's take a look at the one_less method. It takes the
current value, subtracts 1, and then wraps up the result in another
Integer object.
But, you'll note that instead of Integer->new, we say (blessed $self)->new. That's because to calculate the factorial,
the result of one_less also needs to does('Factorial'). But,
role application only affects one instance, so just saying Integer->new would fail.
The tricky bit is in the implementation of runtime role application. Basically, when you apply a role at runtime, this happens:
sub apply_role_to($invocant) { { package Some::Dynamic::Package::1; use Moose; extends 'The::Original::Class'; with 'The::Role::You::Just::Applied'; } $invocant = bless $class, 'Some::Dynamic::Package::1'; }
That's not valid Perl, but the idea holds. Whatever you passed to
apply is reblessed into a dynamically generated package that isa
the original class. When we say:
(blessed $self)->new( value => ... );
instead of:
Integer->new( value => ... );
we're making sure that the Integer we return is in that
dynamically-generated class with the role applied to it. That way if
we call one_less on a class that does Factorial, the instance we
return can also do Factorial.
The key thing to take away is that it's possible, easy, and clean to
add methods (via roles) to specific instances of classes. However,
you do need to make sure your class isn't working against, like our
Integer class would be if we just returned Integer->new.
Incidentally, did you know you can modify a method's invocant? Try this sometime:
package Foo; sub go { say 'Foo::go'; $_[0] = 'Bar'; } package Bar; sub go { say 'Bar::go'; $_[0] = 'Foo'; } my $class = 'Foo'; $class->go; # Foo::go $class->go; # Bar::go $class->go; # Foo::go
This is a great way to ensure that your code is completely unmaintainable. But it's also good for implementing runtime role application.
Read more of this story at use Perl.
I've gotten some mileage out of Facebook. I caught up with a few old friends, whom I otherwise would've never heard from. That's about it. I actually appreciate that a lot, but the rest of Facebook can really just go to Hell.
First off, there's the fact that it doesn't seem to want to play well with others. I can't use my OpenID, I can't get an RSS feed of anything, and I can't really shut off most of the obnoxious "your friend just took a quiz!" alerts.
I can't convince non-technically-minded people that no, Facebook is not email. I keep getting email from Facebook telling me I have a message from someone else who has my email address. Has Facebook become "The Internet" to some people? Ugh.
It could be worse, though. I could just be getting "pokes." Oh, or "super-pokes." This is a new kind of electronic communication whereby a friend whom you haven't heard from in years sends you an email, but without any content. Instead you get a little message bar saying, "Long Lost Friend has sent you a Beer. Click here to send one back!" Dude, you know what? Why don't you send me an email and I will buy you a real beer and we can eat some tacos. This "poke" stuff is rubbish.
Today and a few weeks ago, I logged in and saw a quiz that a friend wanted me to take. Normally I ignore these, but these two were both sort of interesting and related to my friendship with this person. Having gone throught the quiz, I was then asked to "pick 20 friends to send this to." There was a "Skip" button, but clicking it brought up the error, "You must pick 20 friends." Great.
I am not a huge fan of using Google for all my data needs, but I do have hopes for the Social Graph API as a long overdue means to show people that friendship network data need not live in these walled city ghettos like Facebook, MySpace, Orkut, or, God save us, Xanga.
Dear cpan-testers recipients,
Effective Monday night, Feb 11th, 2008, after over a million test reports[1], we will no longer be supporting the delivery via email of cpan-testers test reports. With an influx of over 3000 messages a day, this was causing us to deliver almost 200,000 outbound messages, most of which nobody read.
cpan-testers test report submission is unaffected by this change.[2]
If you are currently subscribed to cpan-testers, you'll be unsubscribed, and your mail server will breathe a sign of relief.
You can find test reports at:
http://www.nntp.perl.org/group/perl.cpan.testers/
http://www.nntp.perl.org/group/perl.cpan.testers/rss/posts.xml
nntp://nntp.perl.org/perl.cpan.testers/
http://testers.cpan.org/
Why are we doing this? Things were chugging along smoothly, except every few weeks we'd have a problem where a recipient's mail server would stop accepting mail, and we'd very quickly end up with a large backlog of thousands of messages waiting to be delivered. This would slow down delivery for all of our mailing lists. Recently, this has been happening more and more often. We analyzed the
problem, and came to the conclusion that the best solution was to discontinue outbound email. This will let us focus more time on maintaining more important things.
If you have something that is dependent on receiving all of these emails, please let us know.
Thanks!
-R
Footnotes:
[1] http://www.nntp.perl.org/group/perl.cpan.testers/2008/01/msg1000000.html
[2] There are some volunteers working on a new system that will take email out of the picture altogether, creating a faster, more streamlined, and more consistent test database.
Didn’t it used to be the case that when you used the Mac OS X Finder to
burn a CD-ROM that you could then mount that CD-ROM on a Windows box? In the
last few months, I’m suddenly finding that this is no longer the case. So now
I have to use hdiutil to convert a .dmg file to the
Joliet and ISO9660 file systems:
hdiutil makehybrid -o image.iso -joliet -iso image.dmg
And then I could burn a CD readable on Windows. What the fuck? I burned three CDs that were then useless to me before I finally dug up this hint. And I had this problem with CDs burned by Tiger, too, last summer, so it’s not just Leopard. It seems to me that Mac OS X should always default to building a hybrid CD that’s then readable by Windows, Linux, and everything else. Why doesn’t it?
This past Thursday and Friday, we had an email glitch affecting cpan.org and pm.org email addresses. The issue has been fixed, and mail is flowing normally once again. If you sent a message to a cpan.org or pm.org address and it hasn't gone through, or if it bounced, you may wish to send it again.
Read more of this story at use Perl.
The Perl 6 design team met by phone on 06 February 2008. Larry, Jerry, Will, Jesse, Nicholas, and chromatic attended.
c:
Larry:
Nicholas:
Larry:
$:foo is still available and :foo looks like a named variable, I made that the named version of $^a Jesse:
Larry:
@_ or %_ in one of these subs, it automatically adds that as a slurpy on the endJerry:
snprintf() and get it working on Windowsperl6doc for Rakudo this weekWill:
Nicholas:
when with hashes and arrays in 5.10After some hacking on the vienna.pm server, http://cpants.perl.org/ now is updated every 6 hours daily. I also fixed a bug which prevented updated modules beeing tested.
I'm completly offline the next seven days (snowboarding, yay!), but I hope that CPANTS will run without me in that time. I also plan to fix more bugs and even add a few features in the two weeks between my holidays and Dutch Perl Workshop.
I needed to buy a new laptop for work a few months back, and after a little agonizing between the MacBook Pro and a Thinkpad T61p, I plumped for the latter. As I noted at the time, one of the major selling points was the quality of IBM/Lenovo’s after-sales warranty service, compared to the atrocious stories I’d heard about AppleCare in Europe. I was, however, taking a leap of faith — I had used IBM service to great effect in the US, but had never actually tried it out in Ireland.
Sadly, I had to put this to the test today, after the hard disk started producing these warnings:
/var/log/messages:Feb 7 11:21:13 wall kernel: [2075890.116000] end_request: I/O error, dev sda, sector 116189461 /var/log/messages:Feb 7 11:21:38 wall kernel: [2075914.824000] end_request: I/O error, dev sda, sector 116189460 /var/log/messages:Feb 7 11:24:18 wall kernel: [2076075.072000] end_request: I/O error, dev sda, sector 116189462 /var/log/messages:Feb 7 11:25:05 wall kernel: [2076121.932000] end_request: I/O error, dev sda, sector 116189463
It’s a brand new machine, and a Hitachi TravelStar 7K100 drive, with a good reputation for reliability — but these things do happen. :(
Interestingly, I thought this was a case of the Bathtub curve in action — but this comprehensive CMU study of hard drive reliability notes that the ‘infant mortality’ concept doesn’t seem to apply to current hard-drive technology:
Replacement rates [of hard drives in a cluster] are rising significantly over the years, even during early years in the lifecycle. Replacement rates in HPC1 nearly double from year 1 to 2, or from year 2 to 3. This ob- servation suggests that wear-out may start much earlier than expected, leading to steadily increasing replacement rates during most of a system’s useful life. This is an in- teresting observation because it does not agree with the common assumption that after the first year of operation, failure rates reach a steady state for a few years, forming the “bottom of the bathtub”.
Anyway, I digress.
I ran the BIOS hard disk self-test, got the expected failure, then rang up Lenovo’s International Warranty line for Ireland. I got through immediately to a helpful guy in India, and gave him my details and the BIOS error message; he had no tricky questions, no guff about me using Linux rather than Windows, and there were no attempts to sting me for shipping.
There’s now a replacement HD (and a set of spare recovery disks, bonus!) winging their way via 2-day shipping, expected on Tuesday; I’m to hand over the broken HD to the courier once it arrives. Fantastic stuff!
Assuming the courier doesn’t screw up, this is yet another major win for IBM/Lenovo support, and I feel vindicated. ;)
Update: the HD arrived this morning at 10am — a day early. Very impressive!
I received a letter from BT today. I thought it was marketing spam. The first big heading is "You have Free UK Weekend calls from now on". Sure looks like marketing spam. The first paragraph is:
At BT, we're always looking for ways to give you more value for money. From the beginning of February 2008 we are giving you, as an existing BT Together Option 1 customer, Free UK Weekend calls. You don't have to contact us or do anything to claim these free calls. You can just start using them this weekend.
Ah, how friendly. The second heading is "Sign up for Free UK Evening calls, as well". Oh, they want me to pay for something, a sure sign of marketing spam.
The third headline, the smallest, is "Other changes to your Calling Plans". This is probably the bit most people ignore. The first paragraph is about how they are renaming the plans (telecoms companies like to do this every few years to confuse their customers and to stop them from comparing plans). The next paragraph is the reason they are sending the letter: they've increased the price of basic line rental.
Dear BT, please don't hide price increases in marketing spam.
(Sorry, no Perl content, but BT have annoyed me enough in the past).
Iterating over a list, five elements at a time:
for ( my $num = 20; $num <= 100; $num += 5 ) {
print $num, $/;
}
That's ugly!
sub increment {
my ( $start, $end, $inc ) = @_;
$start -= $inc;
return sub {
return if $start >= $end;
$start += $inc;
};
}
my $inc = increment( 20, 100, 5 );
while ( my $num = $inc->() ) {
print $num, $/;
}
A vaguely interesting abstraction, but I hate that you have to declare $inc before hand (you can work around this with grep, but I like the lazy list).
use List::Maker;
for my $num (<20..100 x 5>) {
print $num, $/;
}
Elegant and clear. I love what Damian has done, but ...
package List::Maker;
# several lines later
no warnings 'redefine';
*CORE::GLOBAL::glob = sub
{
Oops. No file globbing any more. Plus, internally this uses grep to calculate all the elements at once. I'd prefer a lazy list.
Damian has stated that the file globbing issue is fixed in the next release, but we're still waiting on that release.
The moral of this story: no matter how tempting, don't reach for *CORE::GLOBAL::.... It's caused us too much pain at work with other modules. Encouraging the problem doesn't help.
FamilySearch, the world's largest repository of genealogical resources, announced its 2008 FamilySearch Developers Conference.
The newly released FamilySearch Family Tree API and soon-?to-?be-?released Record Search API will be main topics of discussion. Other popular topics will be how to read, write, combine, separate, and synchronize with new FamilySearch online resources, developer keys, tree cleaning, GEDCOM, and PAF.
Genealogy these days is all about lots of data mangling, web services, big databases: it's an ideal area for Perl to work in. Paul Johnson's Gedcom module has long been used to handle GEDCOM files, the standard interchange format for genealogical data.
I chatted with Pat Eyler about the FamilySearch project, of which he's a member:
"World's largest repository of genealogical resources" only tells part of the story. Imagine this:Billions of names, petabytes of image data, and the tools to access and use all of it.
- all of the microfilmed genealogical records that the LDS church owns made freely available
- a framework for indexing these (or any other genealogical images someone wants to donate)
- a unified database of individuals and relationships with varying levels of access control, available to anyone through a rich web client or through web services
There's quite a bit of data here for the examining. Even if you're not interested in the conference, check out what's going on with the data. It seems to be an ideal dovetail with the open source community.
Read more of this story at use Perl.
Read more of this story at use Perl.
Ruby's a fine language in many ways. It's not Ruby's fault that you can occasionally write a line of code that reads almost like a very short, very very simple English sentence. It's not even Ruby's fault that some (probably well-meaning) people look at that example and try to push that further than it should go (likely just beyond "See Spot run.").
When _why invents something, it's new. It's often bizarre, but always whimsical and charming.
You can tell _why didn't invent rspec (an introduction to RSpec - Part I).
Besides the more-or-less-pointless renaming of "test" to "behavior" (No kidding, you're testing the behavior of your code? What have my tests been testing for the past decade, sparkly pink unicorn content?), I thought the point of inventing a dee-ess-ell was to be able to choose the most appropriate linguistic representation (syntax most appropriate for the semantics) with concepts lifted directly from the language of the problem domain.
(From that sentence alone, you can tell I don't get invited to the best post-Rails Ruby parties.)
As happens occasionally, the RSpec community circa 2005 or 2006 discovered what the developers of Perl knew in approximately 1988 (for those wondering if I made a snarky typo by accident or on purpose, 1988 was twenty years ago). To wit, it's nice to associate names with test assertions.
The proper cultured Rubyist response is probably "our syntax is better because it doesn't look like we're calling functions."
That's true, but it only applies to the output, which is the least important part of the process -- and, frankly, if the whole point of waving your Big Sword Of Super Duper DEE-ESS-ELL +2 Against Enterprisey is to improve the reading and writing of code, then you could produce this output from any language such as, for example, COBOL, Malbolge, or Java.
I have nothing against fluent interfaces, and I'm perfectly happy to write my test code as actual code. That's fine. Yet how ironic that the actual code (you know, written by people who believe that dee-ess-ells are basically English) reads like the code equivalent of pidgin English. (Props to big daddy P.C.) NOUN IT "description of task" DO some Ruby code END.
It do, do it? Can it has cheezburger, too?
This here is the problem with so-called internal dee-ess-ells: your lack of abstraction leaks big. A real domain-specific language could fix this with a trivial grammar change.
Even with this silliness, the output of RSpec is substantially better than that of Ruby's standard testing module, which is at best functional in that it tells you how long the test suite took to run, oh, and if a test failed. I continually miss Perl's Test::Class and Test::Harness, at least for those tests which I absolutely must write in the host language. (See also Jonathan Rockway's comparison of RSpec to Perl's Test::More.)
The question you're probably asking yourself right now isn't "What then does BDD want to be when it grows up?" (Another good question is "Exactly how serious are you about leaky internal pidgins?" To that I answer "Reader IT should be able to pick out truth from over the top satire DO reader.ponder END TRANSMISSION FULLSTOP." Bonus points for figuring out where this entry's title comes from and why. Zing!)
I'm a generous guy at this hour, so the answer to the non-obvious question is "FIT, ironically enough, is a framework for describing the desired behavior of software in the actual language of the problem domain."
The QA Hackathon sounds very interesting. The flight to Oslo isn't expensive (at the moment), $job and @family don't object. But: I hate flying. It's uncomfortable, and it's bad for the environment (and I already totaly blew my CO2 balance last year with my trip to Mongolia...)
I have to decide soonish, before the ticket prices rise... Hmm...
Except of course it wasn't Acta Quandalia (which would never commit such a silly error) and it didn't concern k-quandles; it was some unspecified journal, and it concerned some property of some sort of topological space, and that was the end of the investigation of those topological spaces.
This would not qualify as a major screwup under my definition in the original article, since the theorems are true, but it certainly would have been rather embarrassing. Journals are not supposed to publish papers about the properties of the empty set.
Hmm, there's a thought. How about a Journal of the Properties of the Empty Set? The editors would never be at a loss for material. And the cover almost designs itself.
Handsome, isn't it? I See A Great Need!Ahem. Anyway, if the folklore in question is true, I suppose the mathematicians involved might have felt proud rather than ashamed, since they could now boast of having completely solved the problem of partially uniform k-quandles. But on the other hand, suppose you had been granted a doctorate on the strength of your thesis on the properties of objects from some class which was subsequently shown to be empty. Wouldn't you feel at least a bit like a fraud?
Is this story true? Are there any examples? Please help me, gentle readers.
I just caught a link to a programming language benchmark for bioinformatics. Unsurprisingly, the Perl is grotty and the C and C++ and Java implementations beat it handily.
Are there any PDLlers who'd like to bring some sanity to the results? (My eyes are going on strike from the C-style nested loops. Make sure you catch the use of bitwise and as control flow operator. That has to work only by accident.)
Read more of this story at use Perl.
Yesterday, someone pointed me in the direction of the RSpec testing framework for Ruby:
http://blog.davidchelimsky.net/articles/2007/05/14/an-introduction-to-rspec-part-i
I suggest reading the post, if only to enjoy literary constructions along the lines of:
Behaviour Driven Development is an Agile development process that comprises aspects of Acceptance Test Driven Planning, Domain Driven Design and Test Driven Development. RSpec is a BDD tool aimed at TDD in the context of BDD.
That sounds like something I should pay you a lot of money for! Sign me up!
Anyway, here's their example for testing a "User" object's role functionality:
describe User do it "should be in any roles assigned to it" do user = User.new user.assign_role("assigned role") user.should be_in_role("assigned role") end it "should NOT be in any roles not assigned to it" do user = User.new user.should_not be_in_role("unassigned role") end end
The idea is you get a user object, add it to a role, and see if the change sticks (and that it doesn't affect any other roles). A reasonable test, I guess. There are a few bugs here (how do we know that "assigned role" isn't accidentally persisted across user objects), but that's really not important. You can write bad tests in any language.
For the sake of comparison, let's translate this directly into Perl:
use Test::More tests => 2; # i assume RSpec needs something like this my $user = User->new; $user->assign_role('assigned role'); ok $user->is_in_role('assigned role'), 'user in assigned role'; $user = User->new; ok !$user->is_in_role('unassigned role'), 'user not in unassigned role';
The Perl code is cleaner for a variety of reasons.
First, notice that the RSpec version isn't actually Ruby code. It's
not any real language; it's a new one made up only for testing. Since
this is eventually run by Ruby, there is some filter in effect that
changes what the programmer types, user.should be_in_role to
user.in_role? which is actually executed.
This is cute when it works, but the disadvantages are numerous. Source filtering is prone to errors because modern dynamic languages have complicated syntax -- without a macro system it's nearly impossible to change program text hygienically. So it's best to just not do it. Even if you can transform the program text correctly, the result is simply not Ruby. A Ruby programmer will have to puzzle over how this works, because it's completely inconsistent with the rest of the language.
Why waste mental energy puzzling over syntax? Isn't that why you left Perl for Ruby in the first place?
In Perl, we just use the ok function. It's a regular function that
tells the test harness "pass" if the argument is true, or "fail"
otherwise. It's the simplest thing that could possibly work, and
therefore is not likely to cause any unexpected side-effects. There's
no source filtering, and it works like everything else in Perl.
Notice that the RSpec invents a new piece of syntax, the should_be
and should_not_be "method/operator thingies", whereas Test::More
uses existing constructs as intended: ok $foo and ok !$foo (read
as "foo?" and "not foo?"). While both are equally clear to a speaker
of English, the Perl version is clear to any Perl programmer. No
English is required; no knowledge of the testing framework is
required. It's how the ! operator always works in Perl.
Making up new syntax is, by definition, inconsistent with the rest of the language, inconsistent with how the programmer thinks, and yet another piece of added complexity the programmer has to manage. That's a lot of inconsistency and confusion just to make your tests look like English-but-not-really.
Another downside of using RSpec is that the test is actually harder to
use as "documentation". If you look at an arbitrary Perl module that
is tested with Test::More, you can get a good idea of how to use
the module by looking at the tests. You can even cut-n-paste the test
into a script and start playing immediately. With tests written in
RSpec, you can't do that. The testing language isn't Ruby, so you're
stuck manually "unsugaring" the test if you actually want to run it.
Finally, you'll notice that the RSpec example is just plain longer. The Perl version expresses its intent in five lines of code (actual Perl, not some newly-created pseudolanguage); the RSpec version takes eleven. There's twice as much code there, for an overall decrease in readability.
In conclusion, RSpec is taking testing in the wrong direction. It
hurts maintainability, reduces documentation value, adds complexity,
and potentially introduces subtle bugs -- all to make your tests
harder to write. I will stick with good-old Test::More-style tests
for now.
Read more of this story at use Perl.
Read more of this story at use Perl.
A few other CPAN authors have already uploaded bundles that install all the modules they want to have around before they start working. This has always seemed like a good idea, but with all the re-installing of perl I've been doing lately, it's finally hit home just how much time I can save.
I've used Task-style bundling instead of a Bundle, but it's the same basic idea. Now you can see what I'm most attached to, if you care, with Task::BeLike::RJBS. Strangely enough, there's only one email module in there, and very little related to the web. I guess familiarity breeds contempt after all.
|
When you need perl, think perl.org
|
|