Bug with Moose::Role attributes

2009-07-15 Thread Ovid

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

2009-07-15 Thread Jesse Luehrs
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

2009-07-15 Thread Stevan Little

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

2009-05-27 Thread Dmitri Ostapenko
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

2009-05-27 Thread Chris Prather
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

2009-05-27 Thread Dimitri Ostapenko

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

2009-05-27 Thread Hans Dieter Pearcey
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

2009-05-27 Thread Dimitri Ostapenko

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

2009-05-27 Thread Hans Dieter Pearcey
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

2009-05-27 Thread Stevan Little


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