Beefy Boxes and Bandwidth Generously Provided by pair Networks
Welcome to the Monastery
 
PerlMonks  

A couple questions on Moose::MOP::Class

by John M. Dlugosz (Monsignor)
on May 02, 2011 at 22:50 UTC ( [id://902579]=perlquestion: print w/replies, xml ) Need Help??

John M. Dlugosz has asked for the wisdom of the Perl Monks concerning the following question:

I'm reading the docs, and I don't understand a couple of things.

Under the discussion of performance of method wrapping, "simple wrapper with no modifiers". If the modifiers are qw/before after around/, what else is there? How can you have a wrapper with no modifiers? For that matter, how can you have more than one on one wrapper?

"Similarly, methods named with a fully qualified name using Sub::Name are also included." Looking at the docs for Sub::Name, I see it gives a name to a sub or a specific instance of a closure for purposes of Carp etc. telling me the correct location.

Naming a function does nothing to install it into the Package, which is what is used for method dispatch. When doing dispatch, the name in the Package (symbol table) is what is used as "the" name to look up, so what does giving it a different internal name have to do with anything?

  • Comment on A couple questions on Moose::MOP::Class

Replies are listed 'Best First'.
Re: A couple questions on Moose::MOP::Class
by stvn (Monsignor) on May 03, 2011 at 02:00 UTC
    How can you have a wrapper with no modifiers?

    That is referring to the base overhead of Class::MOP::Method::Wrapped, which is the object which must wrap the CODE ref in order to support the method modifiers. So that performance hit that we talk about there is the base cost for using modifiers, any actual modifiers you add onto it costs additional. Think of it like a cover charge :)

    Looking at the docs for Sub::Name, I see it gives a name to a sub or a specific instance of a closure for purposes of Carp etc. telling me the correct location. Naming a function does nothing to install it into the Package, which is what is used for method dispatch. When doing dispatch, the name in the Package (symbol table) is what is used as "the" name to look up, so what does giving it a different internal name have to do with anything?

    Moose and Class::MOP make a distinction between methods defined in your class, and functions imported from other modules. This is why if you import function 'foo' from module 'Foo::Bar' in your role, and then compose your role into a class, the class will not have a 'foo' method. Similarly if you query the methods of a class via the MOP, you will find that there are no imported functions in the list, even though they are still in your symbol table (assuming you are not using namespace::clean that is).

    So since Perl has no real way to tell the difference between a method and a function, Moose had to devise a test for method purity. The test that was settled on was to dive deep into the B level of things and look at the names in the actual stash. If a CV structure is linked to the proper package stash then we know that it was defined in that package and we can therefore assume it is a method. If it has a different package stash name, then we assume it is imported and therefore is not a method. This works great except that Moose generates a LOT of methods and those would not, on their own, have the proper package stash name. So what Sub::Name provides is exactly that, it allows you to take a CV and assign the package stash name as well as the CV stash name.

    Welcome to the twisty maze of ugliness that is making Perl 5 bend to your whim (and do so with a production-quality level of stability too).

    -stvn
      Re Class::MOP::Method::Wrapped: Out of curiosity, does it gather all the before/after/around stuff into one thoughtful list, or does it just keep chaining things with more single-use wrappers?

      And I was wondering... how does an 'after' method get called without having the wrapper show up in the call stack? If the original is chained to with a GOTO, it won't get control back again.

      Moose and Class::MOP make a distinction between methods defined in your class, and functions imported from other modules.
      Yea, that's what "no moose" and "autoclean" is all about. So, the method enumerator does the same thing as autoclean, and they work by comparing the name recorded in the function itself with the name it's being accessed under, to tell if it was not imported.

      Let me see if I'm following... for code that's been generated or otherwise not created using the classic 'sub' at package level, it won't record that origin and would always look like it was imported, even for generated methods like the accessors. So what that passage was trying to say is that a fully-quallified name in the Sub::Name or the natural origin of the function is used to determine if it was imported. Right?

      From reading Sub::Name, I thought that it simply records what you wanted in the same spot that a natural born sub does anyway. You talk about "B" and "stash" and other things not in my namespace, which makes me think now that the determination of the natural born name was not something it was originally meant to do. So Sub::Name is a newer thing and Carp et al. were updated to look at it, but defining a sub still doesn't populate that itself?

      Re Welcome to the twisty maze: Thanks.

        Re Class::MOP::Method::Wrapped: Out of curiosity, does it gather all the before/after/around stuff into one thoughtful list, or does it just keep chaining things with more single-use wrappers?

        No, the Class::MOP::Method::Wrapped object keeps 3 ordered lists, one of befores, one of arounds and one of afters and it makes sure to apply them in the order you defined them. The docs actually go into a lot of detail about how this works.

        And I was wondering... how does an 'after' method get called without having the wrapper show up in the call stack?

        It does show up actually, they are named ":before", ":around" and ":after", here is a comment from the source explaining some of this:

        # NOTE: # the methods above used to be named like this: # ${pkg}::${method}:(before|after|around) # but this proved problematic when using one modifier # to wrap multiple methods (something which is likely # to happen pretty regularly IMO). So instead of naming # it like this, I have chosen to just name them purely # with their modifier names, like so: # :(before|after|around) # The fact is that in a stack trace, it will be fairly # evident from the context what method they are attached # to, and so don't need the fully qualified name.

        If the original is chained to with a GOTO, it won't get control back again.

        It isn't done with GOTO actually, for exactly that reason, it is all quite controlled. If you look at the source you will see the $_build_wrapped_method method which does much of the dirty work.

        o, the method enumerator does the same thing as autoclean, ...

        Yes, but the method enumerator did it long before autoclean existed and way before autoclean was stable/sane.

        Let me see if I'm following... for code that's been generated or otherwise not created using the classic 'sub' at package level, it won't record that origin and would always look like it was imported, even for generated methods like the accessors. So what that passage was trying to say is that a fully-quallified name in the Sub::Name or the natural origin of the function is used to determine if it was imported. Right?

        Yes, but Sub::Name alters the internal (C-level) structure of the CV data structure to basically add the fully-qualified name. It then appears just as if it was originally defined in that package.

        You talk about "B" and "stash" and other things not in my namespace, ...

        B is the "The Perl Compiler Backend", it is a way to access all the C-level data structures from Perl. The "stash" is the name for an element of those data structures, specifically the place where I go to find the actual fully-qualified name of the sub.

        ... which makes me think now that the determination of the natural born name was not something it was originally meant to do.

        Right, we had to get all sneaky and use XS to get at this stuff, it is not normally exposed in Perl.

        So Sub::Name is a newer thing and Carp et al. were updated to look at it, but defining a sub still doesn't populate that itself?

        No, Sub::Name alters the underly C-level structure such that when Carp gets to it,... it is just there. No changes had to be made to other modules.

        -stvn
      ... since Perl has no real way to tell the difference between a method and a function...

      Maybe yours doesn't, but I have a patched version of Perl 5.13 right here which does!

        How does it tell? Any sub created in the Package looks the same... how does it know you intend to call it using the arrow-notation dispatch and expect the class or instance as the first parameter?

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://902579]
Approved by planetscape
Front-paged by planetscape
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others chanting in the Monastery: (5)
As of 2024-04-18 15:10 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found