Bug with Moose::Role attributes
Hi all, On the Perl 6 language list, Raphael Descamps posted a link to a paper explaining how to implement stateful traits (http://scg.unibe.ch/archive/papers/Berg07eStatefulTraits.pdf). One of the authors of that paper worked on the original traits paper and the research appears solid. Basically, it argues that traits need to have private accessors. I won't go into full detail (you'll have to read the paper), but I decided to see exactly how Moose handles this. I *think* this is a bug in Moose 0.87, but I'm unsure. Consider the following code. Now that the role provides an attribute named counter and the class using the role provides a method of the same name. The methods are not semantically equivalent, so I had two expectations, possibly due to me misunderstanding Moose: 1. The class method would silently override the role attribute. 2. This code would break horribly. #!/usr/bin/env perl -l { package MyRole; use Moose::Role; has counter = ( is = 'rw', default = 0 ); sub inc { my $self = shift; $self-counter( $self-counter + 1 ); return $self-counter; } } { package Foo; use Moose; with 'MyRole'; sub counter { 'this is the counter' } } my $foo = Foo-new; print $foo-inc for 1 .. 5; print $foo-counter; However, my expectations were wrong. Here's the output when run: 1 2 3 4 5 5 In other words, the class's counter() method was silently discarded, denying the class the ability to control its behavior. I would suggest a couple of things. 1. A class's attributes should silently override a role's attributes (I hate this, but it's in keeping with what Moose does with class/role methods). 2. A class's methods MUST fail when trying to override a role's attributes, thus forcing deliberate exclusion. This is because methods and attributes serve such radically different purposes that a failure here is warranted. 3. A class's attributes MUST fail when trying to override a role's methods (see above). I expect that people will have different views, but that's OK. Have fun with that. I would also love to see private attributes available for Moose roles. Right now, that attribute in the role unnecessarily clutters the interface of the class and there's no need for that. Plus, the class shouldn't have to provide an attribute which only that role uses because that violates the role's encapsulation. There's more I can say, but dinner's beckoning. I highly recommend the paper linked to above. Cheers, Ovid -- Buy the book - http://www.oreilly.com/catalog/perlhks/ Tech blog- http://use.perl.org/~Ovid/journal/ Twitter - http://twitter.com/OvidPerl Official Perl 6 Wiki - http://www.perlfoundation.org/perl6
Re: Bug with Moose::Role attributes
On Wed, Jul 15, 2009 at 11:24:11AM -0700, Ovid wrote: Consider the following code. Now that the role provides an attribute named counter and the class using the role provides a method of the same name. The methods are not semantically equivalent, so I had two expectations, possibly due to me misunderstanding Moose: 1. The class method would silently override the role attribute. 2. This code would break horribly. #!/usr/bin/env perl -l { package MyRole; use Moose::Role; has counter = ( is = 'rw', default = 0 ); sub inc { my $self = shift; $self-counter( $self-counter + 1 ); return $self-counter; } } { package Foo; use Moose; with 'MyRole'; sub counter { 'this is the counter' } } my $foo = Foo-new; print $foo-inc for 1 .. 5; print $foo-counter; However, my expectations were wrong. Here's the output when run: 1 2 3 4 5 5 In other words, the class's counter() method was silently discarded, denying the class the ability to control its behavior. We actually just discussed this in #moose yesterday, it's mostly an artifact of the role implementation not really being quite complete - attributes in roles don't *really* exist until they're added to a class, and at that point have no concept that they actually came from a role. This is hopefully something that will be addressed when the attributes in roles system is redone (I think Yuval has a plan for this?) Jesse
Re: Bug with Moose::Role attributes
Hey Ovid, On Jul 15, 2009, at 2:24 PM, Ovid wrote: On the Perl 6 language list, Raphael Descamps posted a link to a paper explaining how to implement stateful traits (http://scg.unibe.ch/archive/papers/Berg07eStatefulTraits.pdf ). One of the authors of that paper worked on the original traits paper and the research appears solid. I was actually approached by the authors of this paper before it was published but after they had finished the implementation. They were wondering how we handled attribute conflicts in Perl 6/Moose. In Moose we will die on an attribute conflict, this is basically punting because the this problem is very hard. I explained to them though that Moose attributes were significantly more complex then Smalltalk attributes so our more extreme solution was not really applicable to them. Moose attributes are truly first class citizens of the MOP layer while Smalltalk simply has a list of slots in the instance and accessor methods that are (IIRC) totally unrelated to them on any formal level. See the problem is that we (Moose) don't just have reader/writer methods to worry about, but we have reader/writer or accessor, predicate methods, clearer methods, associated builder and lazy_build methods and then the possibility of delegation methods with handles. The result is that we have a *LOT* of things to check, and we don't want to just test this once on creation of the attribute, but we actually need to compare the attribute to *every other attribute* the class (and it's superclasses) have. This is the only way to be sure that we are not creating a conflict with a method that is somehow tied to an attribute and therefore to state. Basically, it argues that traits need to have private accessors. Yeah, this is something I remember discussing with chromatic, Luke Palmer and Audrey at YAPC::NA in Toronto. We kept going around and around on this never coming to a totally satisfying solution. It basically always felt (to me anyway) that it was hiding the problem instead of actually dealing with it. Although I have not read the paper yet, so perhaps when i see their reasoning I might feel different. Also note that (at least some) of the authors or this paper expressed to me that they were not happy with this outcome. That having privacy in Smalltalk felt very un-Smalltalk-ish and so the solution kind of felt dirty. I feel the same way about this in regards to Perl, the idea of requiring privacy seems very un-Perlish. I won't go into full detail (you'll have to read the paper), but I decided to see exactly how Moose handles this. I *think* this is a bug in Moose 0.87, but I'm unsure. Nope, this is a bug in Moose 0.01 (or whatever version it was we introduced roles in). snip I would suggest a couple of things. 1. A class's attributes should silently override a role's attributes (I hate this, but it's in keeping with what Moose does with class/role methods). The problem with this is that many of the roles methods will likely expect the attribute (and it's associated methods) to be there, so silently overriding a role attribute is pretty much totally out of the question. While kinda annoying we do the only sensible thing here, which is to die. 2. A class's methods MUST fail when trying to override a role's attributes, thus forcing deliberate exclusion. This is because methods and attributes serve such radically different purposes that a failure here is warranted. Yes, I will agree here, but as of right now role attributes are not first class citizens so this is not possible. There was a lot of talk at this years YAPC::NA hackathon about actually making them first class, so rest assured it is being addressed. 3. A class's attributes MUST fail when trying to override a role's methods (see above). Same as #2, I agree with you, but until we have first class role attributes and can calculate the methods they will create at composition time, we won't be able to fix this. I expect that people will have different views, but that's OK. Have fun with that. Well not me, except for the first item, which I suspect if you read my point there you will agree with me on. I would also love to see private attributes available for Moose roles. Yuk! I would prefer to solve the problem without them if possible. Privacy in perl is hackish at best since you cannot really properly hide a method from the dispatcher (just die-ing when called from an improper context is *NOT* correct privacy, true privacy means that the method is invisible to the dispatcher and not present in the MRO). Right now, that attribute in the role unnecessarily clutters the interface of the class and there's no need for that. Unless of course that is the purpose of the role, to include an attribute. Sure I can see occasions when you do want to hide data, but it is not always the case and I
Role attributes
Is there a way to tell which attributes come from role? I can't think of a good way of doing this besides from changing attributes names in some way. Using prefix for example.. tx,
Re: Role attributes
On Wed, May 27, 2009 at 4:24 PM, Dmitri Ostapenko dmi...@farematrix.com wrote: Is there a way to tell which attributes come from role? I can't think of a good way of doing this besides from changing attributes names in some way. Using prefix for example.. tx, What are you trying to achieve? -Chris
Re: Role attributes
Chris Prather wrote: On Wed, May 27, 2009 at 4:24 PM, Dmitri Ostapenko dmi...@farematrix.com wrote: Is there a way to tell which attributes come from role? I can't think of a good way of doing this besides from changing attributes names in some way. Using prefix for example.. tx, What are you trying to achieve? -Chris !DSPAM:2337,4a1da1e770674824671560! I'm writing interface module for DB table (Pg) that has close relationship with another table, but does not inherit from it as each row in main table can have multiple rows in base table. Class I'm writing needs to interact with both tables. In case of pure postgres base tables, classes map to tables beautifully. Base table has corresponding role and child table has corresponding class. This clean mapping breaks if underlying tables don't have inheritance. So using a role to model lower-level table I need to be able to tell which attributes come from class and which come from role in methods for saving and retrieving data. Maybe base class would be better suited for something like this?
Re: Role attributes
On Wed, May 27, 2009 at 04:40:37PM -0400, Dimitri Ostapenko wrote: So using a role to model lower-level table I need to be able to tell which attributes come from class and which come from role in methods for saving and retrieving data. Maybe you want a trait for your attributes that lets you store a table per attribute definition, or something? hdp.
Re: Role attributes
Hans Dieter Pearcey wrote: On Wed, May 27, 2009 at 04:40:37PM -0400, Dimitri Ostapenko wrote: So using a role to model lower-level table I need to be able to tell which attributes come from class and which come from role in methods for saving and retrieving data. Maybe you want a trait for your attributes that lets you store a table per attribute definition, or something? hdp. !DSPAM:2337,4a1da61670674007936253! Let's say we have table 'family' and table 'family_member'. Family has attributes: - size - family name - kind family_member has: - sex - first_name - age For a family of 3 there will be 3 rows in 'family_member' for 1 row in 'family'. package FamilyMember; use Moose::Role; use MY::Types; has 'id' = (is = 'rw', isa = 'PosInt' ); # serial has 'sex' = (is = 'ro', isa = 'SexType',required=1); has 'first_name' = (is = 'ro', isa = 'Str',required=1 ); has 'age' = (is = 'ro', isa = 'PosInt', default= 0 ); has 'family_id' = (is = 'ro', isa = 'PosInt', required=1 ); # foreign key package Family; use Moose; use MY::Types; with qw(MY::Base MY::FamilyMember ); has 'id' = (is = 'rw', isa = 'PosInt' ); has 'size' = (is = 'ro', isa = 'PosInt', required=1); has 'family_name' = (is = 'ro', isa = 'Str', required=1 );# Unique within a table has 'kind' = (is = 'ro', isa = 'FamilyKind', default= 'Traditional' ); sub _table { 'family'}; sub _base_table { 'family_member'}; # Return 3 rows from base table, table sub get { my (%arg) = @_; my $id = $arg{id}; my $family_name; my ($rows,$where); croak 'id' or 'family_name' is required unless ($id || $family_name); if ( $id ) { $where = id='$id'; } else { $where = family_name='$family_name'; } my $qry = 'SELECT * FROM '. _table().' a,'. _base_table().' b WHERE a.id=b.family_id AND '.$where; my $rows = MY::DB::select (sql = $qry, returns = 'hash'); return $rows } sub save { # Need to know here which attr comes from where to be able to save into correct table }
Re: Role attributes
On Wed, May 27, 2009 at 05:16:28PM -0400, Dimitri Ostapenko wrote: # Need to know here which attr comes from where to be able to save into correct table Yes, I understand the problem. That's why I suggested a role for your attribute class so that you can say e.g. has foo = (is = 'ro', ..etc.., from_table = tablename); hdp.
Re: Role attributes
On May 27, 2009, at 4:40 PM, Dimitri Ostapenko wrote: Chris Prather wrote: On Wed, May 27, 2009 at 4:24 PM, Dmitri Ostapenko dmi...@farematrix.com wrote: Is there a way to tell which attributes come from role? I can't think of a good way of doing this besides from changing attributes names in some way. Using prefix for example.. tx, What are you trying to achieve? -Chris I'm writing interface module for DB table (Pg) that has close relationship with another table, but does not inherit from it as each row in main table can have multiple rows in base table. Class I'm writing needs to interact with both tables. In case of pure postgres base tables, classes map to tables beautifully. Base table has corresponding role and child table has corresponding class. This clean mapping breaks if underlying tables don't have inheritance. So using a role to model lower-level table I need to be able to tell which attributes come from class and which come from role in methods for saving and retrieving data. Maybe base class would be better suited for something like this? Perhaps. However using a custom meta-attribute would work too, like so (untested): package MyCustomAttributeMarker; use Moose::Role; package SomeRole; use Moose::Role; has foo = ( traits = [ 'MyCustomAttributeMarker' ], ... ); package SomeOtherRole; use Moose::Role; has bar = ( traits = [ 'MyCustomAttributeMarker' ], ... ); package SomeClass; use Moose; with 'SomeRole', 'SomeOtherRole'; has baz = ( ... ); ... my @attrs_from_roles = grep { $_-does('MyCustomAttributeMarker') } SomeClass-meta-get_all_attributes; Moose does not provide this by default because the whole purpose of roles is that they are composed into the class so you don't need to actually worry about where they came from. - Stevan