Re: [TEST CASE] Constructor provided arguments and strict-default fire ordering is confusing or buggy.

2010-10-27 Thread Zbigniew Lukasiak
On Mon, Oct 25, 2010 at 10:48 PM, Hans Dieter Pearcey  wrote:
> On Mon, 25 Oct 2010 16:23:45 -0500, Evan Carroll  wrote:
>> I'll go ahead and
>> show you what I wanted to do with this. I have a Class, it should
>> accept either a "company_id", or a hash of "attributes" if you provide
>> a company_id the attribute hash is lazy when it is needed. If you
>> provide a hash of company attributes, then the value is immediately
>> entered into the DB, and the company_id gets set appropriately:
>
> Using new() for this seems wrong to me.  Your object constructor should
> construct objects, not construct objects and sometimes insert data but other
> times retrieve it.  For example, in DBIC this is find_or_create, which is
> itself built on top of other predicates that are built on top of object
> construction; it's a complex operation, and trying to make it all an
> inextricable part of calling new is a code smell.

I found this: 
http://misko.hevery.com/code-reviewers-guide/flaw-constructor-does-real-work/
to be a very clear explanation of why this is a code smell :)

-- 
Zbigniew Lukasiak
http://brudnopis.blogspot.com/
http://perlalchemy.blogspot.com/


Re: [TEST CASE] Constructor provided arguments and strict-default fire ordering is confusing or buggy.

2010-10-25 Thread Hans Dieter Pearcey
On Mon, 25 Oct 2010 16:23:45 -0500, Evan Carroll  wrote:
> I'll go ahead and
> show you what I wanted to do with this. I have a Class, it should
> accept either a "company_id", or a hash of "attributes" if you provide
> a company_id the attribute hash is lazy when it is needed. If you
> provide a hash of company attributes, then the value is immediately
> entered into the DB, and the company_id gets set appropriately:

Using new() for this seems wrong to me.  Your object constructor should
construct objects, not construct objects and sometimes insert data but other
times retrieve it.  For example, in DBIC this is find_or_create, which is
itself built on top of other predicates that are built on top of object
construction; it's a complex operation, and trying to make it all an
inextricable part of calling new is a code smell.

If you insist on doing it anyway, I don't think you'll find any support for the
approach you've chosen, because we've repeatedly said that using 'lazy' on
everything is the right way to deal with this kind of attribute
interdependency.  Using lazy already gives you the initialization order that
you want.

In this particular case, you could mark everything as lazy and resolve it in
BUILD.  If you find that unappealing, this would also be a good use of the
hypothetical "lazy default/builder, but call it automatically after
initialization" attribute trait/option that's come up with some frequency in
the past, if you felt like writing that.

hdp.


Re: [TEST CASE] Constructor provided arguments and strict-default fire ordering is confusing or buggy.

2010-10-25 Thread Evan Carroll
> Also, I have no idea what your parenthetical means.

Parenthetical indicate that I know default fires before the
initializer for the attribute, but that not all defaults for all
attributes will fire before any of the initializers. Currently, an
Attribute process not a class process.

> "Awkward and counter-intuitive" is very subjective and therefore not a
> compelling reason to change things; for example, people regularly seem to 
> think
> that it is awkward and counter-intuitive not to override new() when coming 
> from
> vanilla Perl 5 OO.
>
> In this particular case, the way Moose does things means that code related to 
> a
> given attribute (default, etc.) only sees that particular attribute in a
> half-built state -- that is, each other attribute is either completely
> initialized or not at all.  This means there's much less opportunity for your
> object to be exposed in an inconsistent state.

I don't see the logic in the term "inconsistent." I've showed the flow
defined in a sequential list in my original post, maybe you can
clarify why you feel such a flow is inconsistent. I'll go ahead and
show you what I wanted to do with this. I have a Class, it should
accept either a "company_id", or a hash of "attributes" if you provide
a company_id the attribute hash is lazy when it is needed. If you
provide a hash of company attributes, then the value is immediately
entered into the DB, and the company_id gets set appropriately:

has 'attr' => (
  isa   => 'HashRef'
  , is  => 'ro'
  , lazy=> 1
  , predicate => '_has_attr'
  , default => sub {
my $self = shift;
die "No company id" unless $self->_has_company;
# Get from db.
  }
);

has 'company' => (
  isa  => 'Int'
  , is => 'ro'
  , predicate => '_has_company'
  , default => sub {
my $self = shift;
die 'No attributes or company id' unless $self->_has_attr;
# Insert into DB
  }
)

Anyway, it just seems very naturally that this should work. You're
explicitly providing non-defaults for some attributes, but they're
order is undefined until other attribute's get their defaults -- it
seems like the current method provides no gain whatsoever.

-- 
Evan Carroll - m...@evancarroll.com
System Lord of the Internets
web: http://www.evancarroll.com
ph: 281.901.0011


Re: [TEST CASE] Constructor provided arguments and strict-default fire ordering is confusing or buggy.

2010-10-25 Thread Hans Dieter Pearcey
On Mon, 25 Oct 2010 15:28:26 -0500, Evan Carroll  wrote:
> I'd have assumed that the process went like this:
> * BUILDARGS if any

yes

> * Moose provided-new which sets the attributes to the values in the hashref
> * The default/initializer stage (undefined for the class, defined for
> the attribute)

* Initialize each attribute in turn, from constructor argument or
  default/builder

Also, I have no idea what your parenthetical means.

> * BUILD which gets the complete object

yes

> However, I'm now seeing this. The non-lazy defaults fire before `foo`
> get's set even though foo is explicitly provided. This seems awkward
> and counter-intuitive. You can find a test case here
> http://gist.github.com/645659 . Could anyone provide commentary on
> this behavior? Is this a bug in Moose. I'm on latest stable: 1.17.

"Awkward and counter-intuitive" is very subjective and therefore not a
compelling reason to change things; for example, people regularly seem to think
that it is awkward and counter-intuitive not to override new() when coming from
vanilla Perl 5 OO.

In this particular case, the way Moose does things means that code related to a
given attribute (default, etc.) only sees that particular attribute in a
half-built state -- that is, each other attribute is either completely
initialized or not at all.  This means there's much less opportunity for your
object to be exposed in an inconsistent state.

hdp.


Re: [TEST CASE] Constructor provided arguments and strict-default fire ordering is confusing or buggy.

2010-10-25 Thread Evan Carroll
> This is not a bug. Attribute initialization order is explicitly
> undefined (the documentation mentions this in several places). If you
> need a defined initialization order, you should make the appropriate
> attributes lazy.

That's not how I read that statement. When you say attribute
initialization order is undefined I read that literally. Example,

has "foo" => ( isa => "Int", is => "ro", default => 1 );
has "bar" => ( isa => "Int", is => "ro", default => 2 );

Is undefined such that "foo", and "bar" will be set in an undefined
order. Not, that the constructor provided arguments and
strict-defaults are undefined. I don't even read "initialization" as
being an apt choice of words for setting "foo", via

Class->new({ foo => bar })

I'm not sure if this misunderstanding is unique to myself or not, but
I didn't take your statement (or the docs) to be at odds with my own
mental model of Moose behavior.

-- 
Evan Carroll - m...@evancarroll.com
System Lord of the Internets
web: http://www.evancarroll.com
ph: 281.901.0011


Re: [TEST CASE] Constructor provided arguments and strict-default fire ordering is confusing or buggy.

2010-10-25 Thread Jesse Luehrs
On Mon, Oct 25, 2010 at 03:28:26PM -0500, Evan Carroll wrote:
> I've recently discovered that rather against my own intuition Moose
> does not set constructor provided arguments first. I've always thought
> that Moose did the logical thing here.. If I was to read the code
> here:
> 
> Class->new({ foo => bar })->baz;
> 
> I'd have assumed that the process went like this:
> * BUILDARGS if any
> * Moose provided-new which sets the attributes to the values in the hashref
> * The default/initializer stage (undefined for the class, defined for
> the attribute)
> * BUILD which gets the complete object
> 
> However, I'm now seeing this. The non-lazy defaults fire before `foo`
> get's set even though foo is explicitly provided. This seems awkward
> and counter-intuitive. You can find a test case here
> http://gist.github.com/645659 . Could anyone provide commentary on
> this behavior? Is this a bug in Moose. I'm on latest stable: 1.17.

This is not a bug. Attribute initialization order is explicitly
undefined (the documentation mentions this in several places). If you
need a defined initialization order, you should make the appropriate
attributes lazy.

-doy