Rabu, 30 Desember 2009

CPAN CPAN on the disk, ...

Interestingly, there are some ID's up there that I've never ever heard of, ever. Either I'm the hobbit, or this list is not representative at all, or both. Maybe we should rank on total number of prereq'ed modules...
$ cd /minicpan && ( for dir in */*/*; do 
echo -e `ls $dir/*.tar.gz 2>/dev/null | perl -ne's/-[^A-Za-z_].+//;
$m{$_}++; END{print 0+keys %m}'`\\t${dir#*/*/}; done ) | sort -rn | head -10
213 ADAMK
212 ZOFFIX
206 RJBS
169 MIYAGAWA
124 SMUELLER
122 NUFFIN
117 BINGOS
111 GUGOD
109 MARCEL
100 TOKUHIROM

Keywords: CPAN authors with the most modules, most distributions

Senin, 28 Desember 2009

Given/when expression

Given/when in Perl 5.10 is great (and will be even greater in Perl 6, for example we can remove many of the now-required parentheses). But it is very statement-oriented. Sometimes I miss CASE expressions a la SQL:
SELECT store_name, CASE store_name
WHEN 'Los Angeles' THEN sales*2
WHEN 'San Diego' THEN sales*1.5
ELSE sales
END AS new_sales
FROM store;

So, how do you do given/when expressions in Perl? I can think of a couple alternatives, but none is very appealing. Do you a have better one?
my @store = (
{store_name=>'Los Angeles', sales=>100_000},
{store_name=>'San Diego', sales=>100_000},
{store_name=>'Anaheim', sales=>100_000}
);

1. do {} + if/elsif (con: needs do{}, needs explicit assignment to $_, no implicit smart matching)
for my $s (@store) {
printf "%s %d %d\n", $s->{store_name}, $s->{sales}, do {
local $_ = $s->{store_name};
if (/Angel/) { $s->{sales}*2 }
elsif ($_ eq 'San Diego') { $s->{sales}*1.5 }
else { $s->{sales} }
};
}

2. do {} + given/when (con: needs do{}, needs $tmp assignments)
for my $s (@store) {
printf "%s %d %d\n", $s->{store_name}, $s->{sales}, do {
my $tmp;
given($s->{store_name}) {
when (/Angel/) { $tmp = $s->{sales}*2 }
when ('San Diego') { $tmp = $s->{sales}*1.5 }
default { $tmp = $s->{sales} }
}
$tmp
};
}

3. sub {} + given/when (con: needs sub{}, slower perhaps?, needs explicit return's)
for my $s (@store) {
printf "%s %d %d\n", $s->{store_name}, $s->{sales}, sub {
given ($s->{store_name}) {
when (/Angel/) { return $s->{sales}*2 }
when ('San Diego') { return $s->{sales}*1.5 }
default { return $s->{sales} }
}
}->();
}

Minggu, 20 Desember 2009

Kalender adven Perl 6, RSS di HP

Blog apa yang paling menarik dibaca tahun 2009 ini? Tak diragukan lagi, Perl 6 Advent Calendar, http://perl6advent.wordpress.com/ . Selama sebulan menjelang Natal, Anda akan disuguhi artikel-artikel menarik bergaya bahasa santai yang masing-masing mengulas satu fitur baru/keren dari Perl 6. Saat ini sudah ada 20-an artikel yang diterbitkan. Masih ada beberapa hari lagi tersisa, jangan lewatkan!

Betewe, sejak taun ini ikut-ikutan beli hape kuerti yang bisa internetan, walau gak ikut-ikutan fesbukan, saya menemukan kegiatan yang mengasyikkan: membaca RSS di hape dengan Google Reader. Terasa sekali, perusahaan yang paling gets it, paling memperhatikan usability mobile browsing tak lain tak bukan adalah Google. Dan juga Facebook mungkin (tapi saya jarang pakai). Karena itu gak sabar rasanya menanti ponsel dan netbuk besutan Google tahun depan.

Rabu, 16 Desember 2009

Storable, Regexp, bugs, bugs, bugs

A few hours spent yesterday trying to find out why some of my tests keep failing under certain conditions. Turns out I did a Storable::dclone() on an object, and that object contains a regular
expression. And Storable Don't Do No Regex. Worse is, Storable doesn't complain but will just freeze/thaw the regexes into garbage.
$ perl -MStorable=dclone -MData::Dumper -e'
print "Storable version = $Storable::VERSION\n";
$re = qr/abc/;
print Dumper $re;
print Dumper dclone $re;'
Storable version = 2.21
$VAR1 = qr/(?-xism:abc)/;
$VAR1 = bless( do{\(my $o = undef)}, 'Regexp' );

This means:
$ perl -MStorable=freeze -E'say "yikes" if freeze(qr/a/) eq freeze(qr/b/)'
yikes

Since regexes are so common in Perl, maybe some warnings in Storable documentation should be in order? I'm sure many more bums are in line waiting to be bitten by this. (Bug report filed).

There is Regexp::Copy which contains Regexp::Storable, which is supposed to add regexp (de)serialization to Storable, but turns out that it still has bugs. Even this very simple case will yield a wrong answer:
$ perl -MData::Dumper -MStorable=dclone -MRegexp::Copy -E'say "Regexp::Copy version = $Regexp::Copy::VERSION"; say Dumper dclone([qr/a/, qr/b/])'
Regexp::Copy version = 0.06
$VAR1 = [
qr/(?-xism:b)/,
qr/(?-xism:b)/
];

(Btw, if you "use Regexp::Copy" before/without "use Storable", it will also result in an error. So that's couple of bug reports filed in).

Sadly it's unclear whether these will be fixed soon. The bug queues for Storable and Regexp::Copy contains unresolved entries several years old.

I first tried to switch to Data::Compare (as actually I was just comparing data structure freeze()'s for comparison, as well as some cloning). But turns out that Data::Compare doesn't deal with recursive/circular structure yet (bug filed).

Finally I resorted to using the good ol' Data::Dumper for serializing/comparison part, and Clone for the cloning part.

Is it just me (I hope it's just me) or do other people find serializing/deserializing modules in CPAN tend to be more buggy than, say, Ruby ones? I've never *once* encountered a problem with Ruby's yaml module, yet aside from Storable and Regexp case above, I have also been bitten several times by bugs in YAML.pm, YAML::Syck, and YAML::XS. The last one is this.

And with a couple of bugs in my own code unrelated to all the above, that makes the most number of bugs found/reported between yesterday and today. Not bad after all, but still I'm worried.

Arti sebuah nama

“What’s in a name?” Apalah arti sebuah nama, begitu tulis Shakespeare. “That which we call a rose. By any other name would smell as sweet.” Atau diterjemahkan, terasi dibilang mawar pun tetap bau.

Tentu saja, dalam kenyataan, sebuah nama biasanya mengandung banyak arti, karena kita memberi nama tidak secara acak melainkan disertai asosiasi, maksud, ekspresi, atau harapan tertentu. Saya ingat saat sebuah serial drama Jepang 1980-an beken di Indonesia, dari anak kenalan sampai anjing tetangga diberi nama Oshin. Jiwa setiap zaman dan budaya terjejak dalam nama-nama yang diberikan pada era/kultur tersebut. Masa revolusi dan perjuangan China dulu banyak bayi lelaki diberi nama Jian Guo (bangun negara) atau Guo Qing (hari kemerdekaan). Di Internet, muncul situs-situs bernama aneh seperti digg, reddit, twitter, semuanya karena ingin nama yang pendek di tengah kelangkaan domain .com.

Baru-baru ini saya menulis 2 buah modul kecil dalam bahasa pemrograman Perl, yang satu untuk menebak gender nama orang berdasarkan nama depan (menurut statistik dan sejumlah aturan heuristik), dan yang satu lagi untuk mengurai sebuah nama menjadi komponen-komponennya. Pada waktu Anda membaca tulisan ini, kemungkinan kedua modul tersebut sudah bertengger di situs repositori Perl CPAN.

Berbeda dengan beberapa modul serupa yang sudah ditulis untuk bahasa lain seperti Inggris yang hanya berkutat soal penebakan gender, modul pengurai nama Indonesia ini saya lengkapi dengan rutin untuk mengekstrak segala macam aspek yang memang terindikasi dalam nama. Termasuk agama (dari keberadaan titel seperti Haji/Hj, nama depan seperti Muhammad/Muh, atau singkatan nama baptis seperti FX), suku/etnik (dari pola penamaan tertentu misalnya di Bali dengan nama-nama seperti I Gusti Agung atau Ni Made, di Jawa dengan Raden, atau dari nama depan/marga yang amat khas seperti Liem untuk etnik China, Siregar untuk Batak, dll), hingga profesi/tingkat pendidikan (dari titel akademik). Sudah sangat “SARA” bukan? Tapi apa yang sebetulnya dimaksud dengan isu SARA?

Modul penebak gender biasanya digunakan untuk memberi kata sapaan yang cocok (bisa Bapak atau Ibu) saat menulis surat/email, karena ada studi yang mengatakan bahwa penyebutan kata sapaan yang salah dapat mengurangi efektivitas/tingkat respon/dsb (selain tentunya menyinggung perasaan!). Namun modul pengurai nama saya ini, termasuk alat bantu lain seperti perangkat lunak pendeteksi ras dalam foto wajah, dapat membantu proses diskriminasi lebih lanjut. Bayangkan proses penyaringan mahasiswa/karyawan/pejabat yang kini dapat lebih praktis dalam membuang calon tak diinginkan dari ras, suku, agama/keyakinan tertentu.

Saya sempat ragu sesaat untuk tidak jadi merilis modul ini, namun berdasarkan pertimbangan-pertimbangan di bawah, akhirnya saya berkeputusan untuk tetap merilisnya.

Pertama, perangkat lunak semacam ini tidak membuat jadi mungkin diskriminasi yang sebelumnya tidak dimungkinkan. Maksudnya adalah, semua informasi untuk diskriminasi seperti agama, suku, gender, dsb tersebut sebetulnya sudah terkandung di dalam nama itu sendiri. Perangkat lunak hanya merupakan enkoding informasi ini dalam bentuk instruksi komputer. Entah karena kebanggaan, kebiasaan, atau untuk meneruskan garis keturunan, orang tetap mencantumkan berbagai elemen indikator ke dalam nama mereka, walaupun konsekuensinya mempermudah dirinya didiskriminasi berdasarkan nama.

Kedua, argumen “pedang bermata dua”. Sama seperti senjata pisau atau senapan yang bisa digunakan untuk membunuh maupun menyelamatkan, memulai atau menghentikan perang, demikian juga perangkat lunak dapat dipakai oleh polisi untuk melakukan racial profiling ataupun bagi para organisasi untuk melaksanakan affirmative action. Keberadaan perangkat lunak itu sendiri tidak mengubah kecenderungan ke arah salah satu.

Ketiga, dalam kaitannya dengan SARA, UU ITE di Bab tentang perbuatan yang dilarang menyebutkan bahwa hanya informasi yang ditujukan untuk menimbulkan kebencian atau permusuhan antarindividu/antargolonganlah yang termasuk dilarang disebarkan. Perangkat lunak pengurai (parser) sama sekali tidak dibibiti informasi kebencian/permusuhan.

Bagaimana menurut pandangan para pembaca? Saya menanti masukan dari Anda semua. Apakah merilis perangkat lunak untuk menguraikan (parsing) nama orang dengan tujuan mengetahui gender, agama, suku, golongan, termasuk ke dalam tindakan terlarang? (PCMedia Edisi Jan 2010)

Senin, 07 Desember 2009

Variabel state di Perl 5.10

Bahasa Perl termasuk memberi banyak pilihan skop variabel bagi programer. Pertama, ada variabel global (tepatnya variabel package, karena sebetulnya tidak ada variabel global di Perl; ck ck ck di awal artikel sudah berbohong? :-)

Kedua, yang mungkin paling sering kita pakai, variabel leksikal alias variabel privat yang dideklarasikan dengan my() yang hanya bisa dilihat/diakses oleh blok atau file atau eval tempat si variabel dideklarasi.

Ketiga, variabel lokal untuk dynamic scoping, menggunakan kata kunci local(), biasanya kita pakai untuk menyimpan/mem-backup sebuah variabel, lalu mengubahnya di dalam sebuah blok, dan nanti otomatis saat kita keluar dari blok tersebut nilai si variabel akan terpulihkan kembali. Kita bisa melokalkan variabel package, filehandle, glob, dsb. Bahkan kita bisa melakukan hal seperti ini:

$config = { foo=>'ujan', bar=>'kemarau' };
...
{
local $config->{foo} = 'blah';
sini();
sana();
}
print $config->{foo}; # 'ujan' lagi


Pada contoh di atas, kita melokalkan satu pair dari hash saja. Saat masuk ke sini() dan sana(), nilai lokal $config->{foo} akan terus dipertahankan. Inilah yang dimaksud dynamic scoping, jadi tidak berbasis pada source code melainkan pada alur running program. Setelah blok selesai, barulah nilai lama $config->{foo} pulih. Asyik kan?

Keempat, ada lagi yang namanya our(), mulai diperkenalkan sejak Perl 5.6. Ini pada dasarnya adalah pengganti untuk "use vars qw($foo)". our() membuat alias leksikal untuk sebuah variabel global, eh, variabel package. Tentu saja variabel tersebut nanti bisa diakses dari package lain.

Kelima, yang menjadi topik posting blog ini, yaitu variabel state. Variabel ini diperkenalkan sejak Perl 5.9.sekian dan resmi menjadi bagian fitur baru dari 5.10. Untuk menggunakannya, di awal skrip kita harus melakukan:

use feature 'state';

atau:

use feature ':5.10'; # jangan lupa kutipnya ya...

Kegunaan variabel state adalah untuk membuat variabel leksikal yang persisten. Sebelumnya hal ini memang bisa dilakukan menggunakan closure, tapi terus terang saya malas menghafalnya. Setelah ada state(), barulah jadi lebih termotivasi untuk menggunakan leksikal persisten.

Akhir-akhir ini saya sering membuat metode yang mengembalikan nilai konstan/statik, contohnya:

sub config_vars {
[qw/
recurse_hash
recurse_array
parse_prefix
...
/]
}


Kenapa tidak pakai variabel biasa saja (mis menggunakan our())? Tujuannya sih agar bisa memanfaatkan inheritance.

Tapi, tahukah Anda, bahwa setiap kali dipanggil, si metode tersebut akan membuat arrayref baru? Buktinya:

$ perl -le'sub f { [1,2,3,4,5,6,7,8,9,10] } print f for 1..5'
ARRAY(0x9d1c40)
ARRAY(0xa083c8)
ARRAY(0xa083b0)
ARRAY(0xa08398)
ARRAY(0xa08380)


Pemborosan bukan? Nah, solusinya kita bisa menggunakan variabel state:

sub config_vars {
state $a = [qw/
recurse_hash
recurse_array
parse_prefix
...
/];
}


Variabel $a ini akan hidup terus walaupun sudah keluar dari skop (tentu saja, pada ujungnya nanti akan di-garbage collect kalau memang tidak ada yang memakai lagi).

Kini:

$ perl -lE'sub f { state $f = [1,2,3,4,5,6,7,8,9,10] } print f for 1..5'
ARRAY(0x12ffc40)
ARRAY(0x12ffc40)
ARRAY(0x12ffc40)
ARRAY(0x12ffc40)
ARRAY(0x12ffc40)


Oya, -E sama seperti -e tapi menghidupkan semua fitur Perl terbaru (dalam kasus ini, ekivalen dengan "use feature ':5.10'").

Selamat bermain dengan variabel di Perl!

Rabu, 02 Desember 2009

Requiring 5.10

So I decided to add "perl = 5.010000" in some of my dist.ini's. This is just because of one particular habit I recently acquired: writing "$a //= 1" and "$b = $a // 2". (Well actually I've rejoiced since defined-or is announced for 5.10, but for some reason have only begun to really use it in the past weeks.)

So much nicer than writing "$a = 1 unless defined($a)" and "$b = defined($a) ? $a : $2". It's the having to repeat myself aspect which I find disgusting, especially if $a is an expression. Which is one of the reason I always hate coding in PHP because in PHP you can't even say "$b = $a || 2".

I'm not switching, state-ing, say-ing, doing recursive patterns, or any of the other cool stuffs in 5.10. So am I so selfish for forcing 5.10 down the throats of other people just for such a minor convenience? The word externality comes to mind (having completed reading Superfreakonomics 2 days ago).

But it's a positive externality, really. ;-)

Selasa, 01 Desember 2009

minicpan

Last weekend I got a new toy: Asus EEE PC S101. This is actually my second netbook (and fourth laptop overall). I sort of dumped my first netbook only after a month of use because I now totally hate hard drives on netbooks: they're hot, they're loud, they're a power drain.

The EEE has a SSD drive, but at only 32GB, putting the whole CPAN (currently at 6.9GB) on it is a bit taxing. With the help of minicpan, I got it down to only 1.2GB. So thanks again Ricardo!

Then I immediately wondered how big BackPAN is, guessing it might be between 30-100GB (with 14 years of CPAN's history and all). But then:

$ perl -MParse::BACKPAN::Packages -e'$p = Parse::BACKPAN::Packages->new(); printf "BACKPAN is %.1fGB\n", $p->size/1024/1024/1024'
BACKPAN is 12.4GB


Much smaller than I had thought. It's just about twice the current size of CPAN. Which probably means that a lot of CPAN authors still leave much of their stuffs around and not delete them.