понедельник, декабря 24, 2007

Perl для Windows

Когда я ещё программил под чистым Windows(совмещая это с программированием под чистым *nix) я использовал для своих нужд ActivePerl. Однако достаточно быстро обнаружил что там есть не всё что мне нужно для работы (сейчас уже не вспомню конкретный пример, но не было достаточно простых модулей) и перешёл на виртуалку под windows (пробовал сначала cygwin, потом перешёл на vmware). В принципе прежде чем использовать ActivePerl надо было бы выяснить что компания канадская, а согласно источнику заслуживающему доверия (коим несомненно является мульт South Park) канадцы все какие-то не такие и делают всё не так - и тогда бы я не потратил время зря. :)

Сейчас узнал что оказывается есть вроде полноценные альтернативы... Например StrawberryPerl . В комплекте Strawberry Perl идёт всё необходимое для того чтобы инсталлить модули прямо с CPAN (в том числе и XS модули).

Вобщем альтернатива вполне достойная. И парни из ActiveState видимо это понимают, так как поспешили выпустить 5.10 раньше чем выйдет StrawberryPerl 5.10 - эта новость пробегала по perl'овым блогам/группам.
P.S. Возможно сейчас ситуация с perl от ActiveState улучшилась в разы - я знаю что многие гуру, ну или почти гуру успешно им пользуются. Да и времени утекло много с тех пор как я его смотрел...

суббота, декабря 15, 2007

ООП в perl. Class::Std vs Object::InsideOut

Речь пойдёт о perl без perl6 синтаксиса.

Большинство знает что классов как таковых в perl5 нет. Вместо этого издавна использовался blessed hash, т.е. объект класса создавался примерно так(этот код естественно в соответствующем классу package):
sub new {
my $self = {};
$self->{NAME} = undef;
$self->{VERSION} = undef;
$self->{OPTIONS} = [];
bless($self);
return $self;
}

sub method1 {
...
}

sub _method2 {
...
}
Ну а далее вся работа с модулем строилась на условных соглашениях, таких как например имена приватных методов и полей начинаются с подчёркивания (см. выше _method2). Естественно были решения позволяющие сделать правильные приватные методы (приведу достаточно экзотический пример: http://perlmonks.org/?node_id=332744 ), но их использование для каждого класса было не очень удобным, так как заставляло делать много лишних телодвижений. Некоторые использовали соглашение о приватных методах (имена с подчёркиванием) и скрипт-сканер который проверял все файлы проекта на то что никто не вызвал приватный метод как публичный. Но на мой взгляд это всё костыли, хоть и работающие.

Многие наверное читали книгу Конвэя: Perl Best Practicies (далее PBP). Там он предлагает отказаться от использования blessed hash объектов и использовать InsideOut объекты. Не знаю как точно перевести это, ну можно например как объекты наизнанку :). Смысл в том что объект не является хэшом с полями а наоборот для каждого поля класса создаётся хэш или массив. В книге Конвэй в основном рассматривает свой собственный класс для использования InsideOut объектов: Class::Std . Но на практике он не всегда идеален. Рассмотрим например класс в котором в конструкторе кидается exception:

package cstd;

use Carp qw(croak);

use Class::Std;
{

sub sub1 : PRIVATE {
croak "sub1 exception";
}

sub BUILD {
sub1();
}
}
1;


Code syntax highlighting by VIM captured with ScreenShot script

И вот этот в котором exception в конструкторе не кидается:

package cstd2;

use Carp qw(croak);

use Class::Std;
{

sub sub1 {
croak "sub1 exception";
}

sub BUILD {

}
}
1;


Code syntax highlighting by VIM captured with ScreenShot script
И выполним вот этот тестовый скрипт:
#!/usr/bin/perl

use strict;
use warnings;
use cstd;
use cstd2;

my $a;

print "cstd:\n";
eval {
$a = cstd->new();
};

print $@ if ($@);

print "\ncstd2:\n";
$a = cstd2->new();
eval {
$a->sub1;
};

print $@ if ($@);


Code syntax highlighting by VIM captured with ScreenShot script

В результате получим вот это:
cstd:
sub1 exception at /usr/lib/perl5/site_perl/5.8.8/Class/Std.pm line 438

cstd2:
sub1 exception at cstd.pl line 20
Как видим в первом случае невоможно понять где произошло исключение, в то время когда exception генерится не в конструкторе - то получаем сообщение о том что exception в нашем скрипте на строке 20.

Создадим класс с exception в методе вызываемом в конструкторе с помощью Object::InsideOut :

package objInOut;
{
use strict;
use warnings;
use Carp qw(croak);
use Object::InsideOut;

sub sub1 :Private {
croak "sub1 exception";
}

sub _init :Init {
sub1();
}
}
1;


Code syntax highlighting by VIM captured with ScreenShot script


Выполним следующий скрипт:

#!/usr/bin/perl

use strict;
use warnings;
use objInOut;
use Carp qw(croak);

my $a;

print "object inside out:\n";
eval {
$a = objInOut->new();
};

print $@ if ($@);


Code syntax highlighting by VIM captured with ScreenShot script

В выводе получим:
object inside out:
OIO error: Trapped uncaught error
Error: sub1 exception at objinsout.pl line 12
Package: Carp
File: /usr/lib/perl5/5.8.8/Carp.pm
Line: 269


Trace begun at /usr/lib/perl5/5.8.8/Carp.pm line 269
Carp::croak('sub1 exception') called at objInOut.pm line 9
objInOut::sub1 at objInOut.pm line 13
Object::InsideOut::new('objInOut', 'HASH(0x847d020)') called at objinsout.pl line 12
eval {...} at objinsout.pl line 11

Тут как видим уже легко понять где точно произошло исключение.

Кроме этого Object::InsideOut поддерживает threads - т.е. я бы советовал использовать именно эту реализацию InsideOut классов. Вобщем это ещё одно подтверждения того что часто бывает что удачная реализация удаётся не идейным вдохновителям а кому-то ещё...