The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

Sub::Meta - handle subroutine meta information

SYNOPSIS

    use Sub::Meta;

    sub hello($) :method { }
    my $meta = Sub::Meta->new(sub => \&hello);
    $meta->subname; # => hello

    $meta->sub;        # \&hello
    $meta->subname;    # hello
    $meta->fullname    # main::hello
    $meta->stashname   # main
    $meta->file        # path/to/file.pl
    $meta->line        # 5
    $meta->is_constant # !!0
    $meta->prototype   # $
    $meta->attribute   # ['method']
    $meta->is_method   # undef
    $meta->parameters  # undef
    $meta->returns     # undef
    $meta->display     # 'sub hello'

    # setter
    $meta->set_subname('world');
    $meta->subname; # world
    $meta->fullname; # main::world

    # apply to sub
    $meta->apply_prototype('$@');
    $meta->prototype; # $@
    Sub::Util::prototype($meta->sub); # $@

And you can hold meta information of parameter type and return type. See also Sub::Meta::Parameters and Sub::Meta::Returns.

    $meta->set_parameters(args => ['Str']));
    $meta->parameters->args; # [ Sub::Meta::Param->new({ type => 'Str' }) ]

    $meta->set_args(['Str']);
    $meta->args; # [ Sub::Meta::Param->new({ type => 'Str' }) ]

    $meta->set_returns('Str');
    $meta->returns->scalar; # 'Str'
    $meta->returns->list;   # 'Str'

And you can compare meta informations:

    my $other = Sub::Meta->new(subname => 'hello');
    $meta->is_same_interface($other); # 1
    $meta eq $other; # 1

DESCRIPTION

Sub::Meta provides methods to handle subroutine meta information. In addition to information that can be obtained from subroutines using module B etc., subroutines can have meta information such as arguments and return values.

METHODS

new

Constructor of Sub::Meta.

    use Sub::Meta;
    use Types::Standard -types;

    # sub Greeting::hello(Str) -> Str
    Sub::Meta->new(
        fullname    => 'Greeting::hello',
        is_constant => 0,
        prototype   => '$',
        attribute   => ['method'],
        is_method   => 1,
        parameters  => { args => [{ type => Str }]},
        returns     => Str,
    );

Others are as follows:

    # sub add(Int, Int) -> Int
    Sub::Meta->new(
        subname => 'add',
        args    => [Int, Int],
        returns => Int,
    );

    # method hello(Str) -> Str
    Sub::Meta->new(
        subname   => 'hello',
        args      => [{ message => Str }],
        is_method => 1,
        returns   => Str,
    );

    # sub twice(@numbers) -> ArrayRef[Int]
    Sub::Meta->new(
        subname   => 'twice',
        args      => [],
        slurpy    => { name => '@numbers' },
        returns   => ArrayRef[Int],
    );

    # Named parameters:
    # sub foo(Str :a) -> Str
    Sub::Meta->new(
        subname   => 'foo',
        args      => { a => Str },
        returns   => Str,
    );

    # is equivalent to
    Sub::Meta->new(
        subname   => 'foo',
        args      => [{ name => 'a', isa => Str, named => 1 }],
        returns   => Str,
    );

Another way to create a Sub::Meta is to use Sub::Meta::Creator:

    use Sub::Meta::Creator;
    use Sub::Meta::Finder::FunctionParameters;

    my $creator = Sub::Meta::Creator->new(
        finders => [ \&Sub::Meta::Finder::FunctionParameters::find_materials ],
    );

    use Function::Parameters;
    use Types::Standard -types;

    method hello(Str $msg) { }
    my $meta = $creator->create(\&hello);
    # =>
    # Sub::Meta
    #   args [
    #       [0] Sub::Meta::Param->new(name => '$msg', type => Str)
    #   ],
    #   invocant   Sub::Meta::Param->(name => '$self', invocant => 1),
    #   nshift     1,
    #   slurpy     !!0

ACCESSORS

sub

Accessor for subroutine.

sub
    method sub() => Maybe[CodeRef]

Return a subroutine.

has_sub
    method has_sub() => Bool

Whether Sub::Meta has subroutine or not.

set_sub($sub)
    method set_sub(CodeRef $sub) => $self

Setter for subroutine.

    sub hello { ... }
    $meta->set_sub(\&hello);
    $meta->sub # => \&hello

    # And set subname, stashname
    $meta->subname; # hello
    $meta->stashname; # main

subname

Accessor for subroutine name

subname
    method subname() => Str
has_subname
    method has_subname() => Bool

Whether Sub::Meta has subroutine name or not.

set_subname($subname)
    method set_subname(Str $subname) => $self

Setter for subroutine name.

    $meta->subname; # hello
    $meta->set_subname('world');
    $meta->subname; # world
    Sub::Util::subname($meta->sub); # hello (NOT apply to sub)
apply_subname($subname)
    method apply_subname(Str $subname) => $self

Sets subroutine name and apply to the subroutine reference.

    $meta->subname; # hello
    $meta->apply_subname('world');
    $meta->subname; # world
    Sub::Util::subname($meta->sub); # world

fullname

Accessor for subroutine full name

fullname
    method fullname() => Str

A subroutine full name, e.g. main::hello

has_fullname
    method has_fullname() => Bool

Whether Sub::Meta has subroutine full name or not.

set_fullname($fullname)
    method set_fullname(Str $fullname) => $self

Setter for subroutine full name.

stashname

Accessor for subroutine stash name

stashname
    method stashname() => Str

A subroutine stash name, e.g. main

has_stashname
    method has_stashname() => Bool

Whether Sub::Meta has subroutine stash name or not.

set_stashname($stashname)
    method set_stashname(Str $stashname) => $self

Setter for subroutine stash name.

subinfo

Accessor for subroutine information

subinfo
    method subinfo() => Tuple[Str,Str]

A subroutine information, e.g. ['main', 'hello']

set_subinfo([$stashname, $subname])
    method set_stashname(Tuple[Str $stashname, Str $subname]) => $self

Setter for subroutine information.

file, line

Accessor for filename and line where subroutine is defined

file
    method file() => Maybe[Str]

A filename where subroutine is defined, e.g. path/to/main.pl.

has_file
    method has_file() => Bool

Whether Sub::Meta has a filename where subroutine is defined.

set_file($filepath)
    method set_file(Str $filepath) => $self

Setter for file.

line
    method line() => Maybe[Int]

A line where the definition of subroutine started, e.g. 5

has_line
    method has_line() => Bool

Whether Sub::Meta has a line where the definition of subroutine started.

set_line($line)
    method set_line(Int $line) => $self

Setter for line.

is_constant

is_constant
    method is_constant() => Maybe[Bool]

If the subroutine is set, it returns whether it is a constant or not, if not set, it returns undef.

set_is_constant($bool)
    method set_is_constant(Bool $bool) => $self

Setter for is_constant.

prototype

Accessor for prototype of subroutine reference.

prototype
    method prototype() => Maybe[Str]

If the subroutine is set, it returns a prototype of subroutine, if not set, it returns undef. e.g. $@

has_prototype
    method has_prototype() => Bool

Whether Sub::Meta has prototype or not.

set_prototype($prototype)
    method set_prototype(Str $prototype) => $self

Setter for prototype.

apply_prototype($prototype)
    method apply_prototype(Str $prototype) => $self

Sets subroutine prototype and apply to the subroutine reference.

attribute

Accessor for attribute of subroutine reference.

attribute
    method attribute() => Maybe[ArrayRef[Str]]

If the subroutine is set, it returns a attribute of subroutine, if not set, it returns undef. e.g. ['method'], undef

has_attribute
    method has_attribute() => Bool

Whether Sub::Meta has attribute or not.

set_attribute($attribute)
    method set_attribute(ArrayRef[Str] $attribute) => $self

Setter for attribute.

apply_attribute(@attribute)
    method apply_attribute(Str @attribute) => $self

Sets subroutine attributes and apply to the subroutine reference.

is_method

is_method
    method is_method() => Bool

Whether the subroutine is a method or not.

set_is_method($bool)
    method set_is_method(Bool $bool) => Bool

Setter for is_method.

parameters

Accessor for parameters object of Sub::Meta::Parameters

parameters
    method parameters() => InstanceOf[Sub::Meta::Parameters]

If the parameters is set, it returns the parameters object.

set_parameters($parameters)
    method set_parameters(InstanceOf[Sub::Meta::Parameters] $parameters) => $self
    method set_parameters(@sub_meta_parameters_args) => $self

Sets the parameters object of Sub::Meta::Parameters.

    my $meta = Sub::Meta->new;

    my $parameters = Sub::Meta::Parameters->new(args => ['Str']);
    $meta->set_parameters($parameters);

    # or
    $meta->set_parameters(args => ['Str']);
    $meta->parameters; # => Sub::Meta::Parameters->new(args => ['Str']);

    # alias
    $meta->set_args(['Str']);
args

The alias of parameters.args.

set_args($args)

The alias of parameters.set_args.

all_args

The alias of parameters.all_args.

nshift

The alias of parameters.nshift.

set_nshift($nshift)

The alias of parameters.set_nshift.

invocant

The alias of parameters.invocant.

invocants

The alias of parameters.invocants.

set_invocant($invocant)

The alias of parameters.set_invocant.

slurpy

The alias of parameters.slurpy.

set_slurpy($slurpy)

The alias of parameters.set_slurpy.

returns

Accessor for returns object of Sub::Meta::Returns

returns
    method returns() => InstanceOf[Sub::Meta::Returns]

If the returns is set, it returns the returns object.

set_returns($returns)
    method set_returns(InstanceOf[Sub::Meta::Returns] $returns) => $self
    method set_returns(@sub_meta_returns_args) => $self

Sets the returns object of Sub::Meta::Returns or any object.

    my $meta = Sub::Meta->new;
    $meta->set_returns({ type => 'Type'});
    $meta->returns; # => Sub::Meta::Returns->new({type => 'Type'});

    # or
    $meta->set_returns(Sub::Meta::Returns->new(type => 'Foo'));
    $meta->set_returns(MyReturns->new)

METHODS

apply_meta($other_meta)

    method apply_meta(InstanceOf[Sub::Meta] $other_meta) => $self

Apply subroutine subname, prototype and attributes of $other_meta.

is_same_interface($other_meta)

    method is_same_interface(InstanceOf[Sub::Meta] $other_meta) => Bool

A boolean value indicating whether the subroutine's interface is same or not. Specifically, check whether subname, is_method, parameters and returns are equal.

is_strict_same_interface($other_meta)

Alias for is_same_interface

is_relaxed_same_interface($other_meta)

    method is_relaxed_same_interface(InstanceOf[Sub::Meta] $other_meta) => Bool

A boolean value indicating whether the subroutine's interface is relaxed same or not. Specifically, check whether subname, is_method, parameters and returns satisfy the condition of $self side.

Difference between strict and relaxed

If it is is_relaxed_same_interface method, the conditions can be many. For example, the number of arguments can be many. The following code is a test to show the difference between strict and relaxed.

    my @tests = (
        {},                { subname => 'foo' },
        {},                { args => [Int] },
        { args => [Int] }, { args => [Int, Str] },
        { args => [Int] }, { args => [Int], slurpy => Str },
        { args => [Int] }, { args => [{ type => Int, name => '$a' }] },
        {},                { returns => Int },
        { returns => { scalar => Int } }, { returns => { scalar => Int, list => Int } },
    );

    while (@tests) {
        my ($a, $b) = splice @tests, 0, 2;
        my $meta = Sub::Meta->new($a);
        my $other = Sub::Meta->new($b);

        ok !$meta->is_strict_same_interface($other);
        ok $meta->is_relaxed_same_interface($other);
    }

is_same_interface_inlined($other_meta_inlined)

    method is_same_interface_inlined(InstanceOf[Sub::Meta] $other_meta) => Str

is_strict_same_interface_inlined($other_meta)

Alias for is_same_interface_inlined

Returns inlined is_same_interface string:

    use Sub::Meta;
    my $meta = Sub::Meta->new(subname => 'hello');
    my $inline = $meta->is_same_interface_inlined('$_[0]');
    # $inline looks like this:
    #    Scalar::Util::blessed($_[0]) && $_[0]->isa('Sub::Meta')
    #    && defined $_[0]->subname && 'hello' eq $_[0]->subname
    #    && !$_[0]->is_method
    #    && !$_[0]->parameters
    #    && !$_[0]->returns
    my $check = eval "sub { $inline }";
    $check->(Sub::Meta->new(subname => 'hello')); # => OK
    $check->(Sub::Meta->new(subname => 'world')); # => NG

is_relaxed_same_interface_inlined($other_meta_inlined)

    method is_relaxed_same_interface_inlined(InstanceOf[Sub::Meta] $other_meta) => Str

Returns inlined is_relaxed_same_interface string.

error_message($other_meta)

    method error_message(InstanceOf[Sub::Meta] $other_meta) => Str

Return the error message when the interface is not same. If same, then return empty string

relaxed_error_message($other_meta)

    method relaxed_error_message(InstanceOf[Sub::Meta] $other_meta) => Str

Return the error message when the interface does not satisfy the $self meta. If match, then return empty string.

display

    method display() => Str

Returns the display of Sub::Meta:

    use Sub::Meta;
    use Types::Standard qw(Str);
    my $meta = Sub::Meta->new(
        subname => 'hello',
        is_method => 1,
        args => [Str],
        returns => Str,
    );
    $meta->display;  # 'method hello(Str) => Str'

OTHERS

parameters_class

    method parameters_class() => Str

Returns class name of parameters. default: Sub::Meta::Parameters Please override for customization.

returns_class

    method returns_class() => Str

Returns class name of returns. default: Sub::Meta::Returns Please override for customization.

NOTE

setter

You can set meta information of subroutine. set_xxx sets xxx and does not affect subroutine reference. On the other hands, apply_xxx sets xxx and apply xxx to subroutine reference.

Setter methods of Sub::Meta returns meta object. So you can chain setting:

    $meta->set_subname('foo')
         ->set_stashname('Some')

Pure-Perl version

By default Sub::Meta tries to load an XS implementation for speed. If that fails, or if the environment variable PERL_SUB_META_PP is defined to a true value, it will fall back to a pure perl implementation.

SEE ALSO

Sub::Identify, Sub::Util, Sub::Info

LICENSE

Copyright (C) kfly8.

This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.

AUTHOR

kfly8 <kfly@cpan.org>