Author: jnthn Date: 2009-04-19 16:22:42 +0200 (Sun, 19 Apr 2009) New Revision: 26277
Modified: docs/Perl6/Spec/S14-roles-and-parametric-types.pod Log: [spec] Tidying up, removing duplication and filling out S14. Modified: docs/Perl6/Spec/S14-roles-and-parametric-types.pod =================================================================== --- docs/Perl6/Spec/S14-roles-and-parametric-types.pod 2009-04-19 14:16:52 UTC (rev 26276) +++ docs/Perl6/Spec/S14-roles-and-parametric-types.pod 2009-04-19 14:22:42 UTC (rev 26277) @@ -11,13 +11,14 @@ Contributions: Tim Nelson <wayl...@wayland.id.au> Jonathan Worthington <jn...@jnthn.net> Date: 24 Feb 2009, extracted from S12-objects.pod - Last Modified: 24 Feb 2009 + Last Modified: 19 Apr 2009 Number: 14 - Version: 1 + Version: 2 =head1 Overview -This synopsis summarizes Apocalypse 14, which discusses roles and parametric types. +This synopsis discusses roles and parametric types, which were +originally discussed in A12. =head1 Roles @@ -66,16 +67,6 @@ 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: @@ -103,19 +94,12 @@ 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: +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 (that will exist per instance of the class) may +be declared like this: my $!spleen; @@ -274,7 +258,7 @@ supplied in square brackets, which is considered part of the actual type name: - $myobj does Array[:of(Int)](@initial) + $myobj does Array[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 @@ -287,20 +271,10 @@ A property is defined by a role like this: - role SomeRole { - has SomeType $.prop is rw = 1; + role answer { + has Int $.answer 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) @@ -390,25 +364,32 @@ 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 +=head1 Parametric Roles -Types can take parameters. Parametric types are named using square brackets, so: +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 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); + } - my Hash of Array of Recipe %book; +You don't just have to parameterize on types; any value is fine. Imagine +we wanted to factor out a "greet" method into a role, which takes +somebody's name and greets them. We can parameterize it on the greeting. -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!"; } } @@ -431,12 +412,13 @@ 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. +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 (it is the exact same dispatch algorithm used +by multi-subs). So we can write: role Request[Str $statement] { method request($object) { say "$statement $object?"; } @@ -460,30 +442,76 @@ 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... +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: +=head2 Relationship Between of And Types + +The of keyword is just syntactic sugar for providing a single +parameter to a parametric type. Thus: + + my Array of Recipe %book; + +Actually means: + + my Array[Recipe] %book; + +This can be nested, so: + + my Hash of Array of Recipe @library; + +Is just: + + my Hash[Array[Recipe]] @library; + +Therefore: + + my Array @array; + +Means an Array of Array (actually, a Positional of Array). + +=head2 Parametric Subtyping + +If you have two types in a subtyping relationship such that T1 is +narrower than T2, then also the roles: + + role R[::T] { } + role R[::T1, ::T2] { } + +Will act such that R[T1] is narrower than R[T2]. This extends to multiple +parameters, however they must all be narrower or the same (this is unlike +in multiple dispatch where you can have one narrower and the rest narrower +or tied). That is, assuming we have some unrelated type T3, then R[T2, T1] +is narrower than R[T1,T1] but R[T2,T1] is not narrower than R[T1,T3]. + +Nesting follows naturally from this definition, so a role R[R[T2]] is +narrower than a role R[R[T1]]. + +This all means that, for example, if you have a sub: + + sub f(Num @arr) { ... } + +Then you can also call it with an array of Int. + + my Int @a = 1,2,3; + f(@a); + +=head2 Interaction of typed and untyped data structures + +Certainly so far as Perl 6.0.0 goes, only types that have been declared +on a container count in the type check. That is, if we have a sub: + + sub f(Int @arr) { ... } + +And call it with any of: + + f([1,2,3]); + my @a = 1,2,3; + f(@a); + +Then neither of these calls will work. The type check is based on the +declared type of the array, and the content is unknown to the type +checker. - 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: