True private attributes and methods for modern Perl OOP
Tags: perl, oop, zydeco, privacy
Published: 2021-03-20 14:58:59

Today we will [continue](https://dev.to/smonff/random-integer-generation-in-an-interval-with-modern-oop-perl-4p2c) our exploration of modern Perl OOP. Those are very experimental features, but I feel like it could be a bit similar to what [Corinna](https://github.com/Ovid/Cor) could look like so I find it fun to try new things. This is also an opportunity to learn more about OOP.

This is an example of how to implement private attributes and methods with the [Zydeco](https://zydeco.toby.ink/) toolkit. We will also see how polymorphism and named parameters works for the needs of this example.

There are other OO modules plugins that allow to add privacy features, but in Perl, it is interesting to see that, privacy is often mostly by convention: subroutines are prefixed with an underscore (e.g. `sub _super_private { ... }`) and everybody will consider it as private. See [a short explanation about this on perlmaven.com](https://perlmaven.com/moo-with-public-and-private-methods) in the *Public and private methods* section.

```perl
use v5.20;
use strict;
use warnings;

package DevTo {
  use Zydeco;
  class Privacy {

    # Predeclaration of the accessor coderef
    my $hidden_attr;

    has wonder ( isa => Str );
    has hidden (
      isa => Str,
      is => private,
      accessor => \$hidden_attr );

    method $innaccessible_method {
      say "No problem acessing " . $self->wonder .
      ' through a private method';
    }

    method $innaccessible_method_attr {
      $self->$hidden_attr('a hidden place');
      say "No problem accessing a private attribute in " .
      $self->$hidden_attr;
    };

    multi method no_problem {
      $self->$innaccessible_method;
    }

    multi method no_problem( Bool *with_attr ) {
      $self->$innaccessible_method_attr;
    }
  }

  my $access = DevTo
    ->new_privacy( wonder => "a public attribute" );
  
  $access->no_problem;
  # ==> "No problem acessing a public attribute through a private method"
  $access->no_problem({ with_attr => true });
  # ==> "No problem accessing a private attribute in a hidden place"

  # This would fail
  # $access->$innaccessible_method;

  # No things are not possible
  # $access->hidden_attr;
  # $access->$hidden_attr;
}
```

So, what are we doing here?

The privacy with Zydeco can target two different entities:

- attributes, by using the `is => private` during the attribute declaration
- methods, by prefixing the method name with a `$`

We have two private methods, that we will call from the public *multi* `no_problem()` method. This one is declared two times, with different signatures and prefixed with the `multi` keyword: it makes possible to setup the polymorphism, dispatching the desired behavior depending on the passing of a boolean or not:

```perl
# Would call $inaccessible_method
$access->no_problem; 

# Would call $inaccessible_method_attr
$access->no_problem({ with_attr => true });
```

Note how *named attributes* are declared and easily used:

```perl
multi method no_problem( Bool *with_attr ) { ... }
```

By prefixing the parameter name with a `*` and not a `$` in the method signature, you get automatic parameter names ready for later 

Then, we create a new `Privacy` object that we call `$access`. `Privacy` got a public attribute, `wonder` and one that is private, called `hidden`. With Zydeco, declaring an attribute as "private" [make it assessor-less](https://zydeco.toby.ink/02_Attributes.html#Declaring_a_Basic_Attribute). Under the hood, if you check on of the underlying technologies that power Zydeco, it [doesn't mean that the attribute is totally inaccessible](https://metacpan.org/pod/MooX::Press#Attribute-Specifications): we *can request an accessor as a coderef*. 

```perl
my $hidden_attr;
has hidden (
  isa => Str,
  is => private,
  accessor => \$hidden_attr );
```

And then access the attribute *in the scope of the variable* as follows:

```perl
$access->$hidden_attr;
```

Outside the variable scope, the attribute will be totally invisible. This is possible thanks to a [handy technic](https://stackoverflow.com/questions/7814489/how-do-you-dynamically-call-a-method-of-an-object) that allows to call methods dynamically.

# References

- https://zydeco.toby.ink
- https://perlmaven.com/moo-with-public-and-private-methods
- https://metacpan.org/pod/MooX::Press#Attribute-Specifications