with Enumerable

RubyライクなEnumerable roleを作ってみた。

#!perl
package IO::Enumerable;
use Any::Moose; # requires Mouse 0.39

with 'Enumerable';

has handle => (
    is  => 'rw',
    isa => 'FileHandle',
);

sub each{
    my($self, $block) = @_;

    my $handle = $self->handle;
    local $_;
    while(<$handle>){
        $block->($_);
    }
}
package main;

my $io = IO::Enumerable->new(handle => \*DATA);
print $io->map(sub{ chomp; $_ })
    ->grep(qr/^b/)
    ->sort
    ->join(' '), "\n"; # => bar baz
__DATA__
foo
bar
baz

これはこれで便利ではあるが,これだとあまりにRuby的すぎる。Ruby的にするためにパフォーマンスが犠牲になっているので,それはまずい。CPANに上げるのはインターフェイスをもう少し考えてからにする。

Yet another native traits in Moose

id:tokuhiromの示唆を得て,Native traits/AttributeHelpersについて考えてみた。
現在MooseではMX::AttributeHelpersが"Native traits"として組み込みになっているが,それにもかかわらずhas()の時点でtraits => ['Array']などとしないと使用できない。これは冗長だ。

use Any::Moose;
has foo => (
    is  => 'ro',
    isa => 'ArrayRef',
    traits => ['Array'], # 冗長
    handles => {
        # ...
    },
);

"isa"でArrayRefを指定しているのだから,traits => ['Array']は明らかに冗長である。
ところで,native traitsの仕組みは委譲の一種と考えることができる。その方向で考えると,既存のMooseの委譲メカニズムでこれに近いことができることに気づく。

#!perl -w
use 5.010;
package My::HashRef;
use Any::Moose;
sub store{
    my($hashref, $key, $value) = @_;
    $hashref->{$key} = $value;
}

sub keys{
    my($hashref) = @_;
    return keys %{$hashref};
}
package Foo;
use Any::Moose;

has map => (
    is  => 'ro',
    isa => 'My::HashRef',
    default => sub{ My::HashRef->new },

    handles => {
        map_keys  => 'keys',
        map_store => 'store',
    },
);

package main;

my $foo = Foo->new();
$foo->map_store(hoge => 42);
$foo->map_store(fuga => 43);

say $foo->dump;
say join ' ', $foo->map_keys; # => fuga hoge
__END__

もちろん,今はこれをオブジェクトに対してしか行えないので,たとえば,isa => 'Str'などとすることはできない。このあたりをもう少し詰めれば,委譲メカニズムのちょっとした拡張でtraitsの指定を省略することができるかもしれない。