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:
- The class of
$brainis 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:thinkmethod. Or... - The class of the
$brainis not declared, and the current class contains a private:thinkmethod.
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 |

