Synopsis 12: Objects
Larry Wall <larry@wall.org>
Maintainer: Larry Wall <larry@wall.org> Date: 27 Oct 2004 Last Modified: 23 Feb 2008 Number: 12 Version: 58
This synopsis summarizes Apocalypse 12, which discusses object-oriented programming.
From t/spec/S12-class/basic.t lines 13–40 (no results): (skip)
| # L<S12/Classes>
|
| class Foo {}
|
|
|
| my $foo = Foo.new();
|
| ok($foo ~~ Foo, '... smartmatch our $foo to the Foo class');
|
|
|
| # note that S12 says that .isa() should be called on metaclasses.
|
| # However, making it an object .isa() means that classes are free to
|
| # override the behaviour without playing with the metamodel via traits
|
| ok($foo.isa(Foo), '.isa(Foo)');
|
| #?rakudo 1 skip 'parse error'
|
| ok($foo.isa(::Foo), '.isa(::Foo)');
|
| ok($foo.isa("Foo"), '.isa("Foo")');
|
| ok(!$foo.isa("Bar"), '!.isa("Bar")');
|
|
|
| my $foo_clone = $foo.clone();
|
| ok($foo_clone ~~ Foo, '... smartmatch our $foo_clone to the Foo class');
|
|
|
| class Foo::Bar {}
|
|
|
| my $foo_bar = Foo::Bar.new();
|
| ok($foo_bar ~~ Foo::Bar, '... smartmatch our $foo_bar to the Foo::Bar class');
|
|
|
| ok($foo_bar.isa(Foo::Bar), '.isa(Foo::Bar)');
|
| #?rakudo 1 skip 'parse error'
|
| ok(!$foo_bar.isa(::Foo), '!Foo::Bar.new.isa(::Foo)');
|
|
|
|
|
A class is a module declared with the class keyword. As with modules, the public storage, interface, and name of the class is represented by a package and its name, which is usually (but not necessarily) a global name.
Taken as an object, a class represents all of the possible values of its type, and the class object can thus be used as a proxy for any "real" object of that type in calculating what a generic object of that type can do. The class object is an Object, but it is not a Class, because there is no mandatory Class class in Perl 6. We wish to support both class-based and prototype-based OO programming. So all metaprogramming is done through the current object's HOW object, which can delegate metaprogramming to any metamodel it likes. However, by default, objects derived from Object support a fairly standard class-based model.
There are two basic class declaration syntaxes:
class Foo; # rest of file is class definition
...
class Bar {...} # block is class definition
The first form is allowed only as the first declaration in a compilation unit (that is, file or eval string).
In either case, the code represented by ... executes at compile time as the body of a method of the metaclass, which is responsible for interpreting the keywords of the class definition. (And since a class is also a module, it also handles any module-oriented keywords. You can export subs from a class at "use" time, for instance.)
A named class declaration can occur as part of an expression, just like named subroutine declarations.
Classes are primarily for instance management, not code reuse. Consider using roles when you simply want to factor out common code.
Perl 6 supports multiple inheritance, anonymous classes, and autoboxing.
All public method calls are "virtual" in the C++ sense. More surprisingly, any class name mentioned in a method is also considered virtual, that is, polymorphic on the actual type of the object.
You may derive from any built-in type, but the derivation of a low-level type like int may only add behaviors, not change the representation. Use composition and/or delegation to change the representation.
Since there are no barewords in Perl 6, bare class names must be predeclared. You can predeclare a stub class and fill it in later just as you would a subroutine. Alternately, you can define a local class or type variable using the :: type sigil. In an rvalue context the :: prefix is a no-op, but in a declarational context, it binds a new type name within its declared scope.
Without a my or other scoping declarator, a bare class declarator declares an our declarator, that is, a name within the current package. Since class files begin parsing in the * (GLOBAL) package, the first class declaration in the file installs itself as a global name, and subsequent declarations then install themselves into the current class rather than the global package.
Hence, to declare an inner class in the current package (or module, or class), use our class or just class. To declare a lexically scoped class, use my class. Class names are always searched for from innermost scopes to outermost. As with an initial ::, the presence of a :: within the name does not imply globalness (unlike in Perl 5). So the outward search can look in children of the searched namespaces.
Class traits are set using is:
class MyStruct is rw {...}
An "isa" is just a trait that happens to be another class:
From t/spec/S12-class/basic.t lines 41–67 (no results): (skip)
| # L<S12/Classes/An isa is just a trait that happens to be another class>
|
| class Bar is Foo {}
|
|
|
| ok(Bar ~~ Foo, '... smartmatch our Bar to the Foo class');
|
|
|
| my $bar = Bar.new();
|
| ok($bar ~~ Bar, '... smartmatch our $bar to the Bar class');
|
| ok($bar.isa(Bar), "... .isa(Bar)");
|
| ok($bar ~~ Foo, '... smartmatch our $bar to the Foo class');
|
| ok($bar.isa(Foo), "new Bar .isa(Foo)");
|
|
|
| my $bar_clone = $bar.clone();
|
| ok($bar_clone ~~ Bar, '... smartmatch our $bar_clone to the Bar class');
|
| ok($bar_clone.isa(Bar), "... .isa(Bar)");
|
| ok($bar_clone ~~ Foo, '... smartmatch our $bar_clone to the Foo class');
|
| ok($bar_clone.isa(Foo), "... .isa(Foo)");
|
|
|
|
|
| # Same, but with the "is Foo" declaration inlined
|
| #?rakudo 5 skip 'not parsing is inside class yet'
|
| #?DOES 3
|
| class Baz { is Foo }
|
| ok(Baz ~~ Foo, '... smartmatch our Baz to the Foo class');
|
| my $baz = Baz.new();
|
| ok($baz ~~ Baz, '... smartmatch our $baz to the Baz class');
|
| ok($baz.isa(Baz), "... .isa(Baz)");
|
|
|
class Dog is Mammal {...}
MI is specified with multiple is modifiers:
class Dog is Mammal is Pet {...}
Roles use does instead of is:
class Dog is Mammal does Pet {...}
You may put these inside as well:
class Dog {
is Mammal;
does Pet;
...
}
Every object (including any class object) delegates to an instance of its metaclass. You can get at the metaclass of any object via the HOW method. A "class" object is just considered an "empty" instance in Perl 6, more properly called a "prototype" object, or just "protoobject". The actual class object is the metaclass object pointed to by the HOW syntax. So when you say "Dog", you're referring to both a package and a protoobject, that latter of which points to the actual object representing the class via HOW. The protoobject differs from an instance object not by having a different type but rather in the extent to which it is defined. Some objects may tell you that they are defined, while others may tell you that they are undefined. That's up to the object, and depends on how the metaclass chooses to dispatch the .defined method.
The notation ^Dog is syntactic sugar for Dog.HOW, so ^ can be considered the "class" sigil when you want to talk about the current metaclass instance.
Classes are open and non-final by default, but may easily be closed or finalized not by themselves but by the entire application, provided nobody issued an explicit compile-time request that the class stay open or non-final. (Or a site policy could close any applications that use the policy.) Platforms that do dynamic loading of sub-applications probably don't want to close or finalize classes wholesale, however.
Roles take on some of the compile-time function of closed classes, so you should probably use those instead anyway.
A private class can be declared using my; most privacy issues are handled with lexical scoping in Perl 6. The fact that importation is lexical by default also means that any names your class imports are also private by default.
Methods are routines declared in a class with the method keyword:
method doit ($a, $b, $c) { ... }
method doit ($self: $a, $b, $c) { ... }
method doit (MyName $self: $a, $b, $c) { ... }
method doit (::?CLASS $self: $a, $b, $c) { ... }
Declaration of the invocant is optional. You may always access the current invocant using the keyword self. You need not declare the invocant's type, since the lexical class of the invocant is known in any event because methods must be declared in the class of the invocant, though of course the actual (virtual) type may be a derived type of the lexical type. You could declare a more restrictive type, but that would probably be a bad thing for proper polymorphism. You may explicitly type the invocant with the lexical type, but any check for that will be optimized away. (The current lexically-determined class may always be named as ::?CLASS even in anonymous classes or roles.)
From t/oo/attributes/recursive.t lines 55–92 (no results): (skip)
| #L<S12/Methods/current lexically-determined class ::?CLASS> |
| { |
| class C { |
| has ::?CLASS $.attr is rw; |
| }; |
| ok !($!), 'Can create class with attribute of type ::?CLASS'; |
| |
| my C $a; |
| my C $b; |
| try { |
| $a .= new(); |
| $b .= new(:attr($a)); |
| }; |
| ok !($!), 'Can instantiate class with ::?CLASS attribute'; |
| is $b.attr, $a, '::?CLASS attribute stores correctly'; |
| try { |
| $a.attr = $b; |
| }; |
| ok !($!), '::?CLASS cycles are fine'; |
| is $b.attr.attr, $b, '::?CLASS cycles resolve correctly'; |
| try { |
| $a.attr .= new(); |
| }; |
| ok !($!), 'Can instantiate attribute of type ::?CLASS'; |
| isa_ok $a.attr, C, '::?CLASS instantiates to correct class'; |
| |
| |
| class D is C { }; |
| ok !($!), 'Can create a subclass of class with ::?CLASS attribute'; |
| my D $d; |
| try { |
| $d .= new(); |
| $d.attr .= new(); |
| }; |
| ok !($!), 'Can instantiate derived class with ::?CLASS attribute'; |
| isa_ok $d.attr, C, '::?CLASS is lexical, not virtual', :todo<bug>; |
| } |
| |
To mark an explicit invocant, just put a colon after it:
method doit ($x: $a, $b, $c) { ... }
This is true also for multi methods:
multi method doit ($x: $a; $b; $c) { ... }
If you declare an explicit invocant for an Array type using an array variable, you may use that directly in list context to produce its elements
method push3 (@x: $a, $b, $c) { ... any(@x) ... }
Note that the self function is not context sensitive and thus always returns the current object as a single item even in list context. Hence if your current object happens to be an array but you did not declare it with an explicit array variable. you need to explicitly access the elements of the array somehow:
any(self) # WRONG
any(self[]) # okay
any(@(self)) # okay
any(@self) # WRONG unless you declared @self yourself
Private methods are declared using my:
From t/spec/S12-methods/private_methods.t lines 6–18 (no results): (skip)
| # L<S12/Methods/"Private methods are declared using my">
|
|
|
| class A {
|
| my method !private {
|
| 12;
|
| }
|
| method public {
|
| self!private
|
| }
|
| }
|
|
|
| is A.new().public, 12, 'Can call private method from within the class';
|
|
|
my method think (Brain $self: $thought)
(Such methods are completely invisible to ordinary method calls, and are in fact called with a different syntax that uses ! in place of the . character. See below.)
To call an ordinary method with ordinary method-dispatch semantics, use either the dot notation or indirect object notation:
From t/oo/methods/instance.t lines 13–91 (13 √, 6 ×): (skip)
| # L<S12/"Methods" /"either the dot notation or indirect object notation:"> |
| class Foo { |
| method doit ($a, $b, $c) { $a + $b + $c } |
| method noargs () { 42 } |
| method nobrackets { 'mice' } |
| method callsmethod1() { .noargs(); } |
| method callsmethod2 { .noargs(); } |
| } |
| |
| my $foo = Foo.new(); |
√ | is($foo.doit(1,2,3), 6, "dot method invocation"); |
| |
| my $val; |
√ | lives_ok { |
| $val = doit $foo: 1,2,3; |
| }, '... indirect method invocation works'; |
√ | is($val, 6, '... got the right value for indirect method invocation'); |
| |
√ | is($foo.noargs, 42, "... no parentheses after method"); |
√ | is($foo.noargs(), 42, "... parentheses after method"); |
| |
| { |
| my $val; |
√ | lives_ok { |
| eval '$val = $foo.noargs()'; |
| die $! if $!; |
| }, "... <space> + parentheses after method"; |
√ | is($val, 42, '... we got the value correctly'); |
| } |
| |
| { |
| my $val; |
× | lives_ok { |
| #eval '$val = $foo.noargs.()'; |
| #die $! if $!; |
| die 'cannot parse "val = $foo.noargs.()"' |
| }, "... '.' + parentheses after method", :todo<bug>; |
× | is($val, 42, '... we got the value correctly', :todo<feature>); |
| } |
| |
| { |
| my $val; |
× | lives_ok { |
| #eval '$val = $foo.noargs .()'; |
| #die $! if $!; |
| die 'cannot parse "$foo.noargs .()"' |
| }, "... <space> + '.' + parentheses after method", :todo<bug>; |
× | is($val, 42, '... we got the value correctly', :todo<feature>); |
| } |
| |
| { |
| my $val; |
√ | lives_ok { $val = $foo.nobrackets() }, 'method declared with no brackets'; |
√ | is($val, 'mice', '... we got the value correctly'); |
| } |
| |
| { |
| my $val; |
√ | lives_ok { $val = $foo.callsmethod1() }, 'method calling method'; |
√ | is($val, 42, '... we got the value correctly'); |
| }; |
| |
| { |
| my $val; |
√ | lives_ok { $val = $foo.callsmethod2() }, 'method calling method with no brackets'; |
√ | is($val, 42, '... we got the value correctly'); |
| }; |
| |
| { |
| # This test could use peer review to make sure it complies with the spec. |
| class Zoo { |
| method a () { my %s; %s.b } |
| method c () { my %s; b(%s) } |
| method b () { 1 } |
| } |
× | dies_ok( { Zoo.new.a }, "can't call current object methods on lexical data structures", :todo<bug>); |
× | dies_ok( { Zoo.new.c }, "meth(%h) is not a valid method call syntax", :todo<bug>); |
| } |
| |
$obj.doit(1,2,3)
doit $obj: 1,2,3
If the method was not found, it will fall back to a subroutine call instead, with the invocant becoming the first positional argument.
Indirect object notation now requires a colon after the invocant, even if there are no arguments after the colon:
From t/spec/S12-methods/indirect_notation.t lines 11–50 (no results): (skip)
| L<S12/Methods/Indirect object notation now requires a colon after the invocant if there are no arguments>
|
|
|
| =end description
|
|
|
| plan 6;
|
|
|
|
|
| ##### Without arguments
|
| class T1
|
| {
|
| method a
|
| {
|
| 'test';
|
| }
|
| }
|
|
|
| {
|
| my T1 $o .= new;
|
| ok( "Still alive after new" );
|
|
|
| is( $o.a(), 'test', "The indirect object notation call without argument 1" );
|
| is( (a $o:), 'test', "The indirect object notation call without arguments 2" );
|
| }
|
|
|
| ##### With arguments
|
| class T2
|
| {
|
| method a( $x )
|
| {
|
| $x;
|
| }
|
| }
|
|
|
| {
|
| my T2 $o .= new;
|
| ok( "Still alive after new" );
|
| my $seed = rand(1000);
|
| is( $o.a( $seed ), $seed, "The indirect object notation call with argument 1" );
|
| is( (a $o: $seed), $seed, "The indirect object notation call with arguments 2" );
|
| }
|
$handle.close;
close $handle:;
To reject method call and only consider subs, simply omit the colon from the invocation line:
close($handle);
close $handle;
However, here the built-in IO class defines method close is export (), which puts a multi sub close (IO) in scope by default. Thus if the $handle evaluates to an IO object, then the two subroutine calls above are still translated into method calls.
Dot notation can omit the invocant if it's in $_:
.doit(1,2,3)
Note that there is no corresponding notation for private methods.
!doit(1,2,3) # WRONG, would be parsed as not(doit(1,2,3))
self!doit(1,2,3) # okay
There are several forms of indirection for the method name. You can replace the identifier with a quoted string, and it will be evaluated as a quote and then the result of that is used as the method name.
$obj."$methodname"(1,2,3) # use contents of $methodname as method name
$obj.'$methodname'(1,2,3) # no interpolation; call method with $ in name!
$obj!"$methodname" # indirect call to private method name
[Note: to help catch the mistaken use of infix:<.> as a string concatenation operator, Perl 6 will warn you about "useless use of quotes" at compile time if the string inside quotes is an identifier. (It does not warn about non-identifier strings, but such strings are likely to produce missing method errors at run time in any case.) Also, if there is whitespace around an intended . concatenation, it cannot be parsed as a method call at all; instead if fails at compile time because standard Perl 6 has a pseudo infix:<.> operator that always fails at compile time.]
For situations where you already have a method located, you can use a simple scalar variable in place of method name:
$methodobj = $foo ?? &bar !! &baz;
$obj.$methodobj(1,2,3)
or more succinctly but less readably:
$obj.$($foo ?? &bar !! &baz)(1,2,3)
The variable must contain a Code object, that is, a closure of some sort. Regardless of whether the closure was defined as a method or a sub or a block, the closure is called directly without any class dispatch; from the closure's point of view, however, it is always called as a method, with the object as its first argument, and the rest of the arguments second, third, and so on. For instance, such a closure may be used to abstract a "navigational" path through a data structure without specifying the root of the path till later:
$locator = -> $root, $x, $y { $root.<foo>[$x]<bar>{$y}[3] }
$obj.$locator(42,"baz") # $obj<foo>[42]<bar><baz>[3]
$locator = { .<here> }
$obj.$locator # $obj<here>
As a convenient form of documentation, such a closure may also be written in the form of an anonymous method:
$locator = method ($root: $x, $y) { $root.<foo>[$x]<bar>{$y}[3] }
$obj.$locator(42,"baz") # $obj<foo>[42]<bar><baz>[3]
$locator = method { self.<here> }
$obj.$locator # $obj<here>
Note however that, like any anonymous closure, an anonymous method can only be dispatched to directly, like a sub. You may, of course, bind an anonymous method to the name of a method in a class's public interface, in which case it is no longer anonymous, and may be dispatched to normally via the class. (And in fact, when the normal method dispatcher is calling individual candidates in its candidate list, it calls each candidate as a sub, not as a method, or you'd end up with recursive dispatchers.) But fundamentally, there's no such thing as a method closure. The method declarator on an anonymous method has the primary effect of making the declaration of the invocant optional. (It also makes it an official Routine that can be returned from, just as if you'd used sub to declare it.)
Instead of a scalar variable, an array variable may also be used:
$obj.@candidates(1,2,3)
As with the scalar variant, each array element must be a Code object, but the list is treated as a list of candidates to call. Note also that the
$obj.$candidates(1,2,3)
form may dispatch to a list of candidates if $candidates is a special Code object representing a partial dispatch to a list of candidates.
Another form of indirection relies on the fact that operators are named using a variant on hash subscript notation, which gives you these forms:
$x.infix:{$op}($y)
$x.prefix:{$op}
$x.postfix:{$op}
Generally you see these with the literal angle bracket form of subscript:
$a.infix:<*>($b) # equivalent to $a * $b
$a.prefix:<++> # equivalent to ++$a
$a.postfix:<++> # equivalent to $a++
If you omit the syntactic category, the call will be dispatched according to the number of arguments either as "prefix" or as "infix":
$a.:<+>($b) # equivalent to $a + $b
$a.:<++> # equivalent to ++$a
$a.:<!> # equivalent to !$a
@a.:<[*]> # equivalent to [*] @a
But it's probably better to spell out the syntactic category when the actual operator is not obvious:
$x.infix:{$op}($y)
$x.prefix:{$op}
You must use a special syntax to call a private method:
$mybrain!think($pinky)
self!think($pinky)
For a call on your own private method, you may also use the attribute-ish form:
$!think($pinky) # short for $(self!think($pinky))
Parentheses (or a colon) are required on the dot/bang notations if there are any arguments (not counting adverbial arguments). There may be no space between the method name and the left parenthesis unless you use the dot form of parentheses or otherwise make use of "unspace":
.doit # okay, no arguments
.doit() # okay, no arguments
.doit () # ILLEGAL (two terms in a row)
.doit.() # okay, no arguments, same as .doit()
.doit\ () # okay, no arguments, same as .doit() (unspace form)
.doit\ .() # okay, no arguments, same as .doit.() (unspace form)
However, you can turn any of the legal forms above into a list operator by appending a colon:
.doit: 1,2,3 # okay, three arguments
.doit(1): 2,3 # okay, one argument plus list
.doit (): 1,2,3 # ILLEGAL (two terms in a row)
.doit.(1): 2,3 # okay, same as .doit(1,2,3)
.doit\ .(1,2): 3 # okay, same as .doit(1,2,3)
In particular, this allows us to pass a closure in addition to the "normal" arguments:
.doit: { $^a <=> $^b } # okay
.doit(): { $^a <=> $^b } # okay
.doit(1,2,3): { $^a <=> $^b } # okay
In case of ambiguity between indirect object notation and dot form, the nearest thing wins:
dothis $obj.dothat: 1,2,3
means
dothis ($obj.dothat(1,2,3))
and you must say
dothis ($obj.dothat): 1,2,3
or
$obj.dothat.dothis: 1,2,3
if you mean the other thing.
Also note that if any term in a list is a bare closure or pointy sub, it will be considered to be the final argument of its list unless the closure's right curly is followed immediately by comma or colon. In particular, a method call does *not* extend the list, so you can say:
@list.grep: { $_ % 2 }.map: { $_ - 1 }.say
and that will be taken as equivalent to
@list.grep({ $_ % 2 }).map({ $_ - 1 }).say
Methods (and subs) may be declared as lvalues with is rw. You can use an argumentless rw method anywhere you can use a variable, including in temp and let statements. (In fact, you can use an rw method with arguments as a variable as long as the arguments are used only to identify the actual value to change, and don't otherwise have strange side effects that differ between rvalue and lvalue usage. Setter methods that expect the new value as an argument do not fall into the well-behaved category, however.)
Method calls on mutable scalars always go to the object contained in the scalar (autoboxing value types as necessary):
$result = $object.doit();
$length = "mystring".codes;
Method calls on non-scalar variables just calls the Array, Hash or Code object bound to the variable:
$elems = @array.elems;
@keys = %hash.keys;
$sig = &sub.signature;
Use the prefix VAR macro on a scalar variable to get at its underlying Scalar object:
if VAR($scalar).readonly {...}
VAR is a no-op on a non-scalar variables and values:
VAR(1); # 1
VAR(@x); # @x
There's also a corresponding postfix:<.VAR> macro that can be used as if it were a method:
if $scalar.VAR.readonly {...}
(But since it's a macro, VAR is not dispatched as a real method. To dispatch to a real .VAR method, use the indirect $obj."VAR" form.)
You can also get at the container through the appropriate symbol table:
if MY::<$scalar>.readonly {...}
From t/spec/S12-class/inheritance-class-methods.t lines 6–12 (no results): (skip)
| # L<S12/Class methods/>
|
|
|
| class A is B { method f {1; } };
|
| class B { method g { self.f } };
|
|
|
| is(A.g(), 1, 'inheritance works on class methods');
|
|
|
Other OO languages give you the ability to declare "class" methods that either don't need or actively prohibit calls on instances. Perl 6 gives you a choice. If you declare an ordinary method, it can function as a "class" method when you pass it a protoobject such as "Dog" regardless of how defined the prototype object is, as long as the method body doesn't try to access any information that is undefined in the current instance.
Alternately, you can associate a class method with the current metaclass instance, which as a singleton object knows your package, and can function as a more traditional "class" method:
our $count;
method ^count { return $count }
Such a metaclass method is always delegated to the HOW object just as methods like .does are, so it's possible to call this as Dog.count or $dog.count. However, best practice is probably to call such a class method as Dog.^count or $dog.^count to make it clear that it's in its own namespace separate from ordinary methods, and so that your class method cannot be accidentally overridden by an ordinary method in a subclass--presuming you don't want to allow for that possibility.
From t/oo/attributes/class.t lines 12–47 (5 √, 12 ×): (skip)
| #L<S12/Class methods/metaclass method always delegated> |
| |
| plan 17; |
| |
√ | ok eval('class Foo { our $.bar = 23; our $.yada is rw = 13; }; 1'), 'class attributes are parsed'; |
| |
| my $test = 0; |
× | ok eval('$test = Foo.bar'), 'accessors for class attributes work'; |
× | is $test, 23, 'class attributes really work'; |
| |
√ | ok eval('class Baz is Foo {}; 1'), 'inheriting class attributes parsed'; |
| |
| my $test2 = 0; |
× | ok eval('$test2 = Baz.bar'), 'inherited class attribute accessors work'; |
× | is $test2, 23, 'inherited class attributes really work'; |
| |
| my $test3 = 0; |
× | ok eval('Baz.yada = 42; $test3 = Baz.yada'), 'inherited rw class attribute accessors work'; |
× | is $test3, 42, 'inherited rw class attributes really work'; |
| |
√ | ok eval('class Quux is Foo { has $.bar = 17; }; 1'), |
| 'overriding with instance method allowed'; |
| my $test4 = 0; |
√ | ok eval('$test4 = Quux.new()'), |
| 'Can instantiate with overridden instance method'; |
√ | is $test4.bar, 17, 'Instance call gets instance attribute, not class attribute'; |
| my $test5 = 0; |
× | ok eval('$test5 = Quux.bar'), 'class attribute still accessible via class name', :todo<feature>; |
× | is $test5, 23, 'class attribute really works, even when overridden', :todo<feature>; |
| my $test6 = 0; |
× | ok eval('$test6 = Quux.^bar'), 'class attribute accessible via ^name', :todo<feature>; |
× | is $test6, 23, 'class attribute via ^name really works', :todo<feature>; |
| my $test7 = 0; |
× | ok eval('$test7 = $test4.^bar'), |
| 'class attribute accessible via ^name called on instance', :todo<feature>; |
× | is $test7, 23, 'class attribute via $obj.^name really works', :todo<feature>; |
From t/oo/submethods.t lines 12–122 (24 √, 2 ×): (skip)
| # L<S12/Submethods> |
| { |
| my $was_in_foo_build = 0; |
| my $was_in_bar_build = 0; |
| |
√ | lives_ok { |
| class Foo { submethod BUILD() { $was_in_foo_build++ } } |
| class Bar is Foo { submethod BUILD() { $was_in_bar_build++ } } |
| }, "class definitions were parsed/run/compiled"; |
| |
| my $a; |
√ | ok eval('$a = Foo.new()'), "Foo.new() worked (1)"; |
√ | is $was_in_foo_build, 1, "Foo's BUILD was called"; |
| # is instead of todo_is to avoid unexpected succeedings |
√ | is $was_in_bar_build, 0, "Bar's BUILD was not called"; |
| |
| my $b; |
√ | ok eval('$b = Bar.new()'), "Bar.new() worked"; |
√ | is $was_in_foo_build, 2, "Foo's BUILD was called again"; |
√ | is $was_in_bar_build, 1, "Bar's BUILD was called, too"; |
| |
| # The next three tests are basically exactly the same as the first three tests |
| # (not counting the initial class definition). This is to verify our call to |
| # Bar.new didn't removed/changed some internal structures which'd prevent |
| # Foo.BUILD of getting called. |
| my $c; |
√ | ok eval('my $c = Foo.new()'), "Foo.new() worked (2)"; |
√ | is $was_in_foo_build, 3, "Foo's BUILD was called again"; |
√ | is $was_in_bar_build, 1, "Bar's BUILD was not called again"; |
| } |
| |
| # See thread "BUILD and other submethods" on p6l |
| # L<"http://groups-beta.google.com/group/perl.perl6.language/msg/e9174e5538ded4a3"> |
| { |
| my $was_in_baz_submethod = 0; |
| my $was_in_grtz_submethod = 0; |
| |
| class Baz { submethod blarb() { $was_in_baz_submethod++ } } |
| class Grtz is Baz { submethod blarb() { $was_in_grtz_submethod++ } } |
| |
| my ($baz, $grtz); |
√ | ok eval('$baz = Baz.new'), "Baz.new() worked"; |
√ | ok eval('$grtz = Grtz.new'), "Grtz.new() worked"; |
| |
| try { $baz.blarb }; |
√ | is $was_in_baz_submethod, 1, "Baz's submethod blarb was called"; |
| # No :todo to avoid unexpected suceedings |
√ | is $was_in_grtz_submethod, 0, "Grtz's submethod blarb was not called"; |
| |
| try { $grtz.blarb }; |
√ | is $was_in_baz_submethod, 1, "Baz's submethod blarb was not called again"; |
| # No :todo to avoid unexpected suceedings |
√ | is $was_in_grtz_submethod, 1, "Grtz's submethod blarb was called now"; |
| |
| try { $grtz.Baz::blarb }; |
√ | is $was_in_baz_submethod, 2, "Baz's submethod blarb was called again now"; |
| # No :todo to avoid unexpected suceedings |
√ | is $was_in_grtz_submethod, 1, "Grtz's submethod blarb was not called again"; |
| } |
| |
| # Roles with BUILD |
| # See thread "Roles and BUILD" on p6l |
| # L<"http://www.nntp.perl.org/group/perl.perl6.language/21277"> |
| { |
| my $was_in_a1_build = 0; |
| my $was_in_a2_build = 0; |
| role RoleA1 { multi submethod BUILD() { $was_in_a1_build++ } } |
| role RoleA2 { multi submethod BUILD() { $was_in_a2_build++ } } |
| class ClassA does RoleA1 does RoleA2 {} |
| |
| ClassA.new; |
| |
√ | is $was_in_a1_build, 1, "roles' BUILD submethods were called when mixed in a class (1)"; |
√ | is $was_in_a2_build, 1, "roles' BUILD submethods were called when mixed in a class (2)"; |
| } |
| |
| { |
| my $was_in_b1_build = 0; |
| my $was_in_b2_build = 0; |
| role RoleB1 { multi submethod BUILD() { $was_in_b1_build++ } } |
| role RoleB2 { multi submethod BUILD() { $was_in_b2_build++ } } |
| class ClassB {} |
| |
| my $B = ClassB.new; |
√ | is $was_in_b1_build, 0, "roles' BUILD submethods were not yet called (1)"; |
√ | is $was_in_b2_build, 0, "roles' BUILD submethods were not yet called (2)"; |
| |
| eval '$B does RoleB1 does RoleB2'; |
× | is $was_in_b1_build, 1, "roles' BUILD submethods were called now (1)", :todo<feature>; |
× | is $was_in_b2_build, 1, "roles' BUILD submethods were called now (2)", :todo<feature>; |
| }; |
| |
| # BUILD with signatures that don't map directly to attributes |
| { |
| class ClassC |
| { |
| has $.double_value; |
| |
| submethod BUILD ( $value = 1 ) |
| { |
| $.double_value = $value * 2; |
| } |
| } |
| |
| my $C = ClassC.new(); |
√ | is( $C.double_value, 2, |
| 'BUILD() should allow default values of optional params in signature' ); |
| |
| my $C2 = ClassC.new( :value(100) ); |
√ | is( $C2.double_value, 200, '... or value passed in' ); |
| } |
Submethods are for declaring infrastructural methods that shouldn't be inherited by subclasses, such as initializers:
submethod BUILD ($arg) {
$.attr = $arg;
}
Apart from the keyword, submethod declaration and call syntax is identical to method syntax. You may mix methods and submethods of the same name within the class hierarchy, but only the methods are visible to derived classes via inheritance. A submethod is called only when a method call is dispatched directly to the current class.
Conjecture: in order to catch spelling errors it is a compile-time warning to define a submethod in any class that does not inherit the corresponding method name from some base class. More importantly, this would help safeguard Liskov substitutability. (But note that the standard Object class already supplies a default BUILD and new.)
From t/oo/attributes/array.t lines 3–21 (1 √, 0 ×): (skip)
| # L<S12/"Attributes"> |
| # ( The spec doesn't explicitly method that this should |
| # work, but audreyt and markstos agree it should. |
| # Perhaps the spec can be clarified on this point.) |
| |
| { |
| class Foo { |
| has @.a is rw; |
| method param (*%h) { |
| @.a = @( %h<key> ); |
| self; |
| } |
| } |
| # The workaround until this is fixed: |
| # my @f = @( Foo.new.param( key => <c d>).a ); |
| my @f = Foo.new.param( key => <c d> ).a; |
√ | is_deeply(@f, <c d>, "@.a attribute is returned as array"); |
| } |
| |
From t/oo/attributes/recursive.t lines 13–36 (6 √, 0 ×): (skip)
| #L<S12/Attributes> |
| { |
| class A { |
| has A $.attr is rw; |
| }; |
| |
√ | ok !($!), 'Can create class with a recursively-typed attribute'; |
| |
| my A $a; |
| my A $b; |
| try { |
| $a .= new(); |
| $b .= new(:attr($a)); |
| }; |
√ | ok !($!), 'Can instantiate class with recursively-typed attribute'; |
√ | isa_ok $a, 'A', 'Sanity check, $a is of type A'; |
√ | is $b.attr, $a, "Recursively-typed attribute stores correctly"; |
| try { |
| $a.attr = $b; |
| }; |
√ | ok !($!), "Cycles are fine"; |
√ | is $b.attr.attr, $b, "Cycles resolve correctly"; |
| } |
| |
From t/oo/attributes/instance.t lines 87–127 (9 √, 2 ×): (skip)
| # L<S12/Attributes> |
| |
| class Foo5 { |
| has $.tail is rw; |
| has @.legs; |
| has $!brain; |
| |
| method set_legs (*@legs) { @.legs = @legs } |
| method inc_brain () { $!brain++ } |
| method get_brain () { $!brain } |
| }; |
| |
| { |
| my $foo = Foo5.new(); |
√ | ok($foo ~~ Foo5, '... our Foo5 instance was created'); |
| |
√ | lives_ok { |
| $foo.tail = "a"; |
| }, "setting a public rw attribute"; |
√ | is($foo.tail, "a", "getting a public rw attribute"); |
| |
√ | lives_ok { |
| $foo.set_legs(1,2,3) |
| }, "setting a public ro attribute (1)"; |
√ | is($foo.legs.[1], 2, "getting a public ro attribute (1)"); |
| |
× | dies_ok { |
| $foo.legs = (4,5,6); |
| }, "setting a public ro attribute (2)", :todo<feature>; |
× | is($foo.legs.[1], 2, "getting a public ro attribute (2)", :todo<feature>); |
| |
√ | lives_ok { |
| $foo.inc_brain(); |
| }, "modifiying a private attribute (1)"; |
√ | is($foo.get_brain, 1, "getting a private attribute (1)"); |
√ | lives_ok { |
| $foo.inc_brain(); |
| }, "modifiying a private attribute (2)"; |
√ | is($foo.get_brain, 2, "getting a private attribute (2)"); |
| } |
| |
From t/oo/attributes/attribute_initialization.t lines 7–48 (8 √, 0 ×): (skip)
| # L<S12/"Attributes"> |
| |
| plan 8; |
| |
| diag('Test for class attribute initialization'); |
| |
| |
| { |
| class T1 { } |
| class T2 { } |
√ | ok eval(q{class T1 is also { has $.t = 1 }; 1}), |
| "Try to initialize public attribute"; |
| |
√ | ok eval(q{ |
| class T2 is also { |
| has $!t = 2; |
| method get { $!t }; |
| }; 1 }), |
| "Try to initialize private attribute"; |
| |
| |
| my T1 $o1; |
| my T2 $o2; |
| |
| $o1 = T1.new(); |
| $o2 = T2.new(); |
√ | is $o1.t, 1, |
| "Testing value for initialized public attribute."; |
√ | dies_ok { $o2.t }, |
| "Try to access the initialized private attribute."; |
√ | is try { $o2.get }, 2, |
| "Testing value for initialized private attribue."; |
| |
| $o1 = T1.new( t => 3 ); |
| $o2 = T2.new( t => 4 ); |
√ | is $o1.t, 3, |
| "Testing value for attributes which is initialized by constructor."; |
√ | dies_ok { $o2.t }, |
| "Try to access the private attribute which is initialized by constructor."; |
√ | is try { $o2.get }, 4, |
| "Testing value for private attribue which is initialized by constructor."; |
| } |
Attributes are stored in an opaque datatype, not in a hash. Not even the class has to care how they're stored, since they're declared much like ordinary variables. Instead of my, use has:
class Dog is Mammal {
has $.name = "fido";
has $.tail is rw;
has @.legs;
has $!brain;
...
}
Public attributes have a secondary sigil of "dot", indicating the automatic generation of an accessor method of the same name. Private attributes use an exclamation to indicate that no public accessor is generated.
From t/oo/attributes/instance.t lines 16–31 (3 √, 2 ×): (skip)
| # L<S12/Attributes/the automatic generation of an accessor method of the same name> |
| |
| class Foo1 { has $.bar; }; |
| |
| { |
| my $foo = Foo1.new(); |
√ | ok($foo ~~ Foo1, '... our Foo1 instance was created'); |
| my $val; |
× | lives_ok { |
| $val = $foo.can("bar") |
| }, '.. checking autogenerated accessor existence', :todo<feature>; |
× | ok($val, '... $foo.can("bar") should have returned true', :todo<feature>); |
√ | is($foo.bar(), undef, '.. autogenerated accessor works'); |
√ | is($foo.bar, undef, '.. autogenerated accessor works w/out parens'); |
| } |
| |
From t/oo/attributes/instance.t lines 68–86 (2 √, 2 ×): (skip)
| # L<S12/Attributes/Private attributes use an exclamation to indicate that no public accessor is> |
| |
| class Foo4 { has $!bar; }; |
| |
| { |
| my $foo = Foo4.new(); |
√ | ok($foo ~~ Foo4, '... our Foo4 instance was created'); |
× | ok(try{!$foo.can("bar")}, '.. checking autogenerated accessor existence', :todo<feature>); |
| } |
| |
| class Foo4a { has $!bar = "baz"; }; |
| |
| { |
| my $foo = eval 'Foo4a.new()'; |
√ | ok(try{$foo ~~ Foo4a}, '... our Foo4a instance was created'); |
× | ok(try{!$foo.can("bar")}, '.. checking autogenerated accessor existence', :todo<feature>); |
| } |
| |
| |
has $!brain;
The "true name" of the private variable always has the exclamation, but much like with our variables, you may declare a lexically scoped alias to the private variable by saying:
has $brain; # also declares $!brain;
As with the ! declaration, no accessor is generated.
And any later references to the private variable within the same block may either use or omit the exclamation, as you wish to emphasize or ignore the privacy of the variable. Outside the block, you must use the ! form. If you declare with the ! form, you must use that form consistently everywhere. If you declare with the . form, you also get the private ! form as a non-virtual name for the actual storage location, and you may use either ! or . form anywhere within the class, even if the class is reopened. Outside the class you must use the public . form, or rely on a method call (which can be a private method call, but only for trusted classes).
For public attributes, some traits are copied to the accessor method. The rw trait causes the generated accessor to be declared rw, making it an lvalue method. The default is a read-only accessor.
From t/oo/attributes/instance.t lines 45–67 (4 √, 4 ×): (skip)
| # L<S12/Attributes/making it an lvalue method> |
| |
| class Foo3 { has $.bar is rw; }; |
| |
| { |
| my $foo = Foo3.new(); |
√ | ok($foo ~~ Foo3, '... our Foo3 instance was created'); |
| my $val; |
× | lives_ok { |
| $val = $foo.can("bar"); |
| }, '.. checking autogenerated accessor existence', :todo<feature>; |
× | ok($val, '... $foo.can("bar") should have returned true', :todo<feature>); |
√ | is($foo.bar(), undef, '.. autogenerated accessor works'); |
√ | lives_ok { |
| $foo.bar = "baz"; |
| }, '.. autogenerated mutator as lvalue works'; |
√ | is($foo.bar, "baz", '.. autogenerated mutator as lvalue set the value correctly'); |
× | lives_ok { |
| $foo.bar("baz2"); |
| }, '.. autogenerated mutator works as method', :todo<feature>; |
× | is($foo.bar, "baz2", '.. autogenerated mutator as method set the value correctly', :todo<feature>); |
| } |
| |
If you declare the class as rw, then all the class's attributes default to rw, much like a C struct.
From t/oo/attributes/is_rw_on_class.t lines 7–29 (2 √, 1 ×): (skip)
| # L<S12/Attributes/If you declare the class as> |
| |
| class Foo { |
| has $.readonly_attr; |
| } |
| |
| { |
| my Foo $foo .= new; |
× | dies_ok { $foo.readonly_attr++ }, "basic sanity", :todo<bug>; |
| } |
| |
| |
| class Bar is rw { |
| has $.readwrite_attr; |
| } |
| |
| { |
| my Bar $bar .= new; |
√ | lives_ok { $bar.readwrite_attr++ }, |
| "'is rw' on the class declaration applies to all attributes (1)"; |
√ | is $bar.readwrite_attr, 1, |
| "'is rw' on the class declaration applies to all attributes (2)"; |
| } |
You may write your own accessors to override any or all of the autogenerated ones.
The attribute variables may be used within instance methods to refer directly to the attribute values. Outside the instance methods, the only access to attributes is through the accessors since an object has to be specified. The dot form of attribute variables may be used in derived classes because the dot form always implies a virtual accessor call. Every dot declaration also declares a corresponding private exclamation storage location, and the exclamation form may be used only in the actual class, not in derived classes. Reference to the internal storage location via $!foo should generally be restricted to submethods. Ordinary methods should stick to the $.foo form.
In fact, within submethods, use of the $.foo form on attributes that are available as $!foo (that is, that are declared directly by this class) is illegal and produces a dire compile-time warning (which may be suppressed). Within a submethod the $.foo form may only be used on attributes from parent classes, because only the parent classes' part of the object is guaranteed to be in a consistent state (because BUILDALL call's the parent classes' BUILD routines first). If you attempt to get around this by declaring BUILD as a method rather than a submethod, that will also be flagged as a dire (but suppressible) compile-time warning. (It is possible to define an inheritable BUILD routine if you have access to all the metadata for the current class, but it's not easy, and it certainly doesn't happen by accident just because you change submethod to method.)
Because $.foo, @.foo, %.foo, &.foo are just shorthands of self.foo with different contexts, the class does not need to declare any of those as an attribute -- a method foo declaration can work just as well.
The dot form can take an argument list as well. These are all equivalent:
self.foo(1,2,3); # a regular method call
self.foo.(1,2,3); # ditto
$.foo(1,2,3); # calls self.foo under $ context
$.foo.(1,2,3); # ditto
@.foo(1,2,3); # calls self.foo under @ context
@.foo.(1,2,3); # ditto
Pseudo-assignment to an attribute declaration specifies the default value. The value on the right is treated as an implicit closure and evaluated at object build time, that is, when the object is being constructed, not when class is being composed. To refer to a value computed at compilation or composition time, you can either use a temporary or a temporal block of some sort:
From t/oo/attributes/defaults.t lines 7–21 (no results): (skip)
| # L<S12/Attributes/The value on the right is evaluated at object build time> |
| |
| my $got_a_num; sub get_a_num { $got_a_num++; 42 } |
| my $got_a_str; sub get_a_str { $got_a_str++; "Pugs" } |
| |
| my $got_a_code; |
| my $was_in_closure; |
| sub get_a_code { |
| $got_a_code++; |
| return { |
| $was_in_closure++; |
| 42; |
| }; |
| } |
| |
From t/oo/attributes/instance.t lines 32–44 (3 √, 1 ×): (skip)
| # L<S12/Attributes/Pseudo-assignment to an attribute declaration specifies the default> |
| |
| eval 'class Foo2 { has $.bar = "baz"; }'; |
| |
| { |
| my $foo = eval 'Foo2.new()'; |
√ | ok(eval('$foo ~~ Foo2'), '... our Foo2 instance was created'); |
× | ok(eval('$foo.can("bar")'), '.. checking autogenerated accessor existence', :todo<feature>); |
√ | is(eval('$foo.bar()'), "baz", '.. autogenerated accessor works'); |
√ | is(eval('$foo.bar'), "baz", '.. autogenerated accessor works w/out parens'); |
| # what exactly will happen if we try to set bar() |
| } |
| |
has $.r = rand; # each object gets different random value
constant $random = rand;
has $.r = $random; # every object gets same value
has $.r = BEGIN { rand };
has $.r = INIT { rand };
has $.r = ENTER { rand };
has $.r = FIRST { rand };
has $.r = constant $myrand = rand;
When it is called at BUILD time, the topic of the implicit closure will be the attribute being initialized, while "self" refers to the entire object being initialized. The closure will be called at the end of the BUILD only if the attribute is not otherwise initialized in either the signature or the body of the BUILD.
From t/oo/attributes/defaults.t lines 22–102 (5 √, 16 ×): (skip)
| # L<S12/Attributes/the attribute being initialized> |
| |
| my $set_by_code_attr; |
| |
| eval 'class Foo { |
| has $.num = get_a_num(); |
| has $.str = { get_a_str() }; |
| has $.code = { get_a_code() }; |
| |
| has $.set_by_code = { |
| $set_by_code_attr := $_; |
| 42; |
| }; |
| |
| has $.self_in_code = { self.echo }; |
| |
| method echo { "echo" } |
| }'; |
| |
| { |
√ | is $got_a_num, 1, "default should be called at compile-time"; |
| my Foo $foo .= new; |
√ | is $got_a_num, 1, "default should be called only once, at compile-time (1)"; |
√ | is $foo.num, 42, "attribute default worked"; |
√ | is $got_a_num, 1, "default should be called only once, at compile-time (2)"; |
| } |
| |
| { |
| $got_a_str = 0; # reset |
| |
| { |
| my Foo $foo .= new; |
× | is $got_a_str, 1, "using a coderef as a default value delays execution", :todo<feature>; |
× | is try { $foo.str }, "Pugs", "attribute default worked", :todo<feature>; |
| } |
| |
| { |
| my Foo $foo .= new; |
× | is $got_a_str, 2, "using a coderef as a default value delays execution", :todo<feature>; |
× | is try { $foo.str }, "Pugs", "attribute default worked", :todo<feature>; |
| } |
| } |
| |
| { |
| $got_a_code = 0; # reset |
| |
| { |
| my Foo $foo .= new; |
× | is $got_a_code, 1, "using a coderef as a default value delays execution", :todo<feature>; |
× | is $was_in_closure, 0, "sub-coderef not yet executed", :todo<feature>; |
| try { $foo.code }; |
× | is $was_in_closure, 0, "sub-coderef still not executed", :todo<feature>; |
| } |
| |
| { |
| my Foo $foo .= new; |
× | is $got_a_code, 2, "using a coderef as a default value delays execution", :todo<feature>; |
× | is $was_in_closure, 0, "sub-coderef not yet executed", :todo<feature>; |
× | is try { $foo.code() }, 42, "sub-coderef execution works", :todo<feature>; |
× | is $was_in_closure, 1, "sub-coderef still not executed", :todo<feature>; |
| } |
| } |
| |
| { |
| my Foo $foo .= new; |
| |
× | is try { $foo.set_by_code }, 42, '$_ is the attribute being initialized (1)', :todo<feature>; |
× | is $set_by_code_attr, 42, '$_ is the attribute being initialized (2)', :todo<feature>; |
| |
√ | lives_ok { $set_by_code_attr++ }, |
| '$_ is the attribute being initialized (3)'; |
| |
× | is try { $foo.set_by_code }, 43, '$_ is the attribute being initialized (4)', :todo<feature>; |
× | is $set_by_code_attr, 43, '$_ is the attribute being initialized (5)', :todo<feature>; |
| } |
| |
| { |
| my Foo $foo .= new; |
| |
× | is try { $foo.self_in_code }, "echo", "self is the object being initialized", :todo<feature>; |
| } |
Class attributes are declared with either my or our. The only difference from ordinary my or our variables is that an accessor is generated according to the secondary sigil:
From t/oo/attributes/recursive.t lines 37–54 (1 √, 1 ×): (skip)
| #L<S12/Attributes/"Class attributes are declared"> |
| { |
| class B { |
| my B $.attr is rw; |
| }; |
| |
√ | ok !($!), "Can create class with a recursively-typed class lexical"; |
| |
| my B $a; |
| try { |
| $a .= new(); |
| B.attr = $a; |
| }; |
× | ok !($!), "Can instantiate class with recursively-typed class lexical"; |
| is B.attr, $a, "Recursively-typed class lexical stores correctly"; |
| |
| } |
| |
From t/oo/attributes/class.t lines 11–47 (5 √, 12 ×): (skip)
| #L<S12/Attributes/"Class attributes are declared"> |
| #L<S12/Class methods/metaclass method always delegated> |
| |
| plan 17; |
| |
√ | ok eval('class Foo { our $.bar = 23; our $.yada is rw = 13; }; 1'), 'class attributes are parsed'; |
| |
| my $test = 0; |
× | ok eval('$test = Foo.bar'), 'accessors for class attributes work'; |
× | is $test, 23, 'class attributes really work'; |
| |
√ | ok eval('class Baz is Foo {}; 1'), 'inheriting class attributes parsed'; |
| |
| my $test2 = 0; |
× | ok eval('$test2 = Baz.bar'), 'inherited class attribute accessors work'; |
× | is $test2, 23, 'inherited class attributes really work'; |
| |
| my $test3 = 0; |
× | ok eval('Baz.yada = 42; $test3 = Baz.yada'), 'inherited rw class attribute accessors work'; |
× | is $test3, 42, 'inherited rw class attributes really work'; |
| |
√ | ok eval('class Quux is Foo { has $.bar = 17; }; 1'), |
| 'overriding with instance method allowed'; |
| my $test4 = 0; |
√ | ok eval('$test4 = Quux.new()'), |
| 'Can instantiate with overridden instance method'; |
√ | is $test4.bar, 17, 'Instance call gets instance attribute, not class attribute'; |
| my $test5 = 0; |
× | ok eval('$test5 = Quux.bar'), 'class attribute still accessible via class name', :todo<feature>; |
× | is $test5, 23, 'class attribute really works, even when overridden', :todo<feature>; |
| my $test6 = 0; |
× | ok eval('$test6 = Quux.^bar'), 'class attrib |