Beefy Boxes and Bandwidth Generously Provided by pair Networks
"be consistent"
 
PerlMonks  

Re: Traits as Method Exporters

by stvn (Monsignor)
on Apr 11, 2004 at 04:59 UTC ( [id://344202]=note: print w/replies, xml ) Need Help??


in reply to Traits as Method Exporters

chromatic is right, in Perl 6 land, they are Roles, and that is not the same as traits (as described in the traits papers and as implemented in Class::Trait). Roles is an expansion/evolution on traits, and from that I know of them so far, a much more perlish way of doing them. chromatic wrote an implementation with Class::Roles, but I believe he said he has put further development on hold till the Perl 6 crew figures out more of the details. I wrote Class::Trait actually, Ovid started it but got busy at work (damn that day job stuff), and I ran with it.

Now, as for traits model being a gussied up Exporter, I would have to disagree. The exporting of subroutines into the implementing package's namespace is only the end result of the traits. Much of the reason why traits make sense (which I think many people overlook, I know I did at first) is because they are strongly grounded in formal theory. The combination of traits (summation), and the use of aliasing, exclusion along with method requirements all create a nice solid little box that traits can live and work in. But even still the exportation of subroutines is only part of what traits need to do, checking for required methods, and handling for method conflicts are vitally important to it all working right.

If you are interesting in seeing more of the process by which traits are composed I would suggest using the debug setting in Class::Trait. About half the code in that module is debug/trace statements actually. The easiest way to do that is to download the distribution, add the following code to the top of the file t/30trait_composition.t:

use Class::Trait 'debug';
then run the file with perl (not through Test::Harness). You will see a about 210 lines of output printed by Class::Trait (its actually about 420 lines in total with the Data::Dumper output) which details the entire trait composition process.

I would also recommend reading the paper "Traits - A Formal Model" linked to on the traits page, it provides an insight into the theory behind how traits get composed and how they interact with classes. I have included a small exerpt/bastardization/summarization of it below, that I was going to include in the Class::Trait documentation, but never got around to. My apologies to the original authors.


Traits Language
method dictionary: D ::= {} | { x[1] -> m[1], ... , x[n] -> m[n] }
trait expression: T ::= N[t] | T + T | T - x | T[x => y]
trait definiton: T{def} ::= N[t] = D | N[t] = D with T
fields: F ::= {} | { f[1], f[2], ... , f[n] }
class expression: C ::= nil | N[c] | F in D extends N[c] | F in D with T extends N[c]
class definition: C{def} ::= N[c] = C

D denotes a method dictionary, which is either empty or contains a finite number of services (i.e., bindings of labels to methods) in which all of the labels x[i] are distinct.

T denotes a trait expression, which can be a named trait N[t], the symmetric composition T + T of two traits, or a trait that is subject to an exclusion T - x or an alias T[x => y].

A trait definition (T{def}) binds a trait name N[t] to one of two forms: D, a method dictionary, or N[t] = D with T, a method dictionary that adds methods to (and possibly overrides methods from) a trait.

F is a possibly empty set of variable (or field) names; it is not important for our purposes whether these are instance variables, class variables, or static variables.

C denotes a class, which can be nil, a named class N[c], or a conventional class (F in D extends N[c]), which consists of some fields F, a method dictionary D, and a named superclass N[c]. In addition, a class can be structured F in D with T extends N[c]; the structured class acquires methods from the trait T augmented (and possibly overridden) by those from the explicit method dictionary D.

Finally, a class definition C{def} is simply a binding of a class name N[c] to a class C.


Model for Traits

Traits are essentially sets of bindings of labels to methods; the labels name the methods. We call these bindings services. Traits both provide services (i.e., the methods implemented) and require services (i.e., those that are invoked on the class using the trait). When we say that a class or a trait provides a service with a particular label we mean that a binding of that label to a method exists. When we say that a service with a particular label is required, we mean that a binding of that label to a method needs to exist.

Traits and Classes

Traits are subordinate to classes in several ways. Unlike classes, traits bind neither self nor super. Classes can contain fields and methods in classes can refer to these fields, so it is possible for classes to provide accessor methods. In contrast, traits do not contain fields, and as a consequence methods in traits cannot refer to fields. However, because the requirements of a triat method can be met by accessor methods, a trait methods can access state indirectly.

A class is built up from traits, methods, and a single superclass using single inheritance. If the traits conflict, the class can eliminate the conflict by overriding the conflicting methods. "super" is bound only at the point that a class is composed, and "self" refers to an instance of a composed class.

Trait operations

We introduce summation (+), exclusion (-), aliasing (=>) and overriding (>) as mechanisms for composing and modifying traits. When traits are composed with +, labels that are bound to different methods will conflict; we represent this by binding the label as a method conflict (#) in the sum. The > operation is intended to be used to override these method conflicts (#) with proper methods.

Summation composes two traits, forming the union of non-conflicting bindings and "disabling" the conflicting bindings.

For example:

{a -> m[1], b -> m[2], c -> m[3]} + {a -> m[1], b -> m[3]}
results in:
{ a -> m[1], b -> #, c -> m[3] }
Exclusion removes a binding from a trait. This can be used to remove a binding to avoid a conflict.

For example:

{ a -> m[1], b -> # } - b
results in:
{ a -> m[1] }
Aliasing introduces an additional name for an existing method.

For example:

{ a -> m[1], b -> m[2] } [ c => b ]
results in:
{ a -> m[1], b -> m[2], c -> m[2] }
When composing classes or traits, we may need to replace conflicts by proper methods. For this reason we introduce the > operator. A trait T[1] may replace some of the bindings in another trait T[2]. Not that the overriding operation is on traits and not on specific bindings.

-stvn

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: note [id://344202]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others goofing around in the Monastery: (9)
As of 2024-04-19 09:33 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found