Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Writing an attribute trait

Tags:

raku

mop

I'm about to choose what language to use for a new project: Perl5 or Perl6. 6 wins so far except that it is missing Moo's lazy attributes. The two implementations I found in modules are missing the key functionality. Hence, my attempt write my own implementation.

Role vs. Class

First problem I've got into is the content of attribute's .package for one declared in a role. Consider the followin:

 role HOW1 {
     method compose ( Mu $class ) {
         note "HOW1.compose";
         nextsame;
     }
 }

 role HOW2 {
     method compose ( Mu $class ) {
         note "HOW2.compose";
         nextsame;
     }
 }

 multi trait_mod:<is> (Attribute:D $attr, :$mooish!) {
     note "Attribute's package.HOW: ", $attr.package.HOW;
     note '$*PACKAGE.HOW: ', $*PACKAGE.HOW;
     $attr.package.HOW does HOW1;
     $*PACKAGE.HOW does HOW2;
 }

 class Foo {
     has $.bar is mooish;
 }

 role FooRole {
     has $.baz is mooish;
 }

The output of the script follows:

Attribute's package.HOW: Perl6::Metamodel::ClassHOW.new
$*PACKAGE.HOW: Perl6::Metamodel::ClassHOW.new
HOW2.compose
HOW1.compose
Attribute's package.HOW: Perl6::Metamodel::GenericHOW.new
$*PACKAGE.HOW: Perl6::Metamodel::ParametricRoleHOW.new
HOW2.compose

As it is clearly seen from the output, applying a role to a metaclass always works for classes and only works for $*PACKAGE.HOW with roles. Use of $*PACKAGE instead of .package could be considered a solution, but not the one I'd really like to use. (Though, if there is no better way...)

Accessor

I would like to provide lazy functionality for private attributes too. Yes, this will be availabe with self!bar syntax only, but this is a sacrifice I'm willing to make. 😉 The problem is that all the examples of custome-made accessor I found so far are using Attribute.set_value() method which is way too low-level. I'd like to have something like this:

 role MooishHOW {
     method compose ( Mu $class ) {
         my $accessor = $class.^add_private_method( 'bar1',
             method () is rw {
                 note self.WHO, ".bar1";
                 Proxy.new(
                     FETCH => -> $o {
                         $!bar1;
                     },
                     STORE => method ( $val ) {
                         note "Storing";
                         $!bar1 = $val;
                     }
                 );
             }
         );

         callsame;
     }
 }

 multi trait_mod:<is> (Attribute:D $attr, :$mooish!) {
     $attr.package.HOW does MooishHOW unless $attr.package.HOW ~~ MooishHOW;
 }

 class Foo {
     has $.bar is mooish;
     has $!bar1 is mooish;

     method to-bar1 {
         note "bar1 val:",self!bar1;
     }
 }

 my $inst = Foo.new;
 $inst.to-bar1; 

But $!bar1 notation doesn't compile because of the scope (MooishRole). Are there a trick I'm missing which would allow referencing a private attribute on self?

Tricky one

Perhaps it is possible to make an attribute to be a Proxy container? This would greatly simplify the overall logic of laziness implementation.

like image 410
Vadim Belman Avatar asked Jul 31 '18 17:07

Vadim Belman


People also ask

How do you write attributes?

Attributes are always specified in the start tag (or opening tag) and usually consists of name/value pairs like name="value" . Attribute values should always be enclosed in quotation marks.

What is an example of a attribute?

An attribute is defined as a quality or characteristic of a person, place, or thing. Real life individuals and fictional characters possess various attributes. For example, someone might be labeled beautiful, charming, funny, or intelligent.

What are 3 examples of a trait?

Examples are height, skin color, hair color, and eye color of humans. The traits are determined not by a single gene but by multiple genes.


1 Answers

I have answered all my questions by finally achieving the target and released AttrX::Mooish module.

So far, the answer for the first question is: no. $*PACKAGE is currently the only way.

Second question: have no answer, but the final code has to rely on set_value() anyway.

The tricky one happened to be possible: set_value() does binding of an attribue to a container making it possible to bind to a Proxy object. No need to for sacrifices, private attributes can be accessed directly with lazyness working on them.

Thanks everybody, your answers let me work around some rough edges!

like image 163
Vadim Belman Avatar answered Nov 09 '22 21:11

Vadim Belman