Sign In/My Account | View Cart  
advertisement


Listen Print

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

Next Pagearrow