@@ -617,42 +617,42 @@ describing only parts of an object's behavior and in how roles are applied
617
617
to classes. Or to phrase it differently, classes are meant for managing
618
618
objects and roles are meant for managing behavior and code reuse.
619
619
620
- = begin code :allow<B L>
620
+ = begin code
621
621
use MONKEY-SEE-NO-EVAL;
622
622
role Serializable {
623
623
method serialize() {
624
- self.L < perl > ; # very primitive serialization
624
+ self.perl; # very primitive serialization
625
625
}
626
626
method deserialize($buf) {
627
- L < EVAL|/routine/EVAL > $buf; # reverse operation to .perl
627
+ EVAL $buf; # reverse operation to .perl
628
628
}
629
629
}
630
630
631
- class Point B < does > Serializable {
631
+ class Point does Serializable {
632
632
has $.x;
633
633
has $.y;
634
634
}
635
635
my $p = Point.new(:x(1), :y(2));
636
- my $serialized = $p.serialize; # method provided by the role
636
+ my $serialized = $p.serialize;
637
637
my $clone-of-p = Point.deserialize($serialized);
638
- say $clone-of-p.x; # OUTPUT: «1»
638
+ say $clone-of-p.x; # OUTPUT: «1»
639
639
= end code
640
640
641
641
Roles are immutable as soon as the compiler parses the closing curly brace of
642
642
the role declaration.
643
643
644
- = head2 Z <> Role Application
644
+ = head2 Role Application
645
645
646
646
Role application differs significantly from class inheritance. When a role
647
647
is applied to a class, the methods of that role are copied into the class.
648
- If multiple roles are applied to the same class, conflicts (e.g. attributes or non-multi
649
- methods of the same name) cause a compile-time error, which can be solved by
650
- providing a method of the same name in the class.
648
+ If multiple roles are applied to the same class, conflicts (e.g.
649
+ attributes or non-multi methods of the same name) cause a compile-time error,
650
+ which can be solved by providing a method of the same name in the class.
651
651
652
652
This is much safer than multiple inheritance, where conflicts are never
653
653
detected by the compiler, but are instead resolved to the superclass
654
- that appears earlier in the method resolution order, which might not be what the
655
- programmer wanted.
654
+ that appears earlier in the method resolution order, which might not
655
+ be what the programmer wanted.
656
656
657
657
For example, if you've discovered an efficient method to ride cows, and are
658
658
trying to market it as a new form of popular transportation, you might have
@@ -674,10 +674,11 @@ C<Automobile>, for things that you can drive.
674
674
class Taurus is Bull is Automobile { }
675
675
676
676
my $t = Taurus.new;
677
- $t.steer; # OUTPUT: «Castrates $t»
677
+ say $t.steer;
678
+ # OUTPUT: «Taurus.new(castrated => Bool::True, direction => Any)»
678
679
679
680
With this setup, your poor customers will find themselves unable to turn
680
- their Taurus and you won't be able to make more of your product! In this
681
+ their Taurus and you won't be able to make more of your product! In this
681
682
case, it may have been better to use roles:
682
683
683
684
= begin code :skip-test
@@ -708,7 +709,7 @@ This code will die with something like:
708
709
709
710
This check will save you a lot of headaches:
710
711
711
- = begin code :skip-test
712
+ = begin code :preamble<role Bull-Like{}; role Steerable{};>
712
713
class Taurus does Bull-Like does Steerable {
713
714
method steer($direction?) {
714
715
self.Steerable::steer($direction?)
@@ -740,19 +741,21 @@ produces the same class C<C> as
740
741
741
742
= head2 Stubs
742
743
743
- When a role contains a stubbed method, a non-stubbed version of a method of
744
- the same name must be supplied at the time the role is applied to a class.
745
- This allows you to create roles that act as abstract interfaces.
744
+ When a role contains a L < stubbed|/routine/... > method,
745
+ a non-stubbed version of a method of the same name must be supplied
746
+ at the time the role is applied to a class. This allows you to
747
+ create roles that act as abstract interfaces.
746
748
747
- = begin code :allow<B L> : skip-test
749
+ = begin code :skip-test
748
750
role AbstractSerializable {
749
- method serialize() { B < L < ... > > } # literal ... here marks the
751
+ method serialize() { ... } # literal ... here marks the
750
752
# method as a stub
751
753
}
752
754
753
755
# the following is a compile time error, for example
754
756
# Method 'serialize' must be implemented by Point because
755
757
# it's required by a role
758
+
756
759
class APoint does AbstractSerializable {
757
760
has $.x;
758
761
has $.y;
@@ -772,103 +775,120 @@ role.
772
775
= head2 Inheritance
773
776
774
777
Roles cannot inherit from classes, but they may cause any class which does
775
- that role to inherit from another class. So if you write:
778
+ that role to inherit from another class. So if you write:
776
779
777
780
= begin code
778
781
role A is Exception { }
779
782
class X::Ouch does A { }
780
783
X::Ouch.^parents.say # OUTPUT: «((Exception))»
781
784
= end code
782
785
783
- ... then C < X::Ouch > will inherit directly from Exception, as we can see above
786
+ then C < X::Ouch > will inherit directly from Exception, as we can see above
784
787
by listing its parents.
785
788
786
789
= head2 Pecking order
787
790
788
791
A method defined directly in a class will always override definitions from
789
- applied roles or from inherited classes. If no such definition exists, methods
790
- from roles override methods inherited from classes. This happens both when
792
+ applied roles or from inherited classes. If no such definition exists, methods
793
+ from roles override methods inherited from classes. This happens both when
791
794
said class was brought in by a role, and also when said class was inherited
792
795
directly.
793
796
794
- Note that each candidate for a multi-method is its own method... in this case,
797
+ role M {
798
+ method f { say "I am in role M" }
799
+ }
800
+
801
+ class A {
802
+ method f { say "I am in class A" }
803
+ }
804
+
805
+ class B is A does M {
806
+ method f { say "I am in class B" }
807
+ }
808
+
809
+ class C is A does M { }
810
+
811
+ B.new.f; # OUTPUT «I am in class B»
812
+ C.new.f; # OUTPUT «I am in role M»
813
+
814
+ Note that each candidate for a multi-method is its own method. In this case,
795
815
the above only applies if two such candidates have the same signature.
796
816
Otherwise, there is no conflict, and the candidate is just added to the
797
817
multi-method.
798
818
799
819
= head2 Automatic Role Punning
800
820
801
- Any attempt to directly instantiate a role, as well as many other operations on it,
802
- will automatically create an instance of a class with the same name as the role, making it
803
- possible to transparently use a role as if it were a class.
821
+ Any attempt to directly instantiate a role, as well as many other operations
822
+ on it, will automatically create an instance of a class with the same name as
823
+ the role, making it possible to transparently use a role as if it were a class.
804
824
805
- = begin code
806
- role Point {
807
- has $.x;
808
- has $.y;
809
- method abs { sqrt($.x * $.x + $.y * $.y) }
810
- }
811
- say Point.new(x => 6, y => 8).abs;
812
- = end code
825
+ = begin code
826
+ role Point {
827
+ has $.x;
828
+ has $.y;
829
+ method abs { sqrt($.x * $.x + $.y * $.y) }
830
+ }
831
+ say Point.new(x => 6, y => 8).abs; # OUTPUT «10»
832
+ = end code
813
833
814
834
We call this automatic creation of classes I < punning > , and the generated class
815
835
a I < pun > .
816
836
817
837
= head2 Parameterized Roles
838
+
818
839
X < |Parameterized Roles >
819
- Roles can be parameterized, by giving them a signature in square brackets:
820
840
821
- = begin code
822
- role BinaryTree[::Type] {
823
- has BinaryTree[Type] $.left;
824
- has BinaryTree[Type] $.right;
825
- has Type $.node;
841
+ Roles can be parameterized, by giving them a signature in square brackets:
826
842
827
- method visit-preorder(&cb) {
828
- cb $.node;
829
- for $.left, $.right -> $branch {
830
- $branch.visit-preorder(&cb) if defined $branch;
843
+ = begin code
844
+ role BinaryTree[::Type] {
845
+ has BinaryTree[Type] $.left;
846
+ has BinaryTree[Type] $.right;
847
+ has Type $.node;
848
+
849
+ method visit-preorder(&cb) {
850
+ cb $.node;
851
+ for $.left, $.right -> $branch {
852
+ $branch.visit-preorder(&cb) if defined $branch;
853
+ }
831
854
}
832
- }
833
- method visit-postorder(&cb) {
834
- for $.left, $.right -> $branch {
835
- $branch.visit-postorder(&cb) if defined $branch;
855
+ method visit-postorder(&cb) {
856
+ for $.left, $.right -> $branch {
857
+ $branch.visit-postorder(&cb) if defined $branch;
858
+ }
859
+ cb $.node;
860
+ }
861
+ method new-from-list(::?CLASS:U: *@el) {
862
+ my $middle-index = @el.elems div 2;
863
+ my @left = @el[0 .. $middle-index - 1];
864
+ my $middle = @el[$middle-index];
865
+ my @right = @el[$middle-index + 1 .. *];
866
+ self.new(
867
+ node => $middle,
868
+ left => @left ?? self.new-from-list(@left) !! self,
869
+ right => @right ?? self.new-from-list(@right) !! self,
870
+ );
836
871
}
837
- cb $.node;
838
- }
839
- method new-from-list(::?CLASS:U: *@el) {
840
- my $middle-index = @el.elems div 2;
841
- my @left = @el[0 .. $middle-index - 1];
842
- my $middle = @el[$middle-index];
843
- my @right = @el[$middle-index + 1 .. *];
844
- self.new(
845
- node => $middle,
846
- left => @left ?? self.new-from-list(@left) !! self,
847
- right => @right ?? self.new-from-list(@right) !! self,
848
- );
849
872
}
850
- }
851
873
852
- my $t = BinaryTree[Int].new-from-list(4, 5, 6);
853
- $t.visit-preorder(&say); # OUTPUT: «546»
854
- $t.visit-postorder(&say); # OUTPUT: «465»
855
- = end code
874
+ my $t = BinaryTree[Int].new-from-list(4, 5, 6);
875
+ $t.visit-preorder(&say); # OUTPUT: «546»
876
+ $t.visit-postorder(&say); # OUTPUT: «465»
877
+ = end code
856
878
857
879
Here the signature consists only of a type capture, but any signature will do:
858
880
859
- = begin code
860
- use v6.c;
861
-
862
- enum Severity <debug info warn error critical>;
881
+ = begin code
882
+ enum Severity <debug info warn error critical>;
863
883
864
- role Logging[$filehandle = $*ERR] {
865
- method log(Severity $sev, $message) {
866
- $filehandle.print("[{uc $sev}] $message\n");
884
+ role Logging[$filehandle = $*ERR] {
885
+ method log(Severity $sev, $message) {
886
+ $filehandle.print("[{uc $sev}] $message\n");
887
+ }
867
888
}
868
- }
869
889
870
- Logging[$*OUT].log(debug, 'here we go'); # OUTPUT: «[DEBUG] here we go»
871
- = end code
890
+ Logging[$*OUT].log(debug, 'here we go'); # OUTPUT: «[DEBUG] here we go»
891
+ = end code
872
892
873
893
You can have multiple roles of the same name, but with different signatures;
874
894
the normal rules of multi dispatch apply for choosing multi candidates.
@@ -924,8 +944,8 @@ objects; those objects are called I<meta objects>. Meta objects are, like
924
944
ordinary objects, instances of classes, in this case we call them I < meta
925
945
classes > .
926
946
927
- For each object or class you can get the meta object by calling C < .HOW > on
928
- it. Note that although this looks like a method call, it works more like a macro.
947
+ For each object or class you can get the meta object by calling C < .HOW > on it.
948
+ Note that although this looks like a method call, it works more like a macro.
929
949
930
950
So, what can you do with the meta object? For one you can check if two
931
951
objects have the same meta class by comparing them for equality:
0 commit comments