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.
Classes
A class is what provides a name and a place for the abstract behavior of a set of objects said to belong to the class.
As in Perl 5, a class is still "just a funny package", structurally speaking. Syntactically, however, a class is now distinct from a package or a module. And the body of a class definition now runs in the context of a metaclass, which is just a way of saying that it has a metaclass instance as its (undeclared) invocant. (An "invocant" is what we call the object or class on behalf of which a method is being called.) Hence class definitions, though apparently declarative, are also executing code to build the class definition, and the various declarations within the class are also running bits of code. By convention classes will use a standard metaclass, but that's just convention. (A very strong convention, we hope.)
The primary role of a class is to manage instances, that is, objects. So a class must worry about object creation and destruction, and everything that happens in between. Classes have a secondary role as units of software reuse, in that they can be inherited from or delegated to. However, because this is a secondary role, and because of weaknesses in models of inheritance, composition, and delegation, Perl 6 will split out the notion of software reuse into a separate class-like entity called a "role". Roles are an abstraction mechanism for use by classes that don't care about the secondary aspects of software reuse, or that (looking at it the other way) care so much about it that they want to encapsulate any decisions about implementation, composition, delegation, and maybe even inheritance. Sounds fancy, but just think of them as includes of partial classes, with some safety checks. Roles don't manage objects. They manage interfaces and other abstract behavior (like default implementations), and they help classes manage objects. As such, a role may only be composed into a class or into another role, never inherited from or delegated to. That's what classes are for.
Classes are arranged in an inheritance hierarchy by their "isa" relationships. Perl 6 supports multiple inheritance, but makes it easy to program in a single-inheritance style, insofar as roles make it easy to mix in (or delegate, or parameterize) private implementation details that don't belong in the public inheritance tree.
In those cases where MI is used, there can be ambiguities in the pecking order of classes in different branches. Perl 6 will have a canonical way to disambiguate these, but by design the dispatch policy is separable from inheritance, so that you can change the rules for a given set of classes. (Certainly the rules can change when we call into another language's class hierarchy, for instance.)
Where possible, class names are treated polymorphically, just as method
names are. This powerful feature makes it possible to inherit systems
of classes in parallel. (These classes might be inner classes, or they
might be inner aliases to outer classes.) By making the class names
"virtual", the base classes can refer to the appropriate derived classes
without knowing their full name. That sounds complicated, but it just
means that if you do the normal thing, Perl will call the right class
instead of the one you thought it was going to call. :-)
(As in C++ culture, we use the term "virtual" to denote a method that dispatched based on the actual runtime type of the object rather than the declared type of the variable. C++ classes have to declare their methods to be virtual explicitly. All of Perl's public methods are virtual implicitly.)
You may derive from any built-in class. For high-level object classes
such as Int or Num there are no restrictions
on how you derive. For low-level representational classes like int
or num, you may not change the representation of the value;
you may only add behaviors. (If you want to change the representation,
you should probably be using composition instead of inheritance. Or
define your own low-level type.) Apart from this, you don't need to
worry about the difference between int and Int,
or num and Num, since Perl 6 will do autoboxing.
Declaration of Classes
Class declarations may be either file scoped or block scoped. A file-scoped declaration must be the first thing in the file, and looks like this:
class Dog is Mammal;
has Limb @.paws;
method walk () { .paws».move() }
That has the advantage of avoiding the use of one set of braces, letting you put everything up against left margin. It is otherwise identical to a block-scoped class, which looks like this:
class Dog is Mammal {
has Limb @.paws;
method walk () { .paws».move() }
}
An incomplete class definition makes use of the ... ("yada,
yada, yada") operator:
class Dog is Mammal {...}
The declaration of a class name introduces the name as a valid bare identifier
or name. In the absence of such a declaration, the name of a class in
an expression must be introduced with the :: class sigil,
or it will be considered a bareword and rejected, since Perl 6 doesn't
allow barewords. Once the name is declared however, it may be used as
an ordinary term in an expression. Unlike in Perl 5, you should not
view it as a bareword string. Rather, you should view it as a parameterless
subroutine that returns a class object, which conveniently stringifies
to the name of the class for Perl 5 compatibility. But when you say
Dog.new()
the invocant of new is an object of type Class,
not a string as in Perl 5.
Unmodified, a class declaration always declares a global name. But if
you prefix it with our, you're defining an inner class:
class Cell {
our class Golgi {...}
...
}
The full name of the inner class is Cell::Golgi, and that
name can be used outside of Cell, since Golgi
is declared in the Cell package. (Classes may be declared
private, however. More later.)
Class traits
A class declaration may apply various traits to the class. (A trait is a property applied at compile time.) When you apply a trait, you're accepting whatever it is that that trait does to your class, which could be pretty much anything. Traits do things to classes. Do not confuse traits with roles, which are sworn to play a subservient role to the class. Traits can do whatever they jolly well please to your class's metadata.
Now, the usual thing to do to a class's metadata is to insert another class into its ISA metadata. So we use trait notation to install a superclass:
class Dog is Mammal {...}
To specify multiple inheritance, just add another trait:
class Dog is Mammal is Pet {...}
But often you'll want a role instead, specified with does:
class Dog is Mammal does Pet {...}
More on that later. But remember that traits are evil. You can have traits like:
class Moose is Mammal is stuffed is really(Hatrack) is spy(Russian) {...}
So what if you actually want to derive from stuffed? That's
a good question, which we will answer later. (The short answer is, you
don't.)
Now as it happens, you can also use is from within the class.
You can also put the does inside to include various roles:
class Dog {
is Mammal;
does Pet;
does Servant;
does Best::Friend[Man];
does Drool;
...
}
In fact, there's no particular reason to put any of these outside the braces except to make them more obvious to the casual reader. If we take the view that inheritance is just one form of implementation, then a simple
class Dog {...}
is sufficient to establish that there's a Dog class defined
out there somewhere. We shouldn't really care about the implementation
of Dog, only its interface--which is usually pretty slobbery.
That being said, you can know more about the interface at compile time
once you know the inheritance, so it's good to have pulled in a definition
of the class as well as a declaration. Since this is typically done
with use, the inheritance tree is generally available even
if you don't mark your class declaration externally with the inheritance.
(But in any event, the actual inheritance tree doesn't have to be available
till runtime, since that's when methods are dispatched. (Though as
is often the case, certain optimizations work better when you give them
more data earlier...))
Use of Classes
A class is used directly by calling class methods, and indirectly by calling methods of an object of that class (or of a derived class that doesn't override the methods in question).
Classes may also be used as objects in their own right, as instances
of a metaclass, the class MetaClass by default. When you
declare class Dog, you're actually calling a metaclass
class method that constructs a metaclass instance (i.e., the Dog
class) and then calls the associated closure (i.e., the body of the class)
as a method on the instance. (With a little grammatical magic thrown
in so that Dog isn't considered a bareword.)
The class Dog is an instance of the class MetaClass,
but it's also an instance of the type Class when you're
thinking of it as a dispatcher. That is, a class object is really allomorphic.
If you treat one as an instance of Class, it behaves as if it were the
user's view of the class, and the user thinks the class is there only
to dispatch to the user's own class and instance methods. If, however,
you treat the object as an instance of MetaClass, you get
access to all its metaclass methods rather than the user-defined methods.
Another way to look at it is that the metaclass object is a separate
object that manages the class object. In any event, you can get from
the ordinary class object to its corresponding metaclass object via
the .meta method, which every object supports.
By the way, a Class is a Module, which in turn
is a Package, which in turn is an Object. Or
something like that. So a class can always be used as if it were a mere
module or package. But modules and packages don't have a .dispatch
method...
By default, classes in Perl are left open. That is, you can add more methods later. (However, an application may close them.) For discussion of this, see the section on "Open vs. Closed Classes".
Class Name Semantics
Class names (and module names) are just package names.
Unlike in Perl 5, when you mention a package name in Perl 6 it doesn't always mean a global name, since Perl 6 knows about inner classes and lexically scoped packages and such. As with other entities in Perl such as variables and methods, a scan is made for who thinks they have the best definition of the name, going out from lexical scopes to package scope to global scope in the case of static class names, and via method inheritance rules in the case of virtual class names.
Note that ::MyClass and MyClass mean the same
thing. In Perl 6, an initial :: is merely an optional sigil
for when the name of the package would be misconstrued as something
else. It specifically does not mean (as it does in Perl 5) that it is
a top-level package. To refer to the top-level package, you would need
to say something like ::*MyClass (or just *MyClass
in places where the * unary operator would not be expected.)
But also note that the * package in Perl is not the "main"
package in the Perl 5 sense.
Likewise, the presence of :: within a package name like
Fish::Carp does not make it a global package name necessarily.
Again, it scans out through various scopes, and only if no local scopes
define package Fish::Carp do you get the global definition.
And again, you can force it by saying ::*Fish::Carp. (Or
just *Fish::Carp in places where the * unary
operator is not expected.)
You can interpolate a parenthesized expression within a package name
after any ::. So, these are all legal package names (or
module names, or class names):
::($alice)
::($alice)::($bob)
::($alice::($bob))
::*::($alice)::Bob
::('*')::($alice ~ '_misc')::Bob
::(get_my_dir())
::(@multilevel)
And any of those package names could be part of a variable or sub name:
$::($alice)::name
@::($alice)::($bob)::elems[1,2,3]
%::*::($alice)::Bob::map{'xyz'}
&::('*')::($alice ~ '_misc')::Bob::doit(1,2,3)
$::(get_my_dir())::x
$::(@multilevel)
Note in the last example that the final element of @multilevel
is taken to be the variable name. This may be illegal under use
strict refs, since it amounts to a symbolic reference. (Not that
the others aren't symbolic, but the rules may be looser for package
names than for variable names, depending on how strict our strictures
get.)
Pages: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 |

