Actions Status Coverage Status MetaCPAN Release

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.

subname

Accessor for subroutine name

fullname

Accessor for subroutine full name

stashname

Accessor for subroutine stash name

subinfo

Accessor for subroutine information

file, line

Accessor for filename and line where subroutine is defined

is_constant

prototype

Accessor for prototype of subroutine reference.

attribute

Accessor for attribute of subroutine reference.

is_method

parameters

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

returns

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

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