黒魔術で独自定義のenum型制約を満たす値のリ ストを取得する話

黒魔術で独自定義のenum型制約を満たす値のリ ストを取得する話

36e748c47de34549cfa3ac0950c5351d?s=128

ybrliiu

May 24, 2019
Tweet

Transcript

  1. 黒魔術で独自定義のenum 型制約を満たす値のリ ストを取得する話 Gotanda.pm #18

  2. 自己紹介 liiu / twitter @_ybrliiu Perl を書くお仕事をしています 趣味 Perl 歴史

    最近廃墟めぐりやVTuber にハマっています
  3. Perl5.30 release!!!

  4. Perl でも大規模なプロダクトになると型制約をつけて開発することが多いと思います CPAN にはType::Tiny, Moose, Mouse など、型制約を実現するモジュールがあります お仕事ではMouse を使っているので、独自型を定義するときは Mouse::Util::TypeConstraint

    を利 用しています use Mouse::Util::TypeConstraints qw( enum ); use Mouse::Util::TypeConstraints qw( enum ); enum RGBColors => qw( red green blue ); enum RGBColors => qw( red green blue );
  5. enum 型の型制約を満たす値のリストを取得したい状況が発生しました。 諸事情で型定義されているところをいじるのは難しい どうすれば可能なのか?

  6. 型情報を取得する Mouse::Util::TypeConstraints では、 find_type_constraint という関数で型情報 (Mouse::Meta::TypeConstraint のインスタンス) を取得できます しかし、このTypeConstraint オブジェクトから得られる情報は、型名と型制約を満たすか満たさ

    ないかを判定するCodeRef くらいです use v5.28; use v5.28; use warnings; use warnings; use utf8; use utf8; use Mouse::Util::TypeConstraints qw( enum find_type_constraint ); use Mouse::Util::TypeConstraints qw( enum find_type_constraint ); use DDP +{ deparse => 1, use_prototypes => 0 }; use DDP +{ deparse => 1, use_prototypes => 0 }; enum RGBColors => qw( red green blue ); enum RGBColors => qw( red green blue ); my $type_constraint = find_type_constraint('RGBColors'); my $type_constraint = find_type_constraint('RGBColors'); say $type_constraint->name; say $type_constraint->name; say 'Pass' if $type_constraint->check('red'); say 'Pass' if $type_constraint->check('red'); # check メソッドで渡された引数が、型制約を満たすかをチェックする関数が入っているインスタンス変数 # check メソッドで渡された引数が、型制約を満たすかをチェックする関数が入っているインスタンス変数 # アクセサを通して取得していないのですでにヤバそうな雰囲気がある # アクセサを通して取得していないのですでにヤバそうな雰囲気がある p ¥$type_constraint->{compiled_type_constraint}; p ¥$type_constraint->{compiled_type_constraint};
  7. enum 型の型制約を満たす値のリストの情報はどこにもない・・・ 詰みでは・・・

  8. そう言えばレキシカル変数を無理やりのぞくことができる変態モジュールがあるとどこかで見た 記憶が それを使って型制約を満たすかをチェックするクロージャを作っているメソッドのレキシカル変 数をのぞけば、型制約を満たす値のリストを取得可能では・・・?

  9. PadWalker https://metacpan.org/pod/PadWalker レキシカル変数をレキシカルスコープの外から見たり書き換えたりできます。 peek_sub という関数を使えばクロージャの中のレキシカル変数の内容を取り出せるらしい これでクロージャの中身無理やり覗けばなんかいけそう

  10. Mouse::Util::TypeConstraints ではenum 型の型制約をどのように実装しているのかみてみます sub enum { sub enum { my($name,

    %valid); my($name, %valid); if(!(@_ == 1 && ref($_[0]) eq 'ARRAY')){ if(!(@_ == 1 && ref($_[0]) eq 'ARRAY')){ $name = shift; $name = shift; } } %valid = map{ $_ => undef } %valid = map{ $_ => undef } (@_ == 1 && ref($_[0]) eq 'ARRAY' ? @{$_[0]} : @_); (@_ == 1 && ref($_[0]) eq 'ARRAY' ? @{$_[0]} : @_); # EnumType # EnumType return _define_type 1, $name => ( return _define_type 1, $name => ( as => 'Str', as => 'Str', optimized_as => sub{ optimized_as => sub{ return defined($_[0]) && !ref($_[0]) && exists $valid{$_[0]}; return defined($_[0]) && !ref($_[0]) && exists $valid{$_[0]}; }, }, ); ); } }
  11. %valid というハッシュを作り、それを型制約のチェックに利用している なるほどクロージャから変数 %valid の内容を持ってきてそのキーを全て取得すれば目的を達成 できる!!

  12. やってみましょう use v5.28; use v5.28; use warnings; use warnings; use

    utf8; use utf8; use Mouse::Util::TypeConstraints qw( enum find_type_constraint ); use Mouse::Util::TypeConstraints qw( enum find_type_constraint ); use DDP +{ deparse => 1, use_prototypes => 0 }; use DDP +{ deparse => 1, use_prototypes => 0 }; enum RGBColors => qw( red green blue ); enum RGBColors => qw( red green blue ); my $type_constraint = find_type_constraint('RGBColors'); my $type_constraint = find_type_constraint('RGBColors'); use PadWalker qw( peek_sub ); use PadWalker qw( peek_sub ); my $valid = peek_sub($type_constraint->{compiled_type_constraint})->{'%valid'}; my $valid = peek_sub($type_constraint->{compiled_type_constraint})->{'%valid'}; p [ keys %$valid ]; p [ keys %$valid ];
  13. できた!!!

  14. このように PadWalker を使えばレキシカル変数の内容を無理やり覗くことができます Making Easy Things Easy and Hard Things

    Possible を体現するすばらしいモジュー ルですね! 黒魔術たのしい^q^
  15. しかしプロダクトにこのようなコードを入れると混乱を招くこと必至・・・ 個人的にはツールやシュガー関数などの作成目的以外で PadWalker を使うのは基本避けたほうが いいと思います 今回のケースだと独自型を定義してるモジュールに定数を作るなどするなどして、それを呼ぶ ようにするのが筋がいい方法でしょうか

  16. ご清聴ありがとうございました