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.

The "indirect object" notation

The other form of method call is known as the "indirect object" syntax, although it differs from Perl 5's syntax in that a colon is required between the indirect object (the invocant) and its arguments:


    doit $object: "a", "b", "c"

The colon may be omitted if there are no arguments (besides the invocant):


    twiddle $thumb;
    $x = new X;

Note that indirect object calls may not be directly interpolated into a string, since they don't start with a sigil. You can always use the $() expression interpolater though:


    say "$(greet $lang), world!";

As in Perl 5, the indirect object syntax is valid only if you haven't declared a subroutine locally that overrides the method lookup. That was a bit of a problem in Perl 5 since, if there happened to be a new constructor in your class, it would call that instead dispatching to the class you wanted it to. That's much less of a problem in Perl 6, however, because Perl 6 cannot confuse a method declaration with a subroutine declaration. (Which is yet another reason for giving methods their own keyword.)

Another factor that makes indirect objects work better in Perl 6 is that the class name in "new X" is a predeclared object, not a bare identifier. (Perl 5 just had to guess when it saw two bare identifiers in a row that you were trying to call a class method.)

The indirect object syntax may not be used with a variable for the methodname. You must use dot notation for that.

Because of precedence, the indirect object notation may not be used as an lvalue unless you parenthesize it:


    (mystate $object) = 42;
    (findtail Dog: "fido") = Wagging::on;

You may parenthesize an argumentless indirect object method to make it look like a function:


    mystate($object) = 42;
    twiddle($thumb);

The dispatch rules for methods and global multi subs conspire to keep these unambiguous, so the user really doesn't have to worry about whether


    close($handle);

is implemented as a global multi sub or a method on a $handle object. In essence, the multimethod dispatching rules degenerate to ordinary method dispatch when there are no extra arguments to consider (and sometimes even when there are arguments). This is particularly important because Perl uses these rules to tell the difference between


    print "Howdy, world!\n";    # global multi sub

and


    print $*OUT;                # ordinary filehandle method

However, you must still put the colon after the invocant if there are other arguments. The colon tells the parser whether to look for the arguments inside:


    doit($object: "a", "b", "c")

or outside:


    doit($object): "a", "b", "c"

If you do say


    doit($object, "a", "b", "c")

the first comma forces it to be interpreted as a sub call rather than a method call.

(We could have decided to say that whenever Perl can't find a doit() sub definition at runtime, it should assume you meant the entire parenthesized list to be the indirect object, which, since it's in scalar context would automatically generate a list reference and call [$object,"a","b","c"].doit(), which is unlikely to be what you mean, or even work. (Unless, of course, that's how you really meant it to work.) But I think it's much more straightforward to simply disallow comma lists at the top level of an indirect object. The old "if it looks like a function" rule applies here. Oddly, though, function syntax is how you call multisubs in Perl 6. And as it happens, the way the multisub/multimethod dispatch rules are defined, it could still end up calling $object.doit("a", "b", "c") if that is deemed to be the best choice among all the candidates. But syntactically, it's not an indirect object. More on dispatch rule interactions later.)

The comma still doesn't work if you go the other way and leave out the parens entirely, since


    doit $object, "a", "b", "c";

would always (in the absence of a prior sub declaration) be parsed as


    (doit $object:), "a", "b", "c";

So a print with both an indirect object and arguments has to look like one of these:


    print $*OUT: "Howdy, world!\n";
    print($*OUT: "Howdy, world!\n");
    print($*OUT): "Howdy, world!\n";

Note that the old Perl 5 form using curlies:


    print {some_hairy_expression()} "Howdy, world!\n";

should instead now be written with parentheses:


    print (some_hairy_expression()): "Howdy, world!\n";

though, in fact, in this case the parens are unnecessary:


    print some_hairy_expression(): "Howdy, world!\n";

You'd only need the parens if the invocant expression contained operators lower in precedence than comma (comma itself not being allowed). Basically, if it looks confusing to you, you can expect it to look confusing to the compiler, and to make the compiler look confused. But it's a feature for the compiler to look confused when it actually is confused. (In Perl 5 this was not always so.)

Note that the disambiguating colon associates with the closest method call, whether direct or indirect. So


    print $obj.meth: "Howdy, world!\n";

passes "Howdy, world!\n" to $obj.meth rather than to print. That's a case where you ought to have parenthesized the indirect object for clarity anyway:


    print ($obj.meth): "Howdy, world!\n";
Calling private methods

A private method does not participate in normal method dispatch. It is not listed in the class's public methods. The .can method does not see it. Calling it via normal dispatch raises a "no such method" exception. It is, in essence, invisible to the outside world. It does not hide a base class's method of the same name--even in the current class! It's fair to ask for warnings about name collisions, of course. But we're not following the C++ approach of making private methods visible but uncallable, because that would violate encapsulation, and in particular, Liskov substitutability. Instead, we separate the namespaces completely by distinguishing the public dot operator from the private dot-colon operator. That is:


    $mouth.say("Yes!")          # always calls public .say method
          .say("Yes!")          # unary form
    $brain.:think("No!")        # always calls private :think method
          .:think("No!")        # unary form

The inclusion of the colon prevents any kind of "virtual" behavior. Calling a private method is illegal except under two very specific conditions. You can call a private method :think on an object $brain only if:

  1. The class of $brain is explicitly declared, and the declared class is either the class definition that we are in or a class that has explicitly granted trust to our current class, and the declared class contains a private :think method. Or...
  2. The class of the $brain is not declared, and the current class contains a private :think method.

The upshot of these rules is that a private method call is essentially a subroutine call with a method-like syntax. But the private method we're going to call can be determined at compile time, just like a subroutine.

Class Methods

Class methods are called on the class as a whole rather than on any particular instance object of the class. They are distinguished from ordinary methods only by the declared type of the invocant. Since an implicit invocant would be typed as an object of the class and not as the class itself, the invocant declaration is not optional in a class method declaration if you wish to specify the type of the invocant. (Untyped explicit invocants are allowed to "squint", however.)

Class Invocant

To declare an ordinary class method, such as a constructor, you say something like:


    method new (Class $class: *@args) { ... }

Such a method may only be called with an invocant that "isa" Class, that is, an object of type Class, or derived from type Class.

Class|Object Invocant

It is possible to write a method that can be called with an invocant that is either a Class or an object of that current class. You can declare the method with a type junction:


    method new (Class|Dog $classorobj: *@args) { ... }

Or to be completely non-specific, you can leave out the type entirely:


    method new ($something: *@args) { ... }

That's not as dangerous as it looks, since almost by definition the dispatcher only calls methods that are consistent with the inheritance tree. You just can't say:


    method new (*@args) { ... }

which would be the equivalent of:


    method new (Dog $_: *@args) { ... }

Well, actually, you could say that, but it would require that you have an existing Dog-compatible object in order to create a new one. And that could present a little bootstrapping problem...

(Though it could certainly cure the boot chewing problem...)

But in fact, you'll rarely need to declare new method at all, because Perl supplies a default constructor to go with your class.

Submethods

Some methods are intended to be inherited by derived classes. Others are intended to be reimplemented in every class, or in every class that doesn't want the default method. We call these "submethods", because they work a little like subs, and a little like methods. (You can also read the "sub" with the meaning it has in words like "subhuman".)

Typically these are (sub)methods related to the details of construction and destruction of the object. So when you call a constructor, for instance, it ends up calling the BUILDALL initialization routine for the class, which ends up calling the BUILD submethod:


    submethod BUILD ($a, $b, $c) {
        $.a = $a;
        $.b = $b;
        $.c = $c;
    }

Since the submethod is doing things that make sense only in the context of the current class (such as initializing attributes), it makes no sense for BUILD to be inherited. Likewise DESTROY is also a submethod.

Why not just make them ordinary subs, then? Ordinary subs can't be called by method invocation, and we want to call these routines that way. Furthermore, if your base class does define an ordinary method named BUILD or DESTROY, it can serve as the default BUILD or DESTROY for all derived classes that don't declare their own submethods. (All public methods are virtual in Perl, but some are more virtual than others.)

You might be saying to yourself, "Wait, private methods aren't virtual. Why not just use a private method for this?" It's true that private methods aren't virtual, because they aren't in fact methods at all. They're just ordinary subroutines in disguise. They have nothing to do with inheritance. By contrast, submethods are all about presenting a unified inherited interface with the option of either inheriting or not inheriting the implementation of that interface, at the discretion of the class doing the implementing.

So the bottom line is that submethods allow you to override an inherited implementation for the current class without overriding the default implementation for other classes. But in any case, it's still using a public interface, called as an ordinary method call, from anywhere in your program that has an object of your type.

Or a class of your type. The default new constructor is an ordinary class method in class Object, so it's inherited by all classes that don't define their own new. But when you write your own new, you need to decide whether your constructor should be inherited or not. If so, that's good, and you should declare it as a method. But if not, you should declare it as a submethod so that derived classes don't try to use it erroneously instead of the default Object.new().

Pages: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20

Next Pagearrow