Author: wayland Date: 2009-02-24 00:33:42 +0100 (Tue, 24 Feb 2009) New Revision: 25503
Added: docs/Perl6/Spec/S14-roles-and-parametric-types.pod Modified: docs/Perl6/Spec/S12-objects.pod docs/Perl6/Spec/S29-functions.pod Log: S29: s/Container.pod/Containers.pod/ S14/S12: Extracted S14 from S12, added Jonathan's blog entry, a bit from S02, and a bit from the spectests Modified: docs/Perl6/Spec/S12-objects.pod =================================================================== --- docs/Perl6/Spec/S12-objects.pod 2009-02-23 21:06:52 UTC (rev 25502) +++ docs/Perl6/Spec/S12-objects.pod 2009-02-23 23:33:42 UTC (rev 25503) @@ -1105,378 +1105,6 @@ A C<proto> declaration may not occur after a C<multi> declaration in the same scope. -=head1 Roles - -Classes are primarily in charge of object management, and only -secondarily in charge of software reuse. In PerlĀ 6, roles take over -the job of managing software reuse. Depending on how you care to look -at it, a role is like a partial class, or an interface with default -implementation, or a set of generic methods and their associated data, -or a class closed at compile time. - -Roles may be composed into a class at compile time, in which case -you get automatic detection of conflicting methods. A role may also -be mixed into a class or object at run time to produce an anonymous -derived class with extra capabilities, but in this case conflicting -methods are overridden by the new role silently. In either case, -a class is necessary for instantiation--a role may not be directly -instantiated. - -A role is declared like a class, but with a C<role> keyword: - - role Pet { - method feed ($food) { - $food.open_can; - $food.put_in_bowl; - self.eat($food); - } - } - -A role may not inherit from a class, but may be composed of other -roles. However, this "crony" composition is not evaluated until -class composition time. This means that if two roles bring in the -same crony, there's no conflict--it's just as if the class pulled in -the crony role itself and the respective roles didn't. A role may -never conflict with itself regardless of its method of incorporation. -A role that brings in two conflicting crony roles I<may> resolve them -as if it were a class. This solution is accepted by the class unless -the class supplies its own solution. If two different roles resolve -the same crony conflict two different ways, those roles are themselves -in conflict and must be resolved by a "more derived" role or the class. - -A role doesn't know its own type until it is composed into a class. -Any mention of its main type (such as C<::?CLASS>) is generic, as is -any reference to C<self> or the type of the invocant. You can use -a role name as a type, but only for constraints, not for declaring -actual objects. (However, if you use a role as if it were a class, -an anonymous class is generated that composes the role, which provides -a way to force a role to test its crony composition for infelicities.) - -A role's main type is generic by default, but you can also parameterize -other types explicitly using type parameters: - - role Pet[::Petfood = TableScraps] { - method feed (Petfood $food) {...} - } - -(Note that in this case you must not use ::Petfood in the inner declaration, -or it would rebind the type to type of the actual food parameter.) - -If a role merely declares methods without defining them, it degenerates -to an interface: - - role Pet { - method feed ($food) {...} - method groom () {...} - method scratch (:$where) {...} - } - -Note that, while these methods must become available at class -composition time, they might be supplied by any of: another role, -the class itself, or some superclass. We know the methods that are -coming from the other roles or the class, but we don't necessarily know -the complete set of methods supplied by our super classes if they are -open or rely on wildcard delegation. However, the class composer is -allowed to assume that only currently declared superclass methods or -non-wildcard methods are going to be available. A stub can always -be installed somewhere to "supply" a missing method's declaration. - -Roles may have attributes: - - role Pet { - has $.collar = Collar.new(Tag.new); - method id () { return $.collar.tag } - method lose_collar () { undefine $.collar } - } - -If you want to parameterize the initial value of a role attribute, -be sure to put a double semicolon if you don't want the parameter to -be considered part of the long name: - - role Pet[::ID;; $tag] { - has ID $.collar .= new($tag); - } - -Within a role the C<has> declarator always indicates the declaration -from the viewpoint of the class. Therefore a private attribute declared -using C<has> is private to the class, not to the role. You may wish to declare an attribute -that is hidden even from the class; a completely private role -attribute may be declared like this: - - my $!spleen; - -The name of such a private attribute is always considered lexically scoped. -If a role declares private lexical items, those items are private to -the role due to the nature of lexical scoping. Accessors to such -items may be exported to the class, but this is not the default. -In particular, a role may say - - trusts ::?Class; - -to allow C<self!attr()> access to the role's C<$!attr> variables with the -class or from other roles composed into the class. Conflicts between -private accessors are also caught at composition time, but of course -need not consider super classes, since no-one outside the current -class (or a trusted class) can call a private accessor at all. -(Private accessors are never virtual, and must be package qualified -if called from a trusted scope other than our own. That is, it's -either C<self!attr()> or C<$obj!TrustsMe::attr().>) - -A role may also distinguish a shared method - - has method foo ... - method foo ... # same - -from a nonshared private method: - - my method !foo ... - my method foo ... # same, but &foo is aliased to &!foo - -Generally you'd just use a lexically scoped sub, though. - - my sub foo ... - -[Conjectural: To put a private sub into the class, say - - our sub !foo ... - -] - -A role can abstract the decision to delegate: - - role Pet { - has $groomer handles <bathe groom trim> = hire_groomer(); - } - -Note that this puts the three methods into the class as well as -C<$groomer>. In contrast, "C<my $!groomer>" would only put the -three methods; the attribute itself is private to the role. - -A role is allowed to declare an additional inheritance for its class when -that is considered an implementation detail: - - role Pet { - is Friend; - } - -A class incorporates a role with the verb "does", like this: - - class Dog is Mammal does Pet does Sentry {...} - -or equivalently, within the body of the class closure: - - class Dog { - is Mammal; - does Pet; - does Sentry; - ... - } - -There is no ordering dependency among the roles. - -A class's explicit method definition hides any role definition of -the same name. A role method in turn hides any methods inherited -from other classes. - -If there are no method name conflicts between roles (or with the -class), then each role's methods can be installed in the class. If, -however, two roles try to introduce a method of the same name the -composition of the class fails. (Two C<has> attributes of the same -name, whether public or private, are simply merged into one slot, -provided the types are the same; otherwise, the composition fails. -Role-private attributes are not merged, and from the viewpoint of -the composition, don't even exist, except to allocate a slot for each -such attribute.) - -There are several ways to solve method conflicts. The first is simply to -write a class method that overrides the conflicting role methods, perhaps -figuring out which role method to call. - -Alternately, if the role's methods are declared C<multi>, they can be -disambiguated based on their long name. If the roles forget to declare -them as multi, you can force a multi on the roles' methods by installing -a proto stub in the class being constructed: - - proto method shake {...} - -(This declaration need not precede the C<does> clause textually, since -roles are not actually composed until the end of the class definition, -at which point we know which roles are to be composed together -in a single logical operation, as well as how the class intends to -override the roles.) - -The proto method will be called if the multi fails: - - proto method shake { warn "They couldn't decide" } - -Run-time mixins are done with C<does> and C<but>. The C<does> binary -operator is a mutator that derives a new anonymous class (if necessary) -and binds the object to it: - - $fido does Sentry - -The C<does> infix operator is non-associative, so this is a syntax error: - - $fido does Sentry does Tricks does TailChasing does Scratch; - -You can, however, say - - $fido does Sentry; - $fido does Tricks; - $fido does TailChasing; - $fido does Scratch; - -And since it returns the left side, you can also say: - - ((($fido does Sentry) does Tricks) does TailChasing) does Scratch; - -Unlike the compile-time role composition, each of these layers on a new -mixin with a new level of inheritance, creating a new anonymous class -for dear old Fido, so that a C<.chase> method from C<TailChasing> hides a -C<.chase> method from C<Sentry>. - -You can also mixin a precomposed set of roles: - - $fido does (Sentry, Tricks, TailChasing, Scratch); - -This will level the playing field for collisions among the new -set of roles, and guarantees the creation of no more than one more -anonymous class. Such a role still can't conflict with itself, but it -can hide its previous methods in the parent class, and the calculation -of what conflicts is done again for the set of roles being mixed in. -If you can't do compile-time composition, we strongly recommend this -approach for run-time mixins since it approximates a compile-time -composition at least for the new roles involved. - -A role applied with C<does> may be parameterized with an initializer -in parentheses, but only if the role supplies exactly one attribute -to the mixin class: - - $fido does Wag($tail); - $line does taint($istainted); - -The supplied initializer will be coerced to type of the attribute. -Note that this initializer is in addition to any parametric type -supplied in square brackets, which is considered part of the actual -type name: - - $myobj does Array[:of(Int)](@initial) - -The C<but> operator creates a copy and works on that. It also knows -how to generalize a particular enumerated value to its role. So - - 0 but True - -is short for something like: - - 0 but Bool::True - -A property is defined by a role like this: - - role SomeRole { - has SomeType $.prop is rw = 1; - } - -You can declare a property with - - my int property answer; - -and that declares a role whose name is the same as the accessor: - - my role answer { - has int $.answer is rw; - } - -Then you can say - - $a = 0 but answer(42) - -Note that the parenthesized form is I<not> a subroutine or method call. -It's just special initializing syntax for roles that contain a single -property. The above really means something like: - - $a = ($anonymous = 0) does answer(42); - -which really means: - - (($anonymous = 0) does answer).answer = 42; - $a = $anonymous; - -Which is why there's a C<but> operator. - -=head1 Traits - -Traits are just properties (roles) applied to declared items like -containers or classes. It's the declaration of the item itself that -makes traits seem more permanent than ordinary properties. In addition -to adding the property, a trait can also have side effects. - -Traits are generally applied with the "is" keyword, though not always. -To define a trait handler for an "is xxx" trait, define one or -more multisubs into a property role like this: - - role xxx { - has Int $.xxx; - multi trait_auxiliary:is(xxx $trait, Class $container; $arg?) {...} - multi trait_auxiliary:is(xxx $trait, Any $container; $arg?) {...} - } - -Then it can function as a trait. A well-behaved trait handler will say - - $container does xxx($arg); - -somewhere inside to set the metadata on the container correctly. -Since a class can function as a role when it comes to parameter type -matching, you can also say: - - class MyBase { - multi trait_auxiliary:is(MyBase $base, Class $class; $arg?) {...} - multi trait_auxiliary:is(MyBase $tied, Any $container; $arg?) {...} - } - -These capture control if C<MyBase> wants to capture control of how it gets -used by any class or container. But usually you can just let it call -the generic defaults: - - multi trait_auxiliary:is(Class $base, Class $class; $arg?) {...} - -which adds C<$base> to the "isa" list of C<$class>, or - - multi trait_auxiliary:is(Class $tied, Any $container; $arg?) {...} - -which sets the "tie" type of the container to the implementation type -in C<$tied>. - -Most traits are introduced by use of a "helping verb", which could -be something like "C<is>", or "C<will>", or "C<can>", or "C<might>", -or "C<should>", or "C<does>". We call these helping verbs "trait -auxiliaries". Here's "C<will>", which (being syntactic sugar) merely -delegates to back to "is": - - multi sub trait_auxiliary:will($trait, $container; &arg) { - trait_auxiliary:is($trait, $container, &arg); - } - -Other traits are applied with a single word, and we call one of those a -"trait verb". For instance, the "C<as>" trait -is defined something like this: - - role as { - has ReturnType $.as; - multi sub trait_verb:as($container; ReturnType $arg) { - $container does as($arg); - } - ... - } - -Unlike compile-time roles, which all flatten out in the same class, -compile-time traits are applied one at a time, like mixin roles. -You can, in fact, apply a trait to a container at run time, but -if you do, it's just an ordinary mixin role. You have to call the -appropriate C<trait_auxiliary:is()> routine yourself if you want it to -do any extra shenanigans. The compiler won't call it for you at run -time like it would at compile time. - =head1 Delegation Delegation lets you pretend that some other object's methods are your own. Added: docs/Perl6/Spec/S14-roles-and-parametric-types.pod =================================================================== --- docs/Perl6/Spec/S14-roles-and-parametric-types.pod (rev 0) +++ docs/Perl6/Spec/S14-roles-and-parametric-types.pod 2009-02-23 23:33:42 UTC (rev 25503) @@ -0,0 +1,490 @@ +=encoding utf8 + +=head1 TITLE + +Synopsis 12: Roles and Parametric Types + +=head1 AUTHOR + +Larry Wall <la...@wall.org> + +=head1 VERSION + + Maintainer: Larry Wall <la...@wall.org> + Date: 24 Feb 2009, extracted from S12-objects.pod + Last Modified: 24 Feb 2009 + Number: 14 + Version: 1 + +=head1 Overview + +This synopsis summarizes Apocalypse 14, which discusses roles and parametric types. + +=head1 Roles + +Classes are primarily in charge of object management, and only +secondarily in charge of software reuse. In PerlĀ 6, roles take over +the job of managing software reuse. Depending on how you care to look +at it, a role is like a partial class, or an interface with default +implementation, or a set of generic methods and their associated data, +or a class closed at compile time. + +Roles may be composed into a class at compile time, in which case +you get automatic detection of conflicting methods. A role may also +be mixed into a class or object at run time to produce an anonymous +derived class with extra capabilities, but in this case conflicting +methods are overridden by the new role silently. In either case, +a class is necessary for instantiation--a role may not be directly +instantiated. + +A role is declared like a class, but with a C<role> keyword: + + role Pet { + method feed ($food) { + $food.open_can; + $food.put_in_bowl; + self.eat($food); + } + } + +A role may not inherit from a class, but may be composed of other +roles. However, this "crony" composition is not evaluated until +class composition time. This means that if two roles bring in the +same crony, there's no conflict--it's just as if the class pulled in +the crony role itself and the respective roles didn't. A role may +never conflict with itself regardless of its method of incorporation. +A role that brings in two conflicting crony roles I<may> resolve them +as if it were a class. This solution is accepted by the class unless +the class supplies its own solution. If two different roles resolve +the same crony conflict two different ways, those roles are themselves +in conflict and must be resolved by a "more derived" role or the class. + +A role doesn't know its own type until it is composed into a class. +Any mention of its main type (such as C<::?CLASS>) is generic, as is +any reference to C<self> or the type of the invocant. You can use +a role name as a type, but only for constraints, not for declaring +actual objects. (However, if you use a role as if it were a class, +an anonymous class is generated that composes the role, which provides +a way to force a role to test its crony composition for infelicities.) + +A role's main type is generic by default, but you can also parameterize +other types explicitly using type parameters: + + role Pet[::Petfood = TableScraps] { + method feed (Petfood $food) {...} + } + +(Note that in this case you must not use ::Petfood in the inner declaration, +or it would rebind the type to type of the actual food parameter.) + +If a role merely declares methods without defining them, it degenerates +to an interface: + + role Pet { + method feed ($food) {...} + method groom () {...} + method scratch (:$where) {...} + } + +Note that, while these methods must become available at class +composition time, they might be supplied by any of: another role, +the class itself, or some superclass. We know the methods that are +coming from the other roles or the class, but we don't necessarily know +the complete set of methods supplied by our super classes if they are +open or rely on wildcard delegation. However, the class composer is +allowed to assume that only currently declared superclass methods or +non-wildcard methods are going to be available. A stub can always +be installed somewhere to "supply" a missing method's declaration. + +Roles may have attributes: + + role Pet { + has $.collar = Collar.new(Tag.new); + method id () { return $.collar.tag } + method lose_collar () { undefine $.collar } + } + +If you want to parameterize the initial value of a role attribute, +be sure to put a double semicolon if you don't want the parameter to +be considered part of the long name: + + role Pet[::ID;; $tag] { + has ID $.collar .= new($tag); + } + +Within a role the C<has> declarator always indicates the declaration +from the viewpoint of the class. Therefore a private attribute declared +using C<has> is private to the class, not to the role. You may wish to declare an attribute +that is hidden even from the class; a completely private role +attribute may be declared like this: + + my $!spleen; + +The name of such a private attribute is always considered lexically scoped. +If a role declares private lexical items, those items are private to +the role due to the nature of lexical scoping. Accessors to such +items may be exported to the class, but this is not the default. +In particular, a role may say + + trusts ::?Class; + +to allow C<self!attr()> access to the role's C<$!attr> variables with the +class or from other roles composed into the class. Conflicts between +private accessors are also caught at composition time, but of course +need not consider super classes, since no-one outside the current +class (or a trusted class) can call a private accessor at all. +(Private accessors are never virtual, and must be package qualified +if called from a trusted scope other than our own. That is, it's +either C<self!attr()> or C<$obj!TrustsMe::attr().>) + +A role may also distinguish a shared method + + has method foo ... + method foo ... # same + +from a nonshared private method: + + my method !foo ... + my method foo ... # same, but &foo is aliased to &!foo + +Generally you'd just use a lexically scoped sub, though. + + my sub foo ... + +[Conjectural: To put a private sub into the class, say + + our sub !foo ... + +] + +A role can abstract the decision to delegate: + + role Pet { + has $groomer handles <bathe groom trim> = hire_groomer(); + } + +Note that this puts the three methods into the class as well as +C<$groomer>. In contrast, "C<my $!groomer>" would only put the +three methods; the attribute itself is private to the role. + +A role is allowed to declare an additional inheritance for its class when +that is considered an implementation detail: + + role Pet { + is Friend; + } + +A class incorporates a role with the verb "does", like this: + + class Dog is Mammal does Pet does Sentry {...} + +or equivalently, within the body of the class closure: + + class Dog { + is Mammal; + does Pet; + does Sentry; + ... + } + +There is no ordering dependency among the roles. + +A class's explicit method definition hides any role definition of +the same name. A role method in turn hides any methods inherited +from other classes. + +If there are no method name conflicts between roles (or with the +class), then each role's methods can be installed in the class. If, +however, two roles try to introduce a method of the same name the +composition of the class fails. (Two C<has> attributes of the same +name, whether public or private, are simply merged into one slot, +provided the types are the same; otherwise, the composition fails. +Role-private attributes are not merged, and from the viewpoint of +the composition, don't even exist, except to allocate a slot for each +such attribute.) + +There are several ways to solve method conflicts. The first is simply to +write a class method that overrides the conflicting role methods, perhaps +figuring out which role method to call. + +Alternately, if the role's methods are declared C<multi>, they can be +disambiguated based on their long name. If the roles forget to declare +them as multi, you can force a multi on the roles' methods by installing +a proto stub in the class being constructed: + + proto method shake {...} + +(This declaration need not precede the C<does> clause textually, since +roles are not actually composed until the end of the class definition, +at which point we know which roles are to be composed together +in a single logical operation, as well as how the class intends to +override the roles.) + +The proto method will be called if the multi fails: + + proto method shake { warn "They couldn't decide" } + +Run-time mixins are done with C<does> and C<but>. The C<does> binary +operator is a mutator that derives a new anonymous class (if necessary) +and binds the object to it: + + $fido does Sentry + +The C<does> infix operator is non-associative, so this is a syntax error: + + $fido does Sentry does Tricks does TailChasing does Scratch; + +You can, however, say + + $fido does Sentry; + $fido does Tricks; + $fido does TailChasing; + $fido does Scratch; + +And since it returns the left side, you can also say: + + ((($fido does Sentry) does Tricks) does TailChasing) does Scratch; + +Unlike the compile-time role composition, each of these layers on a new +mixin with a new level of inheritance, creating a new anonymous class +for dear old Fido, so that a C<.chase> method from C<TailChasing> hides a +C<.chase> method from C<Sentry>. + +You can also mixin a precomposed set of roles: + + $fido does (Sentry, Tricks, TailChasing, Scratch); + +This will level the playing field for collisions among the new +set of roles, and guarantees the creation of no more than one more +anonymous class. Such a role still can't conflict with itself, but it +can hide its previous methods in the parent class, and the calculation +of what conflicts is done again for the set of roles being mixed in. +If you can't do compile-time composition, we strongly recommend this +approach for run-time mixins since it approximates a compile-time +composition at least for the new roles involved. + +A role applied with C<does> may be parameterized with an initializer +in parentheses, but only if the role supplies exactly one attribute +to the mixin class: + + $fido does Wag($tail); + $line does taint($istainted); + +The supplied initializer will be coerced to type of the attribute. +Note that this initializer is in addition to any parametric type +supplied in square brackets, which is considered part of the actual +type name: + + $myobj does Array[:of(Int)](@initial) + +The C<but> operator creates a copy and works on that. It also knows +how to generalize a particular enumerated value to its role. So + + 0 but True + +is short for something like: + + 0 but Bool::True + +A property is defined by a role like this: + + role SomeRole { + has SomeType $.prop is rw = 1; + } + +You can declare a property with + + my int property answer; + +and that declares a role whose name is the same as the accessor: + + my role answer { + has int $.answer is rw; + } + +Then you can say + + $a = 0 but answer(42) + +Note that the parenthesized form is I<not> a subroutine or method call. +It's just special initializing syntax for roles that contain a single +property. The above really means something like: + + $a = ($anonymous = 0) does answer(42); + +which really means: + + (($anonymous = 0) does answer).answer = 42; + $a = $anonymous; + +Which is why there's a C<but> operator. + +=head1 Traits + +Traits are just properties (roles) applied to declared items like +containers or classes. It's the declaration of the item itself that +makes traits seem more permanent than ordinary properties. In addition +to adding the property, a trait can also have side effects. + +Traits are generally applied with the "is" keyword, though not always. +To define a trait handler for an "is xxx" trait, define one or +more multisubs into a property role like this: + + role xxx { + has Int $.xxx; + multi trait_auxiliary:is(xxx $trait, Class $container; $arg?) {...} + multi trait_auxiliary:is(xxx $trait, Any $container; $arg?) {...} + } + +Then it can function as a trait. A well-behaved trait handler will say + + $container does xxx($arg); + +somewhere inside to set the metadata on the container correctly. +Since a class can function as a role when it comes to parameter type +matching, you can also say: + + class MyBase { + multi trait_auxiliary:is(MyBase $base, Class $class; $arg?) {...} + multi trait_auxiliary:is(MyBase $tied, Any $container; $arg?) {...} + } + +These capture control if C<MyBase> wants to capture control of how it gets +used by any class or container. But usually you can just let it call +the generic defaults: + + multi trait_auxiliary:is(Class $base, Class $class; $arg?) {...} + +which adds C<$base> to the "isa" list of C<$class>, or + + multi trait_auxiliary:is(Class $tied, Any $container; $arg?) {...} + +which sets the "tie" type of the container to the implementation type +in C<$tied>. + +Most traits are introduced by use of a "helping verb", which could +be something like "C<is>", or "C<will>", or "C<can>", or "C<might>", +or "C<should>", or "C<does>". We call these helping verbs "trait +auxiliaries". Here's "C<will>", which (being syntactic sugar) merely +delegates to back to "is": + + multi sub trait_auxiliary:will($trait, $container; &arg) { + trait_auxiliary:is($trait, $container, &arg); + } + +Other traits are applied with a single word, and we call one of those a +"trait verb". For instance, the "C<as>" trait +is defined something like this: + + role as { + has ReturnType $.as; + multi sub trait_verb:as($container; ReturnType $arg) { + $container does as($arg); + } + ... + } + +Unlike compile-time roles, which all flatten out in the same class, +compile-time traits are applied one at a time, like mixin roles. +You can, in fact, apply a trait to a container at run time, but +if you do, it's just an ordinary mixin role. You have to call the +appropriate C<trait_auxiliary:is()> routine yourself if you want it to +do any extra shenanigans. The compiler won't call it for you at run +time like it would at compile time. + +=head1 Parametric Types + +Types can take parameters. Parametric types are named using square brackets, so: + + my Hash of Array of Recipe %book; + +actually means: + + my Hash[of => Array[of => Recipe]] %book; + +=head2 Parametric Roles + +In Perl 6, roles can also take parameters. Roles exist to enable greater re-use of code +than we could get through having plain old classes, and by allowing them to be +parameterized we open the door to even more re-use. Taking a simple example, imagine we +wanted to factor out a "greet" method into a role, which takes somebody's name and greets +them. We want to parameterize it on the greeting. + +role Greet[Str $greeting] { + method greet() { say "$greeting!"; } +} +class EnglishMan does Greet["Hello"] { } +class Slovak does Greet["Ahoj"] { } +class Lolcat does Greet["OH HAI"] { } +EnglishMan.new.greet(); # Hello +Slovak.new.greet(); # Ahoj +Lolcat.new.greet(); # OH HAI + +Similarly, we could do a role for requests. + +role Request[Str $statement] { + method request($object) { say "$statement $object?"; } +} +class EnglishMan does Request["Please can I have a"] { } +class Slovak does Request["Prosim si"] { } +class Lolcat does Request["I CAN HAZ"] { } +EnglishMan.new.request("yorkshire pudding"); +Slovak.new.request("borovicka"); +Lolcat.new.request("CHEEZEBURGER"); + +Sadly, the Slovak output sucks here. Borovicka is the nominative form of the word, and we +need to decline it into the accusative case. But some languages don't care about that, and +we don't want to have to make them all supply a transform. Thankfully, you can write many +roles with the same short name, and a different signature, and multi-dispatch will pick +the right one for you. So we write something to produce the accusative case in Slovak and +pass it in. Here's the new code. + +role Request[Str $statement] { + method request($object) { say "$statement $object?"; } +} +role Request[Str $statement, &transform] { + method request($object) { + say "$statement " ~ transform($object) ~ "?"; + } +} +module Language::Slovak { + sub accusative($nom) { + # ...and before some smartass points it out, I know + # I'm missing some of the masculine animate declension... + return $nom.subst(/a$/, 'u'); + } +} +class EnglishMan does Request["Please can I have a"] { } +class Slovak does Request["Prosim si", &Language::Slovak::accusative] { } +class Lolcat does Request["I CAN HAZ"] { } +EnglishMan.new.request("yorkshire pudding"); +Slovak.new.request("borovicka"); +Lolcat.new.request("CHEEZEBURGER"); + +Which means we can now properly order our borovicka in Slovakia, which is awesome. Until +you do it in a loop and find the Headache['very bad'] role got mixed into yourself +overnight, anyway... + +Role attributes can also be used to initialise attributes: + + role AttrParams[$a, $b] { + has $.x = $a; + has $.y = $b; + } + +...and to constrain types. + + role TypeParams[::T] { + method x(T $x) { return "got a " ~ T ~ " it was $x" } + } + + class IntShower does TypeParams[Int] { } # Shows Ints + class StrShower does TypeParams[Str] { } # Shows Strs + + print IntShower.new.x(42); # Prints 'got a Int it was 42' + print StrShower.new.x("OH HAI"); # Prints 'got a Str it was OH HAI' + print IntShower.new.x("OH HAI"); # Dies + + + +=for vim:set expandtab sw=4: Modified: docs/Perl6/Spec/S29-functions.pod =================================================================== --- docs/Perl6/Spec/S29-functions.pod 2009-02-23 21:06:52 UTC (rev 25502) +++ docs/Perl6/Spec/S29-functions.pod 2009-02-23 23:33:42 UTC (rev 25503) @@ -684,67 +684,67 @@ =over -=item all -- see S32-setting-library/Container.pod +=item all -- see S32-setting-library/Containers.pod -=item any -- see S32-setting-library/Container.pod +=item any -- see S32-setting-library/Containers.pod -=item cat -- see S32-setting-library/Container.pod +=item cat -- see S32-setting-library/Containers.pod -=item classify -- see S32-setting-library/Container.pod +=item classify -- see S32-setting-library/Containers.pod =item defined -- see S32-setting-library/Scalars.pod -=item grep -- see S32-setting-library/Container.pod +=item grep -- see S32-setting-library/Containers.pod -=item first -- see S32-setting-library/Container.pod +=item first -- see S32-setting-library/Containers.pod -=item keys -- see S32-setting-library/Container.pod +=item keys -- see S32-setting-library/Containers.pod -=item kv -- see S32-setting-library/Container.pod +=item kv -- see S32-setting-library/Containers.pod -=item join -- see S32-setting-library/Container.pod +=item join -- see S32-setting-library/Containers.pod -=item map -- see S32-setting-library/Container.pod +=item map -- see S32-setting-library/Containers.pod -=item max -- see S32-setting-library/Container.pod +=item max -- see S32-setting-library/Containers.pod -=item min -- see S32-setting-library/Container.pod +=item min -- see S32-setting-library/Containers.pod -=item none -- see S32-setting-library/Container.pod +=item none -- see S32-setting-library/Containers.pod -=item one -- see S32-setting-library/Container.pod +=item one -- see S32-setting-library/Containers.pod -=item pairs -- see S32-setting-library/Container.pod +=item pairs -- see S32-setting-library/Containers.pod =item print -- see S32-setting-library/IO.pod =item printf -- see S32-setting-library/IO.pod -=item roundrobin -- see S32-setting-library/Container.pod +=item roundrobin -- see S32-setting-library/Containers.pod -=item pick -- see S32-setting-library/Container.pod +=item pick -- see S32-setting-library/Containers.pod -=item reduce -- see S32-setting-library/Container.pod +=item reduce -- see S32-setting-library/Containers.pod -=item reverse -- see S32-setting-library/Container.pod +=item reverse -- see S32-setting-library/Containers.pod =item say -- see S32-setting-library/IO.pod -=item shape -- see S32-setting-library/Container.pod +=item shape -- see S32-setting-library/Containers.pod -=item sort -- see S32-setting-library/Container.pod +=item sort -- see S32-setting-library/Containers.pod -=item srand -- see S32-setting-library/Container.pod +=item srand -- see S32-setting-library/Numeric.pod =item undefine -- see S32-setting-library/Scalar.pod =item uri -- see S32-setting-library/IO.pod -=item values -- see S32-setting-library/Container.pod +=item values -- see S32-setting-library/Containers.pod =item warn -- see S32-setting-library/Any.pod -=item zip -- see S32-setting-library/Container.pod +=item zip -- see S32-setting-library/Containers.pod =back