Search code examples
perlmoose

In Moose subroutines, how does $meta get into @_?


chromatic's recent blog got me curious about the Moose subroutine has. I was looking at the Moose source code and noticed that inside the has subroutine, there is a $meta variable unpacked from @_. Where does $meta come from? I've started wading through the various Moose and Class::MOP modules. In many subroutines, it seems that $meta is commonly found as the first argument in @_, even though it is not specifically passed to it as an argument.

Edit: Here is the original source code for the has subroutine:

sub has {
    my $meta = shift;
    my $name = shift;

    Moose->throw_error('Usage: has \'name\' => ( key => value, ... )')
        if @_ % 2 == 1;

    my %options = ( definition_context => Moose::Util::_caller_info(), @_ );
    my $attrs = ( ref($name) eq 'ARRAY' ) ? $name : [ ($name) ];
    $meta->add_attribute( $_, %options ) for @$attrs;
}

Solution

  • molecules comment in ysth answer:

    I'm not sure how the has subroutine gets converted to this closure, but this definitely shows the curried nature of the imported has

    Here is (hopefully!) a simple example of how this could be achieved (however I suspect Moose does it in a much more complex and better way!)

    Meta.pm

    package Meta;
    
    sub new {
        my $class = shift;
        bless { @_ }, $class;
    }
    
    sub has {
        my $meta = shift;
        print "Given => @_ \n";
        print "meta $_ => ", $meta->{$_}, "\n" for keys %$meta;
    }
    
    1;
    

    Import.pm

    package Import;
    use strict;
    use warnings;
    use Meta;
    
    # some arbitrary meta info!
    our $Meta = Meta->new( a => 'A', b => 'B' );
    
    sub import {
        my $caller = caller;
    
        # import 'has' into caller namespace
        no strict 'refs';
        *{$caller . '::has'} = sub { $Meta->has(@_) };
    }
    
    1;
    

    meta_has.pl

    use strict;
    use warnings;
    use Import;
    
    has name => ( is => 'rw', isa => 'Int' );
    

    Now if you run meta_has.pl you will get:

    # Given => name is rw isa Int 
    # meta a => A
    # meta b => B
    

    Hope that helps.

    /I3az/