Encapsulation, or Moose?

Ovid on 2006-10-12T08:22:30

This was not surprising, but also not what I wanted:

#!/usr/bin/perl

use strict;
use warnings;

{
    package Point;
    use Moose;

    has 'x' => ( is => 'rw', isa => 'Int' );
    has 'y' => ( is => 'rw', isa => 'Int' );
}

#use Devel::EnforceEncapsulation;
#Devel::EnforceEncapsulation->apply_to('Point');
use Test::More qw'no_plan';
use Test::Exception;

ok my $point = Point->new, 'Creating a new Point should succeed';
ok $point->x(3), '... as should setting x()';
ok $point->y(7), '... and y()';
is $point->x, 3, '... and x() should have the correct value';
is $point->y, 7, '... as should y()';

throws_ok { $point->x(8.3) }
    qr/\QAttribute (x) does not pass the type contraint (Int) with '8.3'/,
    'Setting an attribute to an invalid value should fail';

Those tests pass -- until you uncomment the encapsulation lines.

Illegal attempt to access Point internals from Class::MOP::Instance

I had my hopes up for a moment :)


Umm...

nothingmuch on 2006-10-12T12:54:49

you do realize something has to "violate" encapsulation somewhere, namely the accessor code, right?

Class::MOP::Instance is precisely the object that lets accessors get at an objects instance structure.

Re:Umm...

Ovid on 2006-10-12T13:44:14

See my reply to Stevan, below.

You are totally mistaken in your assumptions

Stevan on 2006-10-12T13:24:55

There is no violation of encapsulation here actually, you should ask before you assume :)

Class::MOP::Instance is the meta object which represents the instance structure, it is the gateway through which all the access to the underlying instance structure is proxied (it also handles generating code for any inlined accessors as well). This puts a layer of abstraction over the instance structure so that the Moose user does not have to think about what kind of instance they are using, and therefore can change the instance type based on their application requirements.

Now, of course we do not yet have the ability in Moose to alter the instance structure (currently I have very limited tuits because of $work), but the protocol is in place, and we do have some proof of concept examples in Class::MOP. Please see the Array Based instance example and the Inside Out based instance example and note that their tests (here and here) are almost completely identical (aside from some basic meta-level tests specific to the instance type and the declarations to use the specific instance types themselves).

Sometimes enforcing encapsulation is not just about where the access takes place, but instead more about how it takes place.

- Stevan

Re:You are totally mistaken in your assumptions

Ovid on 2006-10-12T13:43:43

What did you think my assumptions were, Stevan? I wasn't assuming this would pass. I was merely hoping I could get away with it :)

Re:You are totally mistaken in your assumptions

Stevan on 2006-10-12T13:55:29

From your title ("Encapsulation or Moose"), and your initial line ("This was not surprising, but also not what I wanted" ) it seemed to me that you were implying that Moose violated encapsulation. Which admitedly, if you go by the definition of encapsulation encoded in Devel::Encapsulation, it does. However, there is more than one way to do it :)

Of course, my response is riddled with assumptions on my part, so if I am mistaken, please clarify it for me.

- Stevan

Re:You are totally mistaken in your assumptions

Ovid on 2006-10-12T13:48:53

The issue here isn't that I think that Class::MOP is doing something naughty. The issue is that I want the final test to fail:

throws_ok { $point->x(8.3) }
    qr/\QAttribute (x) does not pass the type contraint (Int) with '8.3'/,
    'Setting an attribute to an invalid value should fail';

$point->{x} = 8.3;
is $point->x, 8.3, 'Whoops!  I broke it';

Assumptions abound!

Stevan on 2006-10-12T14:08:58

The issue here isn't that I think that Class::MOP is doing something naughty.

What exactly do you think it is doing wrong? It is following the Instance protocol, which states that only methods of (or generated by) Class::MOP::Instance should access the internals of the instance. Again, this violates the rather narrow defintion of encapsulation in Devel::Encapsulation, but it enforces (albeit with a much softer hand) encapsulation in a different way.

The issue is that I want the final test to fail

Moose does not enforce encapsulation, it suggests it. And in fact, Moose never will (by default) enforce encapsulation like this, IMO this is a descision for the user, and not the object system.

- Stevan

Re:Assumptions abound!

Ovid on 2006-10-12T14:16:34

I don't think Class::MOP is doing anything wrong. I'm sorry if it sounded like I was suggesting it! One thing about Moose I like is that it does make it very easy to respect encapsulation and I do agree that this is more of a user problem than a programming one. Sorry if my post came off the wrong way.

Re:Assumptions abound! (whoops)

Stevan on 2006-10-12T15:22:42

I don't think Class::MOP is doing anything wrong.I'm sorry if it sounded like I was suggesting it!

Sorry, my fault, you didn't suggest that. I mistakenly read "isn't" as "is", in your previous post. That will teach me to respond to posts while also half listening on a conference call with the $client :)

Anyway, no worries, I just wanted to be sure you understood the reason why you got that error, and that encapsulation is not actually being violated.

- Stevan