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.

Parallel Dispatch

By default the various dot operators call a method on a single object, even if it ends up calling multiple methods for that object. Since a method call is essentially a unary postfix operator, however, you can use it as a hyper operator on a list of objects:


    @object».meth(@args)        # Call one for each or fail
    @object».?meth(@args)       # Call one for each if available
    @object».*meth(@args)       # Call all available for each
    @object».+meth(@args)       # Call one or more for each

Note that with the last two, if a method uses "last METHOD", it doesn't bomb out of the "hyper" loop, but just goes on to the next entry. One can always bomb out of the hyperloop with a real exception, of course. And maybe with "last HYPER", depending on how hyper's implicit iteration is implemented.

If you want to use an array for serial rather than parallel method calling, see Delegation, which lets you set up cascading handlers.

WALKCLASS and WALKMETH Caching

WALKCLASS generates a list of matching classes. WALKMETH generates a list of method references from matching classes.

The WALKCLASS and WALKMETH routines used in the sample dispatch code need to cache their results so that every dispatch doesn't have to traverse the inheritance tree again, but just consult the preconstructed list in order. However, if there are changes to any of the classes involved, then someone needs to call the appropriate cache clear method to make sure that the inheritance is recalculated.

WALKCLASS/WALKMETH options include some that specify ordering:


    :canonical      # canonical dispatch order
    :ascendant      # most-derived first, like destruction order
    :descendant     # least-derived first, like construction order
    :preorder       # like Perl 5 dispatch
    :breadth        # like multimethod dispatch

and some that specify selection criteria:


    :super              # only immediate parent classes
    :method(Str)        # only classes containing method declaration
    :omit(Selector)     # only classes that don't match selector
    :include(Selector)  # only classes that match selector

Note that :method(Str) selects classes that merely have methods declared, not necessarily defined. A declaration without a definition probably implies that they intend to autoload a definition, so we should call the stub anyway. In fact, Perl 6 differentiates an AUTOMETHDEF from AUTOLOAD. AUTOLOAD works as it does in Perl 5. AUTOMETHDEF is never called unless there is already a declaration of the stub (or equivalently, AUTOMETH faked a stub.)

It would be possible to just define everything in terms of WALKCLASS, but that would imply looking up each method name twice, once inside WALKCLASS to see if the method exists in the current class, and once again outside in order to call it. Even if WALKCLASS caches the cache list, it wouldn't cache the derived method list, so it's better to have a separate cache for that, controlled by WALKMETH, since that's the common case and has to be fast.

(Again, this is all abstract, and is probably implemented in gloriously grungy C code. Nevertheless, you can probably call WALKCLASS and WALKMETH yourself if you feel like writing your own dispatcher.)

Multiple Dispatch

Multiple dispatch is based on the notion that methods often mediate the relationships of multiple objects of diverse types, and therefore the first object in the argument list should not be privileged over other objects in the argument list when it comes to selecting which method to run. In this view, methods aren't subservient to a particular class, but are independent agents. A set of independent-minded, identically named methods use the class hierarchy to do pattern matching on the argument list and decide among themselves which method can best handle the given set of arguments.

The Perl approach is, of course, that sometimes you want to distinguish the first invocant, and sometimes you don't. The interaction of these two approaches gets, um, interesting. But the basic notion is to let the caller specify which approach is expected, and then, where it makes sense, fall back on the other approach when the first one fails. Underlying all this is the Principle of Least Surprise. Do not confuse this with the Principle of Zero Surprise, which usually means you've just swept the real surprises under some else's carpet. (There's a certain amount of surprise you can't go below--the Heisenberg Uncertainty Principle applies to software too.)

With traditional multimethods, all methods live in the same global namespace. Perl 6 takes a different approach--we still keep all the traditional Perl namespaces (lexical, package, global) and we still search for names the same way (outward through the lexical scopes, then the current package, then the global * namespace; or upward in the class hierarchy). Then we simply claim that, under multiple dispatch, the "long name" of any multi routine includes its signature, and that visibility is based on the long name. So an inner or derived multi only hides an outer or base multi of the same name and the same signature. (Routines not declared "multi" still hide everything in the traditional fashion.)

To put it another way, the multiple dispatch always works when both the caller and the callee agree that that's how it should work. (And in some cases it also works when it ought to work, even if they don't agree--sort of a "common law" multimethod, as it were...)

Declaration of Multiple Dispatch Routines

A callee agrees to the multiple dispatch "contract" by including the word "multi" in the declaration of the routine in question. It essentially says, "Ordinarily this would be a unique name, but it's okay to have duplicates of this name (the short name) that are differentiated by signatures (the long name)."

Looking at it from the other end, leaving the "multi" out says "I am a perfect match for any signature--don't bother looking any further outward or upward." In other words, the standard non-multi semantics.

You may not declare a multi in the same scope as a non-multi. However, as long as they are in different scopes, you can have a single non-multi inside a set of multis, or a set of multis inside a single non-multi. You can even have a set of multis inside a non-multi inside a set of multis. Indeed, this is how you hide all the outer multis so that only the inner multi's long names are considered. (And if no long name matches, you get the intermediate non-multi as a kind of backstop.) The same policy applies to both nested lexical scopes and derived subclasses.

Actually, up till now we've been oversimplifying the concept of "long name" slightly. The long name includes only that part of the signature up to the first colon. If there is no colon, then the entire signature is part of the long name. (You can have more colons, in which case the additional arguments function as tie breakers if the original set of long names is insufficient to prevent a tie.)

So sometimes we'll probably slip and say "signature" when we mean "long name". We pray your indulgence.

multi sub

A multi sub in any scope hides any multi sub with the same "long name" in any outer scope. It does not hide subs with the same short name but a different signature. Er, long name, I mean...

multi sub * (tradition multimethods)

If you want a multi that is visible in all namespaces (that don't hide the long name), then declare the name in the global name space, indicated in Perl 6 with a *. Most of the so-called "built-ins" are declared this way:


    multi sub *push (Array $array, *@args) {...}
    multi sub *infix:+ (Num $x, Num $y) returns Num {...}
    multi sub *infix:.. (Int $x, Int $y: Int ?$by) returns Ranger {...}

Note the use of colon in the last example to exclude $by as part of the long name. The range operator is dispatched only on the types of its two main arguments.

multi method

If you declare a method with multi, then that method hides any base class method with the same long name. It does not hide methods with the same short name but a different signature when called as a multimethod. (It does hide methods when called under single dispatch, in which case the first invocant is treated as the only invocant regardless of where you put the colon. Just because a method is declared with multi doesn't make it invisible to single dispatch.)

Unlike a regular method declaration, there is no implied invocant in the syntax of a multi method. A method declared as multi must declare all its invocants so that there's no ambiguity as to the meaning of the first colon. With a multi method, it always means the end of the long name. (With a non-multi, it always means that the optional invocant declaration is present.)

multi submethod

Submethods may be declared with multi, in which case visibility works the same as for ordinary methods. However, a submethod has the additional constraint that the first invocant must be an exact class match. Which effectively means that a submethod is first single dispatched to the class, and then the appropriate submethod within that class is selected, ignoring any other class's submethods of the same name.

multi rule

Since rules are just methods in disguise, you can have multi rules as well. (Of course, that doesn't do you a lot of good unless you have rules with different signatures, which is unusual.)

multi submethod BUILD

It is not likely that Perl 6.0.0 will support multiple dispatch on named arguments, but only on positional arguments. Since all the extra arguments to a BUILD routine come in as named arguments, you probably can't usefully multi a BUILD (yet). However, we should not do anything that precludes multiple BUILD submethods in the future. Which means we should probably enforce the presence of a colon before the first named argument declaration in any multi signature, so that the semantics don't suddenly change if and when we start supporting multiple dispatch that includes named arguments as part of the long name.

multi method constructors

To the extent that you declare constructors (such as .new) with positional arguments, you can use multi on them in 6.0.0.

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

Next Pagearrow