NAME

UNIVERSAL::Object::Immutable - Another useful base class

VERSION

version 0.08

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.

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.

Multiple Inheritance

Using multiple inheritance, and putting this class first in the list, we can be sure that the expected version of BLESS is used.

our @ISA = (
    'UNIVERSAL::Object::Immutable',
    'My::Super::Class'
);
Role Composition

Using the role composition facilities in the MOP package will result in BLESS 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');
# make sure something 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, which was 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 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.