Masteries

技術的なことや仕事に関することを書いていきます.

忘備録: Smart::Args::TypeTinyの`default`で時間がかかる処理を呼ぶ時はサブルーチンリファレンスで渡した方が理想的

Perlの忘備録というか何というか... まあタイトルにある通りです. 或いはSmart::Args::TypeTinyのIMCOMPATIBLE CHANGES WITH Smart::ArgsのDefault parameter can take coderef as lazy valueを読みましょう.

use strict;
use warnings;

use Smart::Args::TypeTiny qw(args);

sub test {
    args
        my $arg1 => { isa => 'Str', optional => 1, default => sub { generator('from test1') } },
        my $arg2 => { isa => 'Str', optional => 1, default => generator('from test2') };

    return 1;
}

sub generator {
    my ($message) = @_;

    warn $message;

    return $message;
}

warn "case 1";
test();
warn "case 2";
test(arg1 => 'test');
warn "case 3";
test(arg2 => 'test');
warn "case 4";
test(arg1 => 'test', arg2 => 'test');

これを実行すると, 次のような結果になります(見やすくなるように改行を入れています):

case 1 at smart-args.pl line 22.
from test2 at smart-args.pl line 17.
from test1 at smart-args.pl line 17.
args: from test1, from test2 at smart-args.pl line 11.

case 2 at smart-args.pl line 24.
from test2 at smart-args.pl line 17.
args: test, from test2 at smart-args.pl line 11.

case 3 at smart-args.pl line 26.
from test2 at smart-args.pl line 17. # ← case 3 では arg2 が渡っていてデフォルト値が不要なのに, generator が呼び出されている
from test1 at smart-args.pl line 17.
args: from test1, test at smart-args.pl line 11.

case 4 at smart-args.pl line 28.
from test2 at smart-args.pl line 17. # ← case 4 では arg2 が渡っていてデフォルト値が不要なのに, generator が呼び出されている. arg1 も同様だけど, generator は呼び出されていない
args: test, test at smart-args.pl line 11.

...要するに, default => generator() のようにオプションを指定すると, 引数が渡ってこようが渡ってこまいが毎回generator()を実行しますが, サブルーチンリファレンスで渡すと引数が渡ってこなかった時だけ(サブルーチンリファレンスを呼び出す形で)generator()を実行してデフォルト値を生成するようになっています. 1とか2とかを渡すならともかく, generator() で行われる処理の内容によっては, チリツモで処理時間が伸びていきそうなので, 気をつけたいところです.

ちなみに...

Smart::Argsでdefaultにサブルーチンリファレンスを渡すと, サブルーチンリファレンスそのものがデフォルトの値になりますので, 注意しましょう:

case 1 at smart-args.pl line 22.
from test2 at smart-args.pl line 17.
args: CODE(0x14d0c6568), from test2 at smart-args.pl line 11.