Re: Hooking a gettor using a Trait
On Tue, Nov 10, 2009 at 09:02:53PM -0800, roger bush wrote: > The following doesn't work. The idea is to add a 'Cached' trait by calling > the default 'clearer' routine on an attribute. The reason it doesn't work > is that when it is invoked, there is no clearer routine. The code dies > with the stack trace saying it doesn't exist on the object (the attribute). > However the _name_ of the clearer routine is known at this point. > > use Moose::Role; > > around accessor_metaclass => sub > { > my $orig = shift; > my $self = shift; > > my $timer = $self->expire_timer; > if ($timer->reset_if_expired) > { > my $clearer = $self->clearer; > $self->$clearer (); > } > > return $self->$orig (@_); > }; You're... not really understanding what this is doing. accessor_metaclass is a method on the attribute which returns the name of a metaclass to use to generate the accessors. You need to write a trait for the accessor metaclass (Moose::Meta::Method::Accessor), and then modify accessor_metaclass to return the name of a subclass of Moose::Meta::Method::Accessor that has that trait applied. -doy
Hooking a gettor using a Trait
The following doesn't work. The idea is to add a 'Cached' trait by calling the default 'clearer' routine on an attribute. The reason it doesn't work is that when it is invoked, there is no clearer routine. The code dies with the stack trace saying it doesn't exist on the object (the attribute). However the _name_ of the clearer routine is known at this point. use Moose::Role; around accessor_metaclass => sub { my $orig = shift; my $self = shift; my $timer = $self->expire_timer; if ($timer->reset_if_expired) { my $clearer = $self->clearer; $self->$clearer (); } return $self->$orig (@_); }; I'm guessing that the object is in a partially constructed state at this point, and this is invoked prior to the addition of the clearer routine. Note that I can take the same code and change it a little bit, and make it part of a class (i.e. use Moose). I'm required to first find the attribute and then invoke this same code (i.e. $self = $instance->meta->get_attribute ('my-attribute')). Is there a way to hook the gettor from within a Role? This seems more natural since the code applies solely to the attribute (as opposed to putting it on the Class and passing in the attribute name). Thanks, Roger
Re: topic/attribute-controls-storage
On Monday 09 November 2009 23:54:07 Stevan Little wrote: > On Nov 9, 2009, at 5:42 PM, Jesse Luehrs wrote: > > On Mon, Nov 09, 2009 at 05:26:17PM -0500, Stevan Little wrote: > >> Can you detail more what this change is and why? > >> > >> Best I can tell from the other thread and the github commit views was > >> that it just adds a bunch of methods to Moose::Meta::Attribute that > >> delegate to the instance inliners. I am not sure what this buys you > >> beyond less code to write and hiding the instance protocol. > >> > >> My concern is that the instance meta-object is actually associated > >> with > >> the meta-class, not with the attribute meta-object so by pushing > >> this in > >> here it will make things more confusing. > > > > The reasoning I had for thinking it was reasonable is that the > > meta-attribute already has get_value, set_value, etc, so I don't see > > why > > it shouldn't also control the inline versions of those. It would > > probably make things simpler for attributes that wanted to use a > > different slot configuration than we have by default. > > Okay, that does make some sense, but I am concerned that people will > attempt to override them at that point (in a subclass of Attribute) > instead of in the instance metaclass where it belongs. If we can > prevent that from happening I would feel better. I tried to document better when to override the inline_* methods in the attribute meta class, and when to create a new instance meta class. This is quite difficult for me though because of the foreign language. You can see the changes I made at http://github.com/xabbu42/class- mop/commit/4e83c39b1c55fe98562719e3580da0ce9f244e42 I copied some bits of doy's reply in this thread, as his description matches most closely how I interpreted the intention of the existing code. An example overriding the attribute inline_* methods would probably be helpful. This would at the same time extend the tests. I'll try to write something, but no promises... - xabbu42 P.S The second paragraph in the documentation of Class::MOP::Instance talking about encapsulation and using wrappers in Class::MOP::Class and Class::MOP::Attribute imho applies to inline_get_slot_value just as well as to get_slot_value. P.P.S Is there not the same potential for confusion with the get_raw_value and company methods of the attribute meta class? > > - Stevan >
Re: Some newb questions
On Tue, Nov 10, 2009 at 05:09:59PM -0600, Markx.Allen wrote: > I've read through almost all of the Moose docs I could find on CPAN and > several of the Moose slide decks floating around the Interwebs. > > That being said I have some basic questions that probably have simple > answers. Maybe I didn't read the fine manuals closely enough, so if I did > overlook something, please point me toward the right spot in the docs and > I'll figure it out. > > That being said here are my questions: > > 1) In the "old style" OO (which I have used for a long time now) I would do > something like this: > > I would make a file called "Account.pm" and try to encapsulate all of the > functionality of accounts in that one file. > > I would make another file called Server.pm and encapsulate functionality > around servers in the other file. Then if Account and Server have a > relationship, I would do this: > > package Account; > > use Server; > > I found that if I do this: > > package Server; # (in a file called Server.pm) > use Moose; > > has 'name' => { > is => 'ro', > isa => 'Str', > }; > > # etc > > package Account; # (in a file called Account.pm) > use Moose; > use Server; > > has 'server' => { > is => 'ro', > isa => 'Server', > }; > > perl -wc Account.pm throws an error > > but if I change "use" to "require Server;" I do not get an error. > > Why doesn't use work? The example you gave here doesn't error, so I don't know what the exact problem is, but putting "use" lines for other classes in the middle of your class definition is bad practice in general. What happens is that the Server class here is being created while the Account class is only half-built (since use statements happen at compile time, and use Moose initializes a new empty class). A lot of times the use statement isn't even necessary (for instance, if it's just used for a type constraint like in this example), but if it is, require is probably a better option (possibly putting it closer to where it's actually used, so something like "require Server; my $server = Server->new;"). Alternatively, you can load all of your classes from some top-level module. > 2) Maybe I haven't really grokked the Moose Way yet, but what's the best way > to implement a collection of Moose Objects? > > In the old style OO, I would implement an object as, say, Account.pm and then > I would create a collection class of that object type called XXXCollection or > AccountCollection.pm in this case. I usually stored individual objects as a > hash of hashes, using the object name as the key. > > I suspect that Roles and Traits can help me write a Collection class > trivially where I will get a lot of functionality "for free" but I'm just not > quite sure I understand the docs well enough to put it all together. A trivial container class would just have an attribute like has foos => ( is => 'ro', isa => 'ArrayRef[Foo]', ); and then you can write whatever methods you want to manipulate that. The native attribute traits can simplify the generation of a lot of these methods (Moose::Meta::Attribute::Native has the docs for this), and you can also abstract a lot of the collection manipulation out into roles, so you can use it on many different types of objects (see MooseX::Role::Matcher for an example). -doy
Re: Some newb questions
On Tue, Nov 10, 2009 at 6:09 PM, Markx.Allen wrote: > I've read through almost all of the Moose docs I could find on CPAN and > several of the Moose slide decks floating around the Interwebs. > > That being said I have some basic questions that probably have simple > answers. Maybe I didn't read the fine manuals closely enough, so if I did > overlook something, please point me toward the right spot in the docs and > I'll figure it out. > > That being said here are my questions: > > 1) In the "old style" OO (which I have used for a long time now) I would do > something like this: > > I would make a file called "Account.pm" and try to encapsulate all of the > functionality of accounts in that one file. > > I would make another file called Server.pm and encapsulate functionality > around servers in the other file. Then if Account and Server have a > relationship, I would do this: > > package Account; > > use Server; > > I found that if I do this: > > package Server; # (in a file called Server.pm) > use Moose; > > has 'name' => { > is => 'ro', > isa => 'Str', > }; > > # etc > > package Account; # (in a file called Account.pm) > use Moose; > use Server; > > has 'server' => { > is => 'ro', > isa => 'Server', > }; > > perl -wc Account.pm throws an error > > but if I change "use" to "require Server;" I do not get an error. > > Why doesn't use work? That does work, there are several examples on CPAN of where it *has* to work since that is shipped code. You'll need to provide more details about the error you're getting for help figuring out what is going on. > 2) Maybe I haven't really grokked the Moose Way yet, but what's the best way > to implement a collection of Moose Objects? > > In the old style OO, I would implement an object as, say, Account.pm and then > I would create a collection class of that object type called XXXCollection or > AccountCollection.pm in this case. I usually stored individual objects as a > hash of hashes, using the object name as the key. That would still work. The simplest possible solution is an ArrayRef or HashRef package Server; use Moose; has accounts => ( isa => 'ArraRef[Account]', is => 'ro', ...); # # then later Server->new( accounts => [Account->new( ...) ] ); The packages in the rather new Native Attributes code can help here. If you have a more specific question someone can help sort it out, but you're asking a very generic question so I can only provide a very generic answer. > I suspect that Roles and Traits can help me write a Collection class > trivially where I will get a lot of functionality "for free" but I'm just not > quite sure I understand the docs well enough to put it all together. Roles can provide a collection API if you want to go that route. A quick example would be something like: package MyApp::Collection::API; use Moose::Role; has accounts => ( isa => 'ArraRef[Object]', is => 'ro', traits => ['Array'] handles => { add_element => 'push', remove_element => 'pop' } ); package AccountCollection; use Moose; use MooseX::Aliases; with qw(MyApp::Collection::API); alias add_account => 'add_element'; alias rmove_account => 'remove_element'; But again It'd help to have specific questions if you want more specific answers :)
Some newb questions
I've read through almost all of the Moose docs I could find on CPAN and several of the Moose slide decks floating around the Interwebs. That being said I have some basic questions that probably have simple answers. Maybe I didn't read the fine manuals closely enough, so if I did overlook something, please point me toward the right spot in the docs and I'll figure it out. That being said here are my questions: 1) In the "old style" OO (which I have used for a long time now) I would do something like this: I would make a file called "Account.pm" and try to encapsulate all of the functionality of accounts in that one file. I would make another file called Server.pm and encapsulate functionality around servers in the other file. Then if Account and Server have a relationship, I would do this: package Account; use Server; I found that if I do this: package Server; # (in a file called Server.pm) use Moose; has 'name' => { is => 'ro', isa => 'Str', }; # etc package Account; # (in a file called Account.pm) use Moose; use Server; has 'server' => { is => 'ro', isa => 'Server', }; perl -wc Account.pm throws an error but if I change "use" to "require Server;" I do not get an error. Why doesn't use work? 2) Maybe I haven't really grokked the Moose Way yet, but what's the best way to implement a collection of Moose Objects? In the old style OO, I would implement an object as, say, Account.pm and then I would create a collection class of that object type called XXXCollection or AccountCollection.pm in this case. I usually stored individual objects as a hash of hashes, using the object name as the key. I suspect that Roles and Traits can help me write a Collection class trivially where I will get a lot of functionality "for free" but I'm just not quite sure I understand the docs well enough to put it all together. Thanks for any help/pointers. --Mark Mark Allen
Re: Time for a rewrite
Oh, and maybe a way to have a type-constraint fire once without testing for failure as was the behavoir pre .76. That broke probably 50% of my MooseX::Types code. I still regress my Mooses to remove this behavoir. Sure, it might make more sense, but testing to see if you have spaces on a string just to trim them can sure add needless overhead and annoyance when you just want a mechanism to fire and clean the input. http://github.com/EvanCarroll/moose/commit/feeec6cb945e079296611d188b0172fd4f1c0e76 -- Evan Carroll System Lord of the Internets http://www.evancarroll.com
Re: Time for a rewrite
On Tue, Nov 10, 2009 at 03:37:54PM -0600, Jesse Luehrs wrote: > 1) Attributes in roles aren't real objects, and there is no attribute > metaclass for roles. This makes it impossible to automatically apply > attribute traits through ::MetaRole. > > 2) Metaclass compatibility code in Moose/CMOP is pretty broken. It > happens to work reasonably in most cases because it can generally figure > things out if there are class or instance (??) meta-traits applied, but > this isn't always the case. I have a branch in cmop for better metaclass > incompatibility detection, but it breaks all kinds of things in Moose, > since Moose doesn't currently fix metaclasses entirely correctly. I > started trying to fix it in Moose as well, but got a bit overwhelmed. > (An example of the failure: when a superclass has a class metaclass > trait applied, and something subclasses it and tries to apply an > attribute metaclass trait without also applying a class metaclass > trait, Moose decides that it doesn't know how to resolve that). > > 3) Going into more detail on the OMG EVIL CODE GENERATION topic, > ::Constructor is reasonably separated out so that parts of it are easy > to override, but ::Destructor is not, which is keeping me from adding > actual support for that to MooseX::NonMoose. Oh, and: 4) lazy_build is nice, but it really doesn't feel like it fits in normal Moose - it should probably be a special case of some more general feature (similar in concept to MooseX::Attributes::Curried, except triggered by an option rather than a separate attribute declarer sub). Also, the clearer and predicate it defines should probably default to private. -doy
Re: Time for a rewrite
I've always liked the interaction between lazy and clear, clear resets the slot. That makes sense. The rest of it is all great. Trigger and Initializer were so confusing for me personally that I had to write specific notes about them. http://en.wikibooks.org/wiki/Programming_with_Moose/Syntax/has#Clearing_up_confusion Also, for when lazy is not enough, sometimes I want control over the initialization order within the same module. I was playing around with role yesterday and iirc correctly I found something surprising, I couldn't do. I thought that `with` specified the order so a role could define an attribute in a role, that a class could override with '+', that didn't work. (If I recall correctly). Roles also have open bugs that breaks the interface part, (requires() isn't working in the newest moose at all, forcing you to remove the pragma because it always throws an exception on moose-attributes). -- Evan Carroll System Lord of the Internets http://www.evancarroll.com
Re: Time for a rewrite
1) Attributes in roles aren't real objects, and there is no attribute metaclass for roles. This makes it impossible to automatically apply attribute traits through ::MetaRole. 2) Metaclass compatibility code in Moose/CMOP is pretty broken. It happens to work reasonably in most cases because it can generally figure things out if there are class or instance (??) meta-traits applied, but this isn't always the case. I have a branch in cmop for better metaclass incompatibility detection, but it breaks all kinds of things in Moose, since Moose doesn't currently fix metaclasses entirely correctly. I started trying to fix it in Moose as well, but got a bit overwhelmed. (An example of the failure: when a superclass has a class metaclass trait applied, and something subclasses it and tries to apply an attribute metaclass trait without also applying a class metaclass trait, Moose decides that it doesn't know how to resolve that). 3) Going into more detail on the OMG EVIL CODE GENERATION topic, ::Constructor is reasonably separated out so that parts of it are easy to override, but ::Destructor is not, which is keeping me from adding actual support for that to MooseX::NonMoose. -doy
Re: Time for a rewrite
2009/11/10 Yuval Kogman 1. few people understand when 'trigger' is fired, it could use a rethink > (either make it *every* time something is set, or make 3 separate triggers, > for accessors, constructors, and default values). If we find a way > i guess i saw a balloon or something. if we find a way to make it clear when we invoke specific callbacks and what they can do, perhaps we can consolidate this with initializers, or maybe even the type checks/coercions
Re: Time for a rewrite
To clarify, this is not an RFC on how to improve Moose. We are already doing that, it's called MooseX and it's doing the job just fine. As always, after a feature is fleshed out a feature as a MooseX module we decide on whether or not to include it. If you want to propose a feature, do it that way =) I'm talking about the other half of progress, letting go of past mistakes in a way that allows us to do the first part more easily and more dramatically, without hurting existing code in the ecosystem. The points that John raised are all either existing MX or potential MX that are simply too much trouble to code right not because they propose doing something radically different, but that's no excuse for not trying them out as extensions beforehand. The problem is simply that right now extensions need work along side lots of baggage, and that's difficult.
Re: Time for a rewrite
So anyway, here is my personal shitlist, all of these are backwards incompatible changes: 1. few people understand when 'trigger' is fired, it could use a rethink (either make it *every* time something is set, or make 3 separate triggers, for accessors, constructors, and default values). If we find a way 2. there are features which grew organically which are very complicated to explain, for instance the interaction between default/builder and lazy attributes. i think there's room to change the semantics in subtle backwards incompatible ways if it can help to simplify the implementation and documentation. 3. the type constraint registry can be deprecated as far as I'm concerned, all we need is string support for class_type and role_type IMHO, so that the only valid values for TC parameters are those and type handles (either MX::Types decorators or typeconstraint objects, but that too can be simplified) 4. i'd like to see roles split up into pure roles and things that are more like mixins. Things like 'around' modifiers and even attribute declaration are currently order sensitive. This can be very useful, for plugins, for runtime role application, and other things that alter a class, but I think that the other use case, namely a safer alternative to MI for code reuse suffers a bit. Having two separate concepts with similar composition rules, but where one is strict and simple (and therefore limited), and the other is more perlish (enough rope and all that) would resolve this tension. 5. THE CODE GEN I HATES IT OMG OMG I HATES IT SO MUCH. Unfortunately I've tried time and time again to rewrite it and failed. It's just too painful. Figuring out which features make it unreasonably hard to redo the codegen (i've since forgotten most of the annoying details) might prompt some small changes which would help this. If we don't strictly need the backwards compatibility then we could have a much easier time finally delivering on Moose::XS, MooseX::{Antlers,Compile}, and all of that shiny. Furthermore, some MooseX modules rely on the current implementation details and would break badly if they changed. Using an explicit versioning model we could code freeze the current code, and do a ground up rewrite that just needs to pass the test. Opting in to backwards incompatibility gives us lots of freedom in that regard. 7. lazy/clearer 8. Moose is big. I'm not talking about the dist size, I'm talking about the number of components. If we can simplify the structure of things you get when you use Moose 2.0 then that would be easier to hack and debug. 9. Lastly, the splitups needed to pull this off would let us implement core features more like we implement MooseX modules, whose development model has IMHO lead to much of Moose's success. Being able to have a quick turnaround, good composability and the ability to back out of failures is something that MooseX modules enjoy but Moose core does not as much.
Re: Time for a rewrite
Let's please focus on things that are wrong and need fixing first, not things that we might like to have in some fancy future moose.
Re: Time for a rewrite
> 3) Type constraints decoupled from Moose and some things changed under the > hood to make it easier to improve error messages for things like > MX:T:Structured and other changes so that slurpy in MX:T:Structured doesn't > have to be an evil hack. A stand alone type constraint system that makes it > easier for me to complete the dependent and faceted type stuff sitting inthe > repo for like a year would be nice. And of course a sweet MooseX::Declare > style syntax. Then we can end the stringy types versus MooseX::Types style > types issue for good. This would be godly. Moose::Util::TypeConstraints are confusing as hell for new people and increase the overall learning curve for Moose (because you so often find yourself needing MX::Types). String types should pull from core, and core types should be purposefully brittle so as to stop the use of string types. I'd also like to see a real mechanism to subtype coercions. Not being able to stack on coercions that limit size, remove padding, and unoverload a type at will really make for a bock of functionality that I find myself constantly implementing. I want some sort anonymous type coercion that I can expand TypeGenerator[ base => Str, trim => 'Both', unoverload => 1, lowercase => 1 ] -- Evan Carroll System Lord of the Internets http://www.evancarroll.com
Re: Time for a rewrite
Hi, Since I'm speaking generally, rather than responding, I hope top posting is considered acceptable. Things I'd like to see done differently (not exactly things I don't like, but just irritate me) 1) See Moose more as a way to hook up a bunch of suger, like MX:Declare, advanced types and exceptions, possible event multi methods and the stuff in Moose::Autobox. 2) So more deep stuff moved to MOP, like Roles (not sure if this is possible, but you said anything :) ) 3) Type constraints decoupled from Moose and some things changed under the hood to make it easier to improve error messages for things like MX:T:Structured and other changes so that slurpy in MX:T:Structured doesn't have to be an evil hack. A stand alone type constraint system that makes it easier for me to complete the dependent and faceted type stuff sitting inthe repo for like a year would be nice. And of course a sweet MooseX::Declare style syntax. Then we can end the stringy types versus MooseX::Types style types issue for good. 4) Lispy conditionals (I know it's been said before, but I think we are closer now to making this workable) - Original Message > From: Yuval Kogman > To: moose@perl.org > Sent: Tue, November 10, 2009 2:49:44 PM > Subject: Time for a rewrite > > Just kidding ;-) > > But there is some truth: > > 1. MooseX::Declare has gained us a lot of insight on what we can do > substantially better > 2. We learned how to structure extensibility with traits > 3. we got a bunch of stuff wrong (ranging from slightly annoying to "oops, > sorry") > 4. we have probably gotten things wrong that we don't know about > > Fortunately the MOP api is designed to be extensible, the only unanswered > question is what defaults to we ship. > > I think it's time to start thinking about how to get Perl's 'use 5.010' for > Moose, in one form or another. > > Fortunately, Perl kinda sorts this out for us: > > use Moose 0.92 qw(foo bar); > > This will: > > 1. require Moose > 2. call Moose->VERSION(0.92) from the POV of the caller > 3. call Moose->import(qw(foo bar)) > > In terms of infrastructure I think that doesn't take much else, > Moose::VERSION will simply init_meta slightly differently, and the import > routine can act accordingly (it is already capable of detecting > pre-initialized metaclasses). > > I think if we structure Moose's features in a more plugin like architecture, > where the metaclass instance can augment or replace the Moose::Exporter > stuff to some extent, it should be trivial to make a future and backwards > compatible way to keep things clean. > > Using a metaclass centered approach we can just let polymorphism ensure we > have all our opportunities open. > > So anyway, I'll reply to myself with what I dislike about Moose in its > current form, and I hope you do the same
Time for a rewrite
Just kidding ;-) But there is some truth: 1. MooseX::Declare has gained us a lot of insight on what we can do substantially better 2. We learned how to structure extensibility with traits 3. we got a bunch of stuff wrong (ranging from slightly annoying to "oops, sorry") 4. we have probably gotten things wrong that we don't know about Fortunately the MOP api is designed to be extensible, the only unanswered question is what defaults to we ship. I think it's time to start thinking about how to get Perl's 'use 5.010' for Moose, in one form or another. Fortunately, Perl kinda sorts this out for us: use Moose 0.92 qw(foo bar); This will: 1. require Moose 2. call Moose->VERSION(0.92) from the POV of the caller 3. call Moose->import(qw(foo bar)) In terms of infrastructure I think that doesn't take much else, Moose::VERSION will simply init_meta slightly differently, and the import routine can act accordingly (it is already capable of detecting pre-initialized metaclasses). I think if we structure Moose's features in a more plugin like architecture, where the metaclass instance can augment or replace the Moose::Exporter stuff to some extent, it should be trivial to make a future and backwards compatible way to keep things clean. Using a metaclass centered approach we can just let polymorphism ensure we have all our opportunities open. So anyway, I'll reply to myself with what I dislike about Moose in its current form, and I hope you do the same
topic/attribute-controls-storage
Hello The code/branch I talk about lives at http://github.com/xabbu42/class-mop/tree/topic/attribute-controls-storage and http://github.com/xabbu42/moose/tree/topic/attribute-controls-storage As discussed in the thread MooseX::Attribute::Delegated I created a topic branch topic/attribute-controls-storage which makes it easier to overwrite the way a single attribute is stored in the object. The smolder script gives me no new failures (MooseX::Daemonize and MooseX::Types::Structured currently fail for me with 0.92 and my branch) and one new warning, which is related to other changes since 0.92 (deprecation of get_attribute_map). Any help/tips for extending on documentation and tests is very appreciated. Comments? Greetings xabbu42