Apocalypse 12
by Larry Wall
|
Pages: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20
Editor's Note: this Apocalypse is out of date and remains here for historic reasons. See Synopsis 12 for the latest information.
Class Attributes
In general, class attributes are just package or lexical variables. If you define a package variable with a dot or colon, it autogenerates an accessor for you just as it does for an ordinary attribute:
our $.count; # generates a public read-only .count accessor
our %:cache is rw; # generates a private read-write .:cache accessor
The implicit invocant of these implicit accessors has a "squinting" type--it can either be the class or an object of the class. (Declare your own accessors if you have a philosophical reason for forcing the type one way or the other.)
The disadvantage of using "our" above is that both
of these are accessible from outside the class via their package name
(though the private one is Officially Ignored, and cannot be named simply
by saying %MyClass:::cache because that syntax is specifically
disallowed).
If on the other hand you declare your class variables lexically:
my $.count; # generates a read-only .count accessor
my %:cache is rw; # generates a read-write .:cache accessor
then the same pair of accessors are generated, but the variables themselves are visible only within the class block. If you reopen the class in another block, you can only see the accessors, not the bare variables. This is probably a feature.
Generally speaking, though, unless you want to provide public accessors
for your class attributes, it's best to just declare them as ordinary
variables (either my or state variables) to
prevent confusion with instance attributes. It's a good policy not to
declare any public accessors until you know you need them. They are,
after all, part of your contract with the outside world, and the outside
world has a way of holding you to your contracts.
Object Construction
The basic idea here is to remove the drudgery of creating objects. In addition we want object creation and cleanup to work right by default. In Perl 5 it's possible to make recursive construction and destruction work, but it's not the default, and it's not easy.
Perl 5 also confused the notions of constructor and initializer. A constructor should create a new object once, then call all the appropriate initializers in the inheritance tree without recreating the object. The initializer for a base class should be called before the initializer for any class derived from it.
The initializer for a class is always named BUILD. It's
in uppercase because it's usually called automatically for you at construction
time.
As with Perl 5, a constructor is only named "new" by convention,
and you can write a constructor with any name you like. However, in
Perl 6, if you do not supply a "new" method, a generic
one will be provided (by inheritance from Object, as it
happens).
The Default Constructor
The default new constructor looks like this:
multi method new (Class $class: *%_) {
return $class.bless(0, *%_);
}
The arguments for the default constructor are always named arguments,
hence the *%_ declaration to collect all those pairs and
pass them on to bless.
You'll note also that bless is no longer a subroutine but
a method call, so it's now impossible to omit the class specification.
This makes it easier to inherit constructors. You can still bless any
reference you could bless in Perl 5, but where you previously used a
function to do that:
# Perl 5 code...
return bless( {attr => "hi"}, $class );
in Perl 6 you use a method call:
# Perl 6 code...
return $class.bless( {attr => "hi"} );
However, if what you pass as the first argument isn't a reference, bless
is going to construct an opaque object and initialize it. In a sense,
bless is the only real constructor in Perl 6. It first
makes sure the data structure is created. If you don't supply a reference
to bless, it calls CREATE to create the object. Then it
calls BUILDALL to call all the initializers.
The signature of bless is something like:
method bless ($class: $candidate, *%_)
The 0 candidate indicates the built-in opaque type. If you're
really strange in the head, you can think of the "0" as
standing for "0paque". Or it's the "zero" object, about
which we know zip. Whatever tilts your windmill...
In any event, strings are reserved for other object layouts. We could conceivably have things like:
return $class.bless("Cstruct", *%_);
So as it happens, 0 is short for the layout "P6opaque".
Any additional arguments to .bless are automatically passed
on to CREATE and BUILDALL. But note that these
must be named arguments. It could be argued that the only
real purpose for writing a .new constructor in Perl 6 is
to translate different positional argument signatures into a unified
set of named arguments. Any other initialization common to all constructors
should be done within BUILD.
Oh, the invocant of .bless is either a class or an object
of the class, but if you use an object of the class, the contents of
that object are not automatically used to prototype the new
object. If you wish to do that, you have to do it explicitly by copying
the attributes:
$obj.bless(0, *%$obj)
(That is just a specific application of the general principle that if
you treat any object like a hash, it will behave like one, to the extent
that it can. That is, %$obj turns the attributes into key/value
pairs, and passes those as arguments to initialize the new object. Note
that %$obj includes the private attributes when used inside
the class, but not outside.)
Just because .bless allows an object to be used for a class
doesn't mean your new constructor has to do the same. Some
folks have philosophical issues with mixing up classes and objects,
and it's fine to disallow that on the constructor level. In fact, you'll
note that the default .new above requires a Class
as its invocant. Unless you override it, it doesn't allow an object
for the constructor invocant. Go thou and don't likewise.
The default cloner
Another good reason not to overload .new to do cloning is
that Perl will also supply a default .clone routine that
works something like this:
multi method clone ($proto: *%_) {
return $proto.bless(0, *%_, *%$proto);
}
Note the order of the two hash arguments to bless. This
gives the supplied attribute values precedence over the copied attribute
values, so that you can change some of the attributes en passant,
if you like. That's because we're passing the two flattened hashes as
arguments to .bless and Perl 6's named argument binding
mechanism always picks the first argument that matches, not
the last. This is opposite of what happens when you use the Perl 5 idiom:
%newvals = (%_, %$proto);
In that case, the last value (the one in %$proto) would "win".
CREATE
submethod CREATE ($self: *%args) {...}
CREATE is called when you don't want to use an existing
data structure as the candidate for your object. In general you won't
define CREATE because the default CREATE does
all the heavy magic to bring an opaque object into existence. But if
you don't want an opaque object, and you don't care to write all your
constructors to create the data structure before calling .bless,
you can define your own CREATE submethod, and it will override
the standard one for all constructors in the class.
BUILDALL
submethod BUILDALL ($self: *%args) {...}
After the data structure is created, it must be populated by each of
the participating classes (and roles) in the proper order. The BUILDALL
method is called upon to do this. The default BUILDALL
is usually correct, so you don't generally have to override it. In essence,
it delegates the initialization of parent classes to the BUILDALL
of the parent classes, and then it calls BUILD on the current
class. In this way the pieces of the object are assembled in the correct
order, from least derived to most derived.
For each class BUILDALL calls on, if the arguments contain
a pair whose key is that class name, it passes the value of the pair
as its argument to that class's BUILDALL. Otherwise it
passes the entire list. (There's not much ambiguity there--most classes
and roles will start with upper case, while most attribute names start
with lower case.)
BUILD
submethod BUILD ($self: *%args) {...}
That is the generic signature of BUILD from the viewpoint
of the caller, but the typical BUILD routine declares explicit
parameters named after the attributes:
submethod BUILD (+$tail, +@legs, *%extraargs) {
$.tail = $tail;
@:legs = @legs;
...
}
That occurs so frequently that there's a shorthand available in the signature declaration. You can put the attributes (distinguished by those secondary sigils, you'll recall) right into the signature. The following means essentially the same thing, without repeating the names:
submethod BUILD (+$.tail, +@:legs, *%extraargs) {...}
It's actually unnecessary to declare the *%extraargs parameter.
If you leave it out, it will default to *%_ (but only on
methods and submethods--see the section on Interface Consistency later).
You may use this special syntax only for instance attributes, not class attributes. Class attributes should generally not be reinitialized every time you make a new object, after all.
If you do not declare a BUILD routine, a default routine
will be supplied that initializes any attributes whose names correspond
to the keys of the argument pairs passed to it, and leaves the other
attributes to default to whatever the class supplied as the default,
or undef otherwise.
In any event, the assignment of default attribute values happens automatically.
For any attribute that is not otherwise initialized, the attribute declaration's
"build" property is evaluated and the resulting value copied
in to the newly created attribute slot. This happens logically at the
end of the BUILD block, so we avoid running initialization
closures unnecessarily. This implicit initialization is based not on
whether the attribute is undefined, but on whether it was initialized
earlier in BUILD. (Otherwise we could never explicitly
create an attribute with an undefined value.)
Pages: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 |

