Traits — Meta Data With Character

Traits are a nice, extensible way to attach meta data to all sorts of objects in Perl 6.

An example is the is cached trait that automatically caches the functions return value, based on the argument(s) passed to it.

Here is a simple implementation of that trait:

 # this gets called when 'is cached' is added
 # to a routine
 multi sub trait_mod:<is>(Routine $r, :$cached!) {
     my %cache;
     #wrap the routine in a block that..
     $r.wrap(-> $arg {
         # looks up the argument in the cache
         %cache{$arg}:exists
             ?? %cache{$arg}
             # ... and calls the original, if it
             # is not found in the cache
             !! (%cache{$arg} = callwith($arg))
         }
     );
 }
 
 # example aplication:
 sub fib($x) is cached {
     say("fib($x)");
     $x <= 1 ?? 1 !! fib($x - 1) + fib($x - 2);
 }
 # only one call for each value from 0 to 10
 say fib(10);

A trait is applied with a verb, here is. That verb appears in the routine name that handles the trait, here trait_mod:<is>. The arguments to that handler are the object on which the trait is applied, and the name of the trait (here cached) as a named argument.

Note that a production grade is cached would need to handle multiple arguments, and maybe things like limiting the cache size.

In this example, the .wrap method is called on the routine, but of course you can do whatever you want. Common applications are mixing roles into the routine or adding them to a dispatch table.

Traits can not only be applied to routines, but also to parameters, attributes and variables. For example writable accessors are realized with the is rw trait:

 class Book {
     has @.pages is rw;
     ...
 }

Traits are also used to attach documentation to classes and attributes (stay tuned for an addvent calendar post on Pod6), marking routine parameters as writable and declaring class inheritance and role application.

This flexibility makes them ideal for writing libraries that make the user code look like a domain-specific language, and supplying meta data in a safe way.

6 thoughts on “Traits — Meta Data With Character

  1. AIA if this formats badly and I can’t edit it…

    “Note that a production grade is cached would need to handle multiple arguments”

    —-

    I’ll bite. I’d be delighted to get comments.

    A1) Parameter list that handles zero or more args
    A2) Cache indexing corresponding to zero or more args

    —-

    A1) Parameter list that handles zero or more args

    From https://raw.github.com/perl6/specs/master/S08-capture.pod:
    “the data structure that is used to send the parameters into a routine invocation (be it a method or a sub) is exposed to the language as a built-in type like any other.”

    So:
    $r.wrap(-> ¢args {
    ?

    A2) Cache indexing corresponding to zero or more args

    From https://raw.github.com/perl6/specs/master/S02-bits.pod:
    “Every object can produce a “WHICH” value that uniquely identifies the object for hashing and other value-based comparisons.”

    So:
    $r.wrap(-> ¢args {
    my $key = ¢args.WHICH
    %cache.exists($key)
    ?

    1. I don’t think that second part really works, because every capture will be a unique object.

      And I’m not sure how well WHICH works in general yet…

    2. Note that captures are no value types, so you can’t use .WHICH for hashing and comparing. So you have to come up with something more clever.

      1. Hi Moritz,

        “captures are no value types, so you can’t use .WHICH for hashing and comparing”

        I realize captures aren’t value types. But it totally doesn’t yet follow for me that you shouldn’t (let alone can’t) expect to use WHICH for hashing and comparing.

        Reread the paragraph I quoted below from: http://perlcabal.org/syn/S02.html#The_.WHICH_Method_for_Value_Types

        Maybe I haven’t had enough coffee, or maybe the spec is wrong, misleading, or overly ambitious, but my simple read of it this is that:
        A) WHICH is intended for hashing and comparing of objects …
        B) … that are often not value types, and that …
        C) … the capture class could “define a .WHICH method that makes different [captures] look like the same object if they happen to have the same contents.”

        I was imagining a capture would walk the tree of objects it contains, collecting WHICHs. If any object doesn’t return a WHICH value, the walk ends and the capture fails to cache. If any object returns an ObjAt which ignores the “same content” issue, then the capture, if cached, ends up taking space in the cache but never matches.

        Am I off base?

      2. Yes, you are off base.

        .WHICH is indeed used for hashing and comparison, but not the same kind of comparison that is cached needs. It does object identity comparsion (like the === operator), but caching needs object equivalence comparsion (like the eqv operator). Only for value types are the two comparison semantics the same.

        Each call to a subroutine creates a new Capture object, so comparison with .WHICH and hashing will never find a match.

  2. > I’m not sure how well WHICH works in general yet…

    Right. I decided not to mention that “implementation detail”.

    > I don’t think that second part really works, because every capture will be a unique object.

    I wondered about that.

    http://perlcabal.org/syn/S02.html#The_.WHICH_Method_for_Value_Types says
    “Every object can produce a “WHICH” value that uniquely identifies the object for hashing and other value-based comparisons. Normal objects just use their location as their identity, but if a class wishes to behave as a value type, it can define a .WHICH method that makes different objects look like the same object if they happen to have the same contents.”

Leave a reply to raiph Cancel reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.