LoginSignup
8
3

More than 5 years have passed since last update.

Function::Parameters の紹介

Last updated at Posted at 2017-12-08

Function::Parameters の紹介をしてみたいと思います
これは、Perl v5.14 以上に、次のような関数定義方法を提供してくれます1

hello.pl
use Function::Parameters;
use Types::Standard qw/Str/;

fun hello(Str $world) {
  print "Hello $world"
}

型制約

上述の例の通り引数に型制約が指定できます
性能は、次のBenchmarkで試した限り、Data::Validator, Kavorka よりも性能は良いようです2

# perl: 5.026001
# validation modules:
#     Data::Validator/1.07
#     Function::Parameters/2.001003
#     Kavorka/0.037
# type system: Types::Standard/1.002001
# with type constraints
# Rate  D::Validator       Kavorka F::Parameters
# D::Validator  172031/s            --          -20%          -62%
# Kavorka       214369/s           25%            --          -53%
# F::Parameters 455903/s          165%          113%            --

指定できる型制約は、$type_constraint->check($value), $type_constraint->get_message($value) が duck type されているなら良く、Mouse, Moo, Type::Tiny などの型制約と相互運用できます3

クイックガイド

関数定義方法をまとめます
デフォルト値は、$x=xxx
名前付き引数を使う場合は、:$x
メソッド定義をしたい場合は、method を利用して定義することが特徴だと思います

code ............................... 説明 呼び出し例
fun foo( ) 引数なし foo, foo()
fun foo($x) 引数1つ foo(1)
fun foo($x=123) 引数1つ、default 0 foo(1), foo(),
fun foo($x, $y) 引数2つ foo(1, 2)
fun foo(@args) 引数複数 foo(1, 2, 3)
fun foo(%args) 引数複数、ただし、偶数個 foo(x => 1, y => 2)
fun foo($x, $y, @args) 引数2つ以上 foo(1, 2, 3)
fun foo($x, $y, %args) 引数2つ以上、ただし、最後の引数は偶数 foo(1, 2, z => 789)
fun foo(:$x) 名前付き引数1つ foo(x => 1)
fun foo(:$x, :$y) 名前付き引数2つ foo(x => 1, y => 2)
fun foo(:$x, :$y, @args) 名前付き引数2つとその他 foo(x => 1, y => 2, 3)
fun foo(:$x, :$y, %args) 名前付き引数2つ、ただし、最後の引数は偶数 foo(x => 1, y => 2, z => 3, w => 4)
method foo(...) Object method $self->foo()
method foo($class: ... ) Classs method Foo->foo()

その他、次のようなトピックは、公式のドキュメントをみてもらうのが良いように思います

実際どう動いているの?

もう少し挙動の中身を追うため、Deparse をしてみようと思います4
わかりやすさのため、Types::Standard ではなく、Dummy の型制約定義をして、Deparse をかけてみます

hello2.pl
package DummyType {
    sub new { bless {}, $_[0] }
    sub check { 1 }
    sub get_message { 'NG!!' }
}

package main;
use Function::Parameters;

fun Str() { DummyType->new() }

fun hello(Str $world) {
    print "Hello $world"
}
% perl -MO=Deparse,-d hello2.pl

package DummyType;
sub new {
    bless {}, $_[0];
}
sub check {
    1;
}
sub get_message {
    "NG!!";
}
package main;
{;};
use Function::Parameters;
sub Str {
    BEGIN {
        $^H{'Function::Parameters/config'} = 'HASH(0x7fd4ae15e048)';
    }
    Function::Parameters::_croak("Too many arguments for fun Str (expected 0, got " . @_ . ")") if @_ > 0;
    "DummyType"->new;
}
sub hello {
    BEGIN {
        $^H{'Function::Parameters/config'} = 'HASH(0x7fd4ae15e048)';
    }
    Function::Parameters::_croak("Too few arguments for fun hello (expected 1, got " . @_ . ")") if @_ < 1;
    Function::Parameters::_croak("Too many arguments for fun hello (expected 1, got " . @_ . ")") if @_ > 1;
    my($world) = @_;
    Function::Parameters::_croak("In fun hello: parameter 1 (\$world): " . bless( {}, 'DummyType' )->get_message($world)) unless bless( {}, 'DummyType' )->check($world);
    print "Hello $world";
}
BEGIN {
    $^H{'Function::Parameters/config'} = 'HASH(0x7fd4ae15e048)';
}
;
;
;

要するに、
hello の内部で、引数の数の確認と型の確認をし、合わなければ、croak するようなコードです

hello2.pl の挙動を、例えば、default 値をとるように次のようにしたとすると、

hello(Str $world='foo') ..

Deparse するとこんな具合です

    my($world) = @_;
    $world = "foo" if @_ < 1;

結構シンプルなのかなって思います!

まとめ

  • Function::Paramerters は、v5.14 以降に関数定義手法のsyntaxと型制約を提供してくれる
  • 性能は、よさそう
  • 挙動は、subroutine 内部で、引数、型チェックするようなコードの生成しているだけ5

以上、Function::Parameters の紹介でした〜


  1. いわゆる subroutine signatures のこと 

  2. 型制約の性能に関して、Params::Validate,Smart::Args は、Data::Validator 同封の benchmark で、Data::Validator 未満という結果でした。Method::Signatures は、Any::Moose 依存の解消がされていないため、候補から外しました 

  3. https://metacpan.org/pod/Function::Parameters#Type-constraints 

  4. Deparse するのに、-d をいれて、ワークアラウンドしています 参照 

  5. 挙動のカスタマイズは諸々できる https://metacpan.org/pod/Function::Parameters#Customizing-and-extending 

8
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
3