Immutability with Moo(se)
Would it be doable, to have a module to activate immutability with Moo(se)? Something like
which would make all the objects from the class immutable.
As a short introduction, immutability is when objects never change: instead of modifying data in place, you must make a copy of it and override the values you want to change. Thus when you use an object at any point in your code, it will always be exactly the same: you are never taking the chance that some method modified it without you realizing.
I've come across the concept of immutability a fair few times recently:
- chromatic explains that it "reduces the possibility that someone will manipulate the object and make it invalid" and thus "you get to remove a lot of error checking code because wtihin the API you can assume that the object is always in a valid state"
- DateTime::Moonpig is immutable because mutability is dangerous.
- Tobyink says "Immutable objects should be favoured over mutable ones."
- nothingmuch wrote (in 2009) three excellent articles about immutability, where s.he explains various reasons why immutability is good, and indicates ways to achieve it with Perl...
...but now we have Moo(se) so I'm hoping there can be a simpler way.
If I understood well, MooX::Immutable would need to clone every input and output:
- input: all attributes that are passed as parameters to the contructor
- output: all accessors return a clone of the corresponding attribute
And I think that... that'd be it? Am I missing something?
Would that be something doable? useful?
PS: after a byterock's first comment, I thought I should clarify what immutability is and what a reasonable goal could be.
Immutability is a way of programming, where you never modify a variable that has been assigned nor an object that has been created. It's generally considered a "good practice". Some languages like Haskell and Erlang are immutable by default: once a variable has some value it cannot change at all anymore. Clojure provides immutable data structures too, so it's easy to keep everything immutable. To understand why that's good, read some of the links I indicated above.
In Perl it's not that simple, because Perl is flexible. In the same way that Moose made it much easier to produce standard and clean object-oriented code, I'm hoping that we can come up with some tool(s) to facilitate the production of immutable code.
But the difficulty is that to produce immutable code, objects need to have all attributes immutable. And CPAN makes this difficult because not many modules will guarantee that they do everything immutable (for instance DateTime is not immutable; DateTime::Moonpig is the immutable version of DateTime).
Moose had a similar problem: how do you subclass a non-Moose class? And they came up with a solution: MooseX::NonMoose. Can we come up with solutions for immutability?
There seem to be two ways to go about it. Two solutions:
1. Try to only use immutable modules from CPAN... or use them, but don't use the methods that mutate state (if you can figure out and then remember which ones they are).
2. Clone all object inputs (constructor parameters) and outputs (accessor return values) so that the object cannot be modified (in theory that's perfect).
Toby pointed out the problem with solution 2:
- not all objects can be cloned easily (but most can - in the same way that not all classes can be used with MooseX::NonMoose, but most can)
- what happens when someone uses a mutable method on a DateTime attribute?
Well, it mutates their clone of the attribute but not the attribute itself. Which is what we want to protect immutability, but it could be confusing for the user. I'm still thinking though, that if you advertise your class as being immutable, as in "You won't be able to modify this object - accessors will only return clones of the attributes", users should be able to figure it out. But yeah, it's no ideal...
For solution 1 even though it's not perfect we could build some tools to help, like:
- a Moo(se)X module that makes ALL scalar, hashref and arraref attributes immutables (with Sub::Trigger::Lock)
- a module that makes it easy to test for immutability, so more modules can guarantee immutability on CPAN.
In an ideal world we could "immutabilize" an object, so that any attempt to modify it throws an exception. For example "$datetime->add_duration()" would throw an exception.
I have no idea whether this is possible...