in reply to Making 'all' the attributes read only by default (Moo)

An immutable object is like waterproof paper towels; a leakproof sieve; or a docile guard dog.


With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
"Science is about questioning the status quo. Questioning authority". I'm with torvalds on this
In the absence of evidence, opinion is indistinguishable from prejudice. Agile (and TDD) debunked
  • Comment on Re: Making 'all' the attributes read only by default (Moo)

Replies are listed 'Best First'.
Re^2: Making 'all' the attributes read only by default (Moo)
by blindluke (Hermit) on Jan 23, 2015 at 09:04 UTC

    It's a good thing, then, that I'm not trying to make my objects immutable :)

    I'm just searching for some way to type is => 'ro' less, and make it clear that all the attributes of a given package have the 'ro' property set.

    - Luke

      Save yourself some typing and some angst:

      use constant Boo => { attr_A => 'A', attr_B => 'B', attr_C => 123, };

      With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
      Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
      "Science is about questioning the status quo. Questioning authority". I'm with torvalds on this
      In the absence of evidence, opinion is indistinguishable from prejudice. Agile (and TDD) debunked

        Can the Boo constant consume a role? I don't think so.

        I know that your replies are usually both knowledgeable and helpful, as I've learned much from many of them, so I'll skip over the 'angst' remark and try to provide an example that better conveys my aim:

        package Ghost { use Moo; with('Attacker'); has 'Str' => ( is => 'ro', default => sub { '8' } ); has 'Dex' => ( is => 'ro', default => sub { '16' } ); has 'Con' => ( is => 'ro', default => sub { '10' } ); has 'Int' => ( is => 'ro', default => sub { '10' } ); has 'Wis' => ( is => 'ro', default => sub { '10' } ); has 'Cha' => ( is => 'ro', default => sub { '6' } ); } my $boo = Ghost->new( Int => '14', Con => '16' );

        I'm asking about a way to state that all the attributes of Ghost are readonly, as I think that:

        package Ghost { use Moo; use Moo::AllAttributesAreReadonly; with('Attacker'); has 'Str' => ( default => sub { '8' } ); has 'Dex' => ( default => sub { '16' } ); has 'Con' => ( default => sub { '10' } ); has 'Int' => ( default => sub { '10' } ); has 'Wis' => ( default => sub { '10' } ); has 'Cha' => ( default => sub { '6' } ); } my $boo = Ghost->new( Int => '14', Con => '16' );

        or something like this, would be much more elegant than the way I am using now. If my "classes" did not consume roles, had no methods, and were as simple as the one I provided, then I would have no reason to use Moo in the first place.

        - Luke

      If all the attributes are "ro", then how is the resulting object not immutable (interface-wise at least)?

        Wow, the poison of "the most important thing about an object is what attributes it has" mindset (that is pushed by tools like Moose) really has ruined many people's concept of OO.

        If all the attributes are "ro", then how is the resulting object not immutable (interface-wise at least)?

        OO is an abstraction. The point of abstraction in programming is to hide implementation details by providing an interface. One of the important ways to hide details is called "data hiding" -- you should not just plumb through your interfaces full access to the data used in the implementation. The point of interfaces is to be as narrow as possible to reduce the complexity of the remaining part of the problem after you have factored out the implementation details you have isolated (inside of the class, in this case).

        Not having mutators dedicated to every single attribute doesn't make your object immutable from an interface perspective. It simply forces your interface to accomplish changes to attributes only via methods that have a purpose that requires, in the implementation details, that attribute values be modified.

        That's not an immutable object. It is merely an object that has a chance of having a decent interface designed. It indicates a class that might adhere to good programming practices recognized in the 1970s and still rightly praised today.

        OO is not a replacement for modular programming. It is a somewhat newer tool that can be used to facilitate modular programming, but only if you use the tool appropriately.

        I recently got to see a new form of the problems with "objects as an 'interface' to bags of attributes". It has been a while now since I realized that such designs lead to poor abstraction that leads to hard to maintain code after a project has gone through enough maintenance. Just recently I got to see the results of trying to improve code by factoring out part of the complexity into a new class, but then making that class mostly just a bag of attributes.

        The immediate effect was to make the code more complicated to understand. Sure, a bit of structure had been imposed. And a few tiny bits of actual behavior did get put into just a couple of methods in the new class. The overly complex code even got slightly smaller.

        But the result was harder to understand because most of the uses of data that was supposed to be hiding in the new objects was being manipulated by code outside of the class. So you could not make much sense of the class by just looking at the class. All this meant was that, when reading parts of the overly complex code, now you would find all of the places where the 'foo' datum was used and not be able to make sense of 'foo'. Then you'd realize that there must be some more manipulations involving 'foo' somewhere else (or, worse, just come to inaccurate conclusions).

        It just made you have to jump back and forth between two files of code whenever you tried to understand anything that involved data now held (quite loosely) in the new object.

        The worst offense on this front was a method in the new class that wrote an output file. This method had to be fed the name of the file to write out. But that name was built from the data held in the object. The code using the new class actually queries the object (via its many accessors) to get the parts needed to construct the name of the output file, does the mundane but non-trivial steps to combine those, and then feeds the result back into the object.

        No, the object isn't "immutable interface-wise", it just isn't obviously badly designed, interface-wise.

        - tye        

        There are some really good explanations in the blog post link I have provided. A simple answer is that if the attribute value is a reference, you can easily use the reference to change the object. Consider a Person object that has some friends, with friends being an array reference. You can make the friends attribute 'ro', but you gan still read the reference and use it to change the object, like this:

        push @{ $john->friends }, $new_friend;

        That is why I'm asking for a shortcut to writing is => 'ro', and not for a way to make the object immutable. I don't think that making the object truly immutable is a sensible goal. Making all the attributes readonly (in the way Moo understands it), on the other hand, can be useful.

        - Luke