October 2011 Archives

My Perl hero of the week: Eric Strom (ASG)

http://search.cpan.org/~asg/

Hash-Abbrev-0.01  Text::Abbrev with aliases
List-Gen-0.96         provides functions for generating lists
Lvalue-0.21            add lvalue getters and setters to existing objects
Perl6-Feeds-0.20       implements perl6 feed operators in perl5 via source filtering
Test-Magic-0.21    terse tests with useful error feedback
Whatever-0.21       a perl6ish whatever-star for perl5
XUL-Gui-0.63         render cross platform gui applications with firefox from perl

He seems to be seriously into functional programming, Haskell and perl6 alike, which seems to be totally weird.

E.g. look what he is doing with us:

use List::Gen;

print "@$_\n" for every 5 => 1 .. 15;
# 1 2 3 4 5
# 6 7 8 9 10
# 11 12 13 14 15

print mapn {"$_[0]: $_[1]\n"} 2 => %myhash;

my $ints    = <0..>;
my $squares = gen {$_**2} $ints;

say "@$squares[2 .. 6]"; # 4 9 16 25 36

$ints->zip('.', -$squares)->say(6); # 0-0 1-1 2-4 3-9 4-16 5-25

list(1, 2, 3)->gen('**2')->say; # 1 4 9

my $fib = ([0, 1] + iterate {fib($_, $_ + 1)->sum})->rec('fib');
my $fac = iterate {$_ < 2 or $_ * self($_ - 1)}->rec;

say "@$fib[0 .. 15]";  #  0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610
say "@$fac[0 .. 10]";  #  1 1 2 6 24 120 720 5040 40320 362880 3628800

say <0, 1, * + * ...>->take(10)->str;   # 0 1 1 2 3 5 8 13 21 34
say <[..*] 1, 1..>->str(8);             # 1 1 2 6 24 120 720 5040

<**2 for 1..10 if even>->say;           # 4 16 36 64 100

<1..>->map('**2')->grep(qr/1/)->say(5); # 1 16 81 100 121

Gather/Take is also somewhere, though it's no real take as shown at YAPC::EU 2011 by this german guy from Frankfurt.pm. (...)

But look at the other modules:

use Whatever;
my $greet = 'hello, ' . &* . '!';
say $greet->('world'); # prints 'hello, world!'

my $result = $someobj->map(sub{$_ * 2});
# simplified as
my $result = $someobj->map(&* * 2);

&* is interpolated into the arg if called as function. Any SV with &* is treated as CVREF with special &* handling. No need to write lots of anonymous subs anymore.

Or look at Lvalue which creates setters and getters for any Class fields.

sub overload {
    my ($object, $proxy) = @_;
    my $pkg = ref $object;
    my $overloader = sub {
        my $op = shift;
        sub {
            if (my $sub = overload::Method($pkg, $op)) {
                @_ = ($object, @_[1, 2]);
                goto &$sub;
            }
            Carp::croak "no overload method '$op' in $pkg";
        }
    };
    no strict 'refs';
    my $fallback = ${$pkg.'::()'};

    my $overload = join ', ' =>
        defined $fallback ? 'fallback => $fallback' : (),
        map "'$_' => \$overloader->('$_')" =>
            grep s/^\((?=..)// => keys %{$pkg.'::'};

    eval qq {package $proxy;
        our \@ISA = 'Lvalue::Loader';
        use overload $overload;
    } or Carp::carp "Lvalue: overloading not preserved for $pkg, "
                  . "bug reports or patches welcome.\n  $@";
}

This works but is way over my head. Hope to understand it some day.

The crown jewel is XUL::Gui

With super simple syntax he creates a full XUL binding, which enables modern cross-platform GUIs, remote or locally. There's a event-loop, it totally looks like Tcl/Tk, just with a better language (HTML and Perl) and easier to learn. Everybody knows HTML, but who can remember Win32::GUI, Tcl/Tk and all the other GUI frameworks. For non-mozilla browsers there is Web::GUI as the limited version of it. But you can always ship or download xulrunner.

use XUL::Gui;
display Label 'hello, world!';

use XUL::Gui;
display Window title => "XUL::Gui's long hello",
    GroupBox(
        Caption('XUL'),
        Button(
            label     => 'click me',
            oncommand => sub {$_->label = 'ouch'}
        ),
        Button(
            id        => 'btn',
            label     =>'automatic id registration',
            oncommand => sub {
                ID(btn)->label = 'means no more variable clutter';
                ID(txt)->value = 'and makes cross tag updates easy';
        }),
        Button(
            type  => 'menu',
            label => 'menu button',
            MenuPopup map
                {MenuItem label => $_} qw/first second third/
        ),
        TextBox( id => 'txt', width => 300 ),
        ProgressMeter( mode => 'undetermined' ),
    ),
    GroupBox(
        Caption('HTML too'),
        TABLE( width => '100%',
            TR map {TD $_}
                'one', I('two'), B('three'), U('four'), SUP('five')
        ),
        BR, HR,
        P('all the HTML tags are in CAPS'),
    );

The worst Perl API's

Perl usually has a pretty and wellthought API and tools to maintain it.

But of cause there is also a hall of shame in some old dirty corners, nobody ever uses.

No.1 B.xs: make_warnings_object

In order to provide Perl read-access to lexical warnings some fake SV is passed to B, created internally by make_warnings_object(). A warning is basically a short integer between 1-6, and lexical warnings are integers > 6 checked by bitmasks e.g. see warnings.pm.

The C API is SV* until 5.8.9 and since 5.10 STRLEN*, which is basically a int*.

Since 5.8.9 and 5.9.4 WARN objects are PV's ("strings"), because it's natural to represent a ptr as RV to IV ("intref") as with < 5.8.9, or as PV pointer. However due to a bug I don't care to be fixed, the integer value is the length of the string provided as PVX, and the string itself is some random value. So to read such a B::SV object for lexical warnings you read now the length of the $sv->PVX.

You decode such a SV with:

my $warn = $] >= 5.008009 ? length($sv->PVX) : $sv->IV;

A normal API would just pass the IV value to B, not even a full SV. Or a B::SPECIAL object as it is done for non-lexical warnings. But leaving a IV as before would have also been fine.

No.2 XSLoader files

XSLoader is the easier interface to load dynamic Perl modules. Compared to DynaLoader. It's so easy that there's even no function to accept the dynamic library to be loaded. It's entirely context dependent, it only takes this filename from the context of the call. So if you need to load dynamic XS modules from the Perl C API, you need to first set the CopFILE ptr of the previous nextstate cop to the path of the dynamic library, and then you call XSLoader::Load.

 CopFILE_set(cxstack[0].blk_oldcop, "$xspath");
 call_pv("XSLoader::load", G_VOID|G_DISCARD);

A normal API would provide e.g.

 XPUSHp("$xspath");
 call_pv("XSLoader::load", G_VOID|G_DISCARD);

That means XSLoader::load($filename) within Perl.

About Reini Urban

user-pic Working at cPanel on cperl, B::C (the perl-compiler), parrot, B::Generate, cygwin perl and more guts, keeping the system alive.