NAME
UNIVERSAL::Object::Immutable - Another useful base class
VERSION
version 0.17
SYNOPSIS
# used exactly as UNIVERSAL::Object is used
our @ISA = ('UNIVERSAL::Object::Immutable');
DESCRIPTION
You can use this class in the same manner that you would use UNIVERSAL::Object, the only difference is that the instances created will be immutable.
When are instances made Immutable?
Obviously we need to create and initialize the instance before we make it immutable, it is only after that when we want it to be immutable. This means that if you need to munge values, or build values based on the contents of other slots, you should do this inside a BUILD
method, during which the instance will still be mutable.
The instance will remain immutable throughout it's lifetime up until the DESTROY
method is called. Before delgating to any DEMOLISH
methods the instance will be unlocked. This will allow you to handle any kind of object descostruction neccesary before the instance is freed.
Supported REPR types
This module will attempt to do the right type of locking for the three main instance types; SCALAR
, REF
, ARRAY
, HASH
and CODE
, all other instance types are unsupported and will throw an error.
Why Immutability?
Immutable data structures are unable to be changed after they are created. By placing and enforcing the guarantee of immutability, the users of our class no longer need to worry about a whole class of problems that arise from mutable state.
Immutability is semi-viral
Inheriting from an immutable class will make your subclass also immutable. This is by design.
When an immutable instance references other data structures, they are not made immutable automatically. This too is by design.
This means that given the following class:
package Person {
use strict;
use warnings;
our @ISA = ('UNIVERSAL::Object::Immutable');
our %HAS = (
given_names => sub { +[] },
family_name => sub { '' },
);
}
package Employee {
use strict;
use warnings;
our @ISA = ('Person');
our %HAS = (
%Person::HAS,
job_title => sub { '' },
);
}
Any of the following lines would cause an error about modification of a read-only value because we are trying to change the values inside the hashes or add new values.
my $e = Employee->new;
$e->{family_name} = 'little';
$e->{family_name} .= 'little';
$e->{given_names} = [ 'stevan', 'calvert' ];
$e->{job_title} = 'developer';
$e->{misspelled_key} = 0;
However, the following would not cause an error because the ARRAY
reference stored in the given_names
slot is not itself read-only and so can be modified in this way.
my $e = Employee->new;
push @{ $e->{given_names} } => 'stevan', 'calvert';
Immutability after the fact
When this class is used at the root of an object hierarchy, all the subclasses will be immutable. However, if you wish to make an immutable subclass of a non-immutable class, then you have two choices.
NOTE: both of the examples below assume that there is not a locally defined new
in your class.
- Multiple Inheritance
-
Using multiple inheritance, and putting this class first in the list, we can be sure that the expected version of
new
is used.our @ISA = ( 'UNIVERSAL::Object::Immutable', 'My::Super::Class' );
- Role Composition
-
Using the role composition facilities in the MOP package will result in
new
being aliased into the consuming package and therefore have the same effect as the multiple inheritance.our @ISA = ('My::Super::Class'); our @DOES = ('UNIVERSAL::Object::Immutable'); # MOP::Util can be used to performs the role composition
Compatibility Note
This class requires Perl 5.8.0 or later, this is because it depends on the Hash::Util module and the Internals::SvREADONLY
feature, both of which were first introduced in that version of Perl. Since this is an optional component, we have not bumped the version requirement for the entire distribution, only for this module specifically.
AUTHOR
Stevan Little <stevan@cpan.org>
COPYRIGHT AND LICENSE
This software is copyright (c) 2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023 by Stevan Little.
This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.