Masteries

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

忘備録: Smart::Args::TypeTinyのdefaultとoptionalについて

metacpan.org

Smart::Args::TypeTinyには, defaultoptional というパラメータがあります.

default

default は, 引数が渡らなかった時のデフォルト値を指定するパラメータです.

use strict;
use warnings;

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

sub test {
    args
        my $arg => { isa => 'Str', default => 'bar' };

    return $arg // 'undef';
}

say test(arg => 'foo'); # => 'foo'
say test(); # => 'bar' ( default が使われる )
say test(arg => undef); # => Type check failed in binding to parameter '$arg'; Undef did not pass type constraint "Str" (エラー)

なお, isaMaybe の場合はこうなります:

use strict;
use warnings;

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

sub test {
    args
        my $arg => { isa => 'Maybe[Str]', default => 'bar' };

    return $arg // 'undef';
}

say test(arg => 'foo'); # => 'foo'
say test(); # => 'bar'
say test(arg => undef); # => 'undef'

まとめると, default のパラメータがあるときは,

  • 引数としてundef以外を渡す ... 渡した値が使われる (isaを満たさない場合は当然エラーになる)
  • 引数を渡さない ... default が使われる
  • 引数としてundefを渡す
    • isaMaybeである ... undef がそのまま使われる (default は使われない)
    • isaMaybeでない ... isa を満たさないのでエラーになる

...という挙動になります.

optional

一方, optional はその名の通り, オプショナルであることを指定するパラメータです.

use strict;
use warnings;

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

sub test {
    args
        my $arg => { isa => 'Str', optional => 1 };

    return $arg // 'undef';
}

say test(arg => 'foo'); # => 'foo'
say test(); # => 'undef' (`optional` なので引数を渡さなくても問題ない. その場合は undef となる)
say test(arg => undef); # => 'undef' (明示的に引数として`undef`を渡しても問題ない)

まとめると, optional のパラメータがあるときは,

  • 引数としてundef以外を渡す ... 渡した値が使われる (isaを満たさない場合は当然エラーになる)
  • 引数を渡さない ... エラーにならず, undef として扱われる
  • 引数としてundefを渡す ... (isaを満たしていなくても)エラーにならず, そのまま undef として扱われる

defaultoptional

では, defaultoptionalのどちらも指定した場合はどうなるでしょうか.

use strict;
use warnings;

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

sub test {
    args
        my $arg => { isa => 'Str', optional => 1, default => 'bar' };

    return $arg // 'undef';
}

say test(arg => 'foo'); # => 'foo'
say test(); # => 'bar' (`optional` なので引数を渡さなくても問題ない. その場合は `default` で指定された値となる)
say test(arg => undef); # => 'undef' (明示的に `undef` を渡しても問題ない)

この挙動, よくよく見ると2番目に紹介したdefaultを指定 + isaMaybeと同じですね. つまり1つの挙動(optionalな引数で, undefを受け取れて, デフォルト値を指定したい)について, 2通りの書き方がある, ということです.

papixの意見

...とはいえ同じ挙動を複数の書き方ができる, というのは混乱を呼びそうです. どちらかに寄せよう! という合意を取れるといいのかな, と思います. 自分なら, こういう場合は前者(defaultを指定 + isaMaybeにする)として, defaultoptionalは同時に使わない... というふうにするのがわかりやすいんじゃないかな, と思いました.

というわけで以上です, すいませんがオチはないです...