「このコード、動いたけど納得いかないかっこ悪い。絶対にもっとまともな実装の方法が有るはず」
— sironekotoro💙💛 (@sironekotoro) 2022年6月13日
という直感は外れたことない気がする
という直感があって、その解が得られるタイミングは往々にして年単位だったりするんですが、割と早く解決できたのでメモ。
いや、今思うとこっちが近いかもしれない。
その君の勘から発した、 君の怒りと苛立ちは理由になる!
こんな例示コードがあります。
package Foo { use Mouse; use Function::Parameters; has List => ( is => 'rw', isa => 'ArrayRef[Str]', required => 0, ); method add($str) { my $array_ref = $self->List(); push @{$array_ref}, $str; $self->List($array_ref); } __PACKAGE__->meta->make_immutable(); } package main; use feature qw/say/; my $foo = Foo->new(); $foo->add('Web2.0'); $foo->add('Web3'); $foo->add('Web5'); say join " ", @{ $foo->List }; # Web2.0 Web3 Web5
Foo ってクラスを作り、そこに List というアトリビュート(またはプロパティ)と add というメソッドを作ったという図です。
List の中には配列リファレンスが入っており、add でどんどん追加していくという用途です。
で、問題はここ。add メソッド。
単に追加したいだけなのに、一旦配列リファレンスに出力して、それに追加したものを再度設定しているというコード。
method add($str) { my $array_ref = $self->List(); # 今あるリストから取得 push @{$array_ref}, $str; # 追加 $self->List($array_ref); # 追加した配列リファレンスで置き換え }
3行も必要か?
取り出して、処理して、戻す。
いや、絶対もっと楽な方法があるだろ・・・と思いつつ、見つけられなかったのでした。
MouseX::AttributeHelpers
はい。
そこでこのモジュールです。
メソッドが消えたじゃん・・・書かなくていいじゃん・・・可読性増してるじゃん・・・
package Foo { use Mouse; use MouseX::AttributeHelpers; has List => ( metaclass => 'Collection::Array', is => 'rw', isa => 'ArrayRef[Str]', required => 0, default => sub { [] }, provides => { push => 'add', } ); __PACKAGE__->meta->make_immutable(); } package main; use feature qw/say/; my $foo = Foo->new(); $foo->add('Web2.0'); $foo->add('Web3'); $foo->add('Web5'); say join " ", @{ $foo->List }; # Web2.0 Web3 Web5
この metaclass => 'Collection::Array',
には当然 Collection::Hash
など他のデータ構造を設定することが可能で、それぞれのデータ構造を扱う関数も provide で扱うことができます。
すごいなぁ。
コードを書く人の負担を軽減するコードというのは素晴らしいなぁ。
ありがちな処理は全部これで済んじゃう。ひゃー。
久々になんか感動したのでした。