On Mon, Oct 8, 2012 at 5:32 AM, Dave Howorth <dhowo...@mrc-lmb.cam.ac.uk> wrote:
> Chris Prather wrote:
>> On Fri, Oct 5, 2012 at 10:54 AM, Dave Howorth
>> <dhowo...@mrc-lmb.cam.ac.uk> wrote:
>>> Jeff Hallock wrote:
>>>> Hi Dave,
>>>>
>>>> 'after' is called after a method call.
>>>>
>>>> If you want a block of code to be called every time you set an attribute, 
>>>> you want to use a trigger:
>>>>
>>>> has 'foo' => (
>>>>       is => 'rw',
>>>>       trigger => sub {
>>>>               # will be called when setting the attribute via 'new' or via 
>>>> the writer method
>>>>               my ( $self, $newval, $oldval ) = @_;
>>>>               ...
>>>>       }
>>>> );
>>> That's great. My code works now. Thanks Jeff.
>>>
>>> It does seem counter-intuitive. I'd expect
>>>
>>>   $o = Class->new(attribute => 'value');
>>>
>>> to have the same effect as
>>>
>>>   $o = Class->new(); $o->attribute('value');
>>>
>>> Anyway, at least it works and hopefully I'll remember the exception for
>>> next time.
>>
>> I actually wouldn't, at least not anymore.
>
> I'm afraid I don't understand what it is you wouldn't, given that it
> seems you would have before but won't now?

After I wrote the first response I realized I had never actually
written a class to work the way you expected, though I still do
understand why you would expect that. Even in the pre-moose days
though I'd write constructors like so:

    sub new {
        my $self = shift;
        my $args = @_ > 1 ? { @_ } : $_[0];
        my $class = ref $self || $self;
        return bless $args, $class;
    }

Which doesn't actually behave the way you expect either.

>> Behavior and state while related are distinct. The call to new() is
>> passing in the new state for the object that is about to be created, I
>> wouldn't expect any extra behavior to fire there, and if it did I
>> would want it to happen explicitly (say via $self->attribute('value')
>> in BUILD method ... or via a trigger).
>
> The call to new is passing in a new value for an attribute.
> The call to the mutator is passing in a new value for an attribute.
> I expect them to treat the new value in the same way in both cases.

Well sure, but what about the case where you have no mutator?

    has foo => ( isa => 'HashRef', traits => ['Hash'], handles => {
... } ); # look ma no mutator

How would you explain the expected behavior then?

How would you explain that state and behavior are isomorphic?

    has value => ( is => 'ro', lazy => 1, default => sub {
long_calculation_in_c() } );

vs

    sub value { long_calculation_in_c() }

I wouldn't expect long_calculation_in_c() to be called if I call
->new(value => $test_value) in the first case ... would you? Is the
default part of the mutator?

What about mutators with side effects?

    use MooseX::Hypothetical::LockableAttributes;
    has value => ( is => 'rw', traits => ['LockOnChange'], lock_timeout => 30 );

The many many variations on mutators is what makes the idea that
attributes and mutators are *different* important. It's the difference
between state (attributes) and behavior (methods ... in this case
accessors / mutators).

>> I can understand why you would have the expectations you have. I can
>> also understand why I have the expectations I have.
>
> I don't understand why you have the expectations you have. I'm not even
> sure I understand what those expectations are. Is there any
> documentation that explain what expectations are sensible to have for Moose?

Moose::Manual::Unsweetened (
https://metacpan.org/module/Moose::Manual::Unsweetened ) goes a long
way to describing the expected outcome of Moose. Things can always be
better.

> (one thing I've noticed is that the documents start out by giving basic
> explanations of how to construct objects, and then more complicated
> explanations of what can be constructed, without giving any examples at
> all of how to use the objects.)

As Jesse (doy) has said elsewhere in this thread, please if you find
something confusing or you can think of a way to clarify something ...
write a doc patch. The fact that our expectations differ from yours
suggests strongly that we're not the right people to write such a
patch.

>> Typically (though
>> arguably not always) with Moose the defaults should be set to the
>> least intrusive / most explicit possible. Since there is no way to
>> unfire an accessor method during construction, the default has to be
>> to make that behavior explicit.
>
> I'm not clear what defaults have to do with it? Nor what would
> constitute unfiring an accessor? Or why you would want to do it?

Not "defaults" in the attribute sense ... defaults in the sense of
"What Moose does without someone altering the behavior by using a
MooseX or other extension". As so why you would want to unfire an
accessor, in the system you described where Class->new( foo => 'bar' )
is exactly equivalent to $o = Class->new(); $o->foo('bar'); any
side-effects that calling the foo() method are unavoidable ... this
makes writing some kinds of accessors more difficult. Moose's
base-line behavior is typically to provide the least intrusive
behavior and let people write out the differences themselves in
extension modules etc.

-Chris

Reply via email to