Sign In/My Account | View Cart  
advertisement


Listen Print

Apocalypse 6
by Larry Wall | Pages: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16

Editor's Note: this Apocalypse is out of date and remains here for historic reasons. See Synopsis 06 for the latest information.

Sublist formals

It's often the case that you'd like to treat a single array argument as if it were an argument list of its own. Well, you can. Just put a sublist signature in square brackets. This is particularly good for declaring multimethods in a functional programming mindset:


    multi apply (&func, []) { }
    multi apply (&func, [$head, *@tail]) {
        return func($head), apply(&func, @tail);
    }

    @squares := apply { $_ * $_ } [1...];

Of course, in this case, the first multimethod is never called because the infinite list is never null no matter how many elements we pull off the front. But that merely means that @squares is bound to an infinite list generator. No big deal, as long as you don't try to flatten the list...

Note that, unlike the example in the previous section which alternated strings and integers, this:


    strintlist( [Str, Int] *@strints ) { ... }

implies single array references coming in, each containing a string and an integer.

Of course, this may be a bad example insofar as we could just write:


    multi apply (&func) { }
    multi apply (&func, $head, *@tail) {
        return func($head), apply(&func, *@tail);
    }

    @squares := apply { $_ * $_ } *1...;

It'd be nice to lose the * though on the calls. Maybe what we really want is a slurpy scalar in front of the slurpy array, where presumably the <== maps to the first slurpy scalar or hash (or it could be passed positionally):


    multi apply (&func) { }
    multi apply (&func, *$head, *@tail) {
        return func($head), apply(&func <== @tail);
    }

    @squares := apply { $_ * $_ } 1...;

Yow, I think I could like that if I tried.

So let's say for now that a slurpy scalar parameter just pulls the first (or next) value off of the the slurpy list. The [] notation is still useful though for when you really do have a single array ref coming in as a parameter.

Attributive parameters

It is typical in many languages to see object initializers that look like this (give or take a keyword):


    function init (a_arg, b_arg, c_arg) {
        a = a_arg;
        b = b_arg;
        c = c_arg;
    }

Other languages try to improve the situation without actually succeeding. In a language resembling C++, it might look more like this:


    method init (int a_arg, int b_arg, int c_arg)
        : a(a_arg), b(b_arg), c(c_arg) {}

But there's still an awful lot of redundancy there, not to mention inconsistent special syntax.

Since (as proven by Perl 5) signatures are all about syntactic sugar anyway, and since Perl 6 intentionally makes attribute variables visually distinct from ordinary variables, we can simply write this in Perl 6 as:


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

Any parameter that appears to be an attribute is immediately copied directly into the corresponding object attribute, and no lexical parameter is generated. You can mix these with ordinary parameters--the general rule of thumb for an initializer is that you should see each dotted attribute at least once:


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

This feature is primarily intended for use in constructors and initializers, but Perl does not try to guess which subroutines fall into that category (other than the fact that Perl 6 will implicitly call certain conventional names like CREATE and BUILD.)

However, submethods such as BUILD are assumed to have an extra *%_ parameter to soak up any extra unrecognized named arguments. Ordinarily you must declare a slurp-hash explicitly to get that behavior. But BUILD submethods are always called with named arguments (except for the invocant), and often have to ignore arguments intended for other classes participating in the current construction. It's likely that this implicit *%_ feature extends to other routines declared in all-caps as well, and perhaps all submethods.

As in Perl 5, subroutines declared in all-caps are expected to be called automatically most of the time--but not necessarily all the time. The BUILD routine is a good example, because it's only called automatically when you rely on the default class initialization rules. But you can override those rules, in which case you may have to call BUILD yourself. More on that in Apocalypse 12. Or go to one of Damian's Perl 6 talks...

Other kinds of subroutines

Closures

All blocks are considered closures in Perl 6, even the blocks that declare modules or classes (presuming you use the block form). A closure is just an anonymous subroutine that has access its lexical context. The fact that some closures are immediately associated with names or have other kinds of parameter declarations does not change the fact that an anonymous bare block without parameters is also a kind of subroutine. Of course, if the compiler can determine that the block is only executed inline, it's free to optimize away all the subroutine linkage--but not the lexical linkage. It can only optimize away the lexical linkage if no external lexicals are accessed (or potentially accessed, in the case of eval).

Pointy subs

As introduced in Apocalypse 4, loops and topicalizers are often written with a special form of closure declaration known these days as "pointy subs". A pointy sub is exactly equivalent to a standard anonymous sub declaration having the same parameters. It's almost pure syntactic sugar--except that we embrace syntactic sugar in Perl when it serves a psychological purpose (not to be confused with a logical psycho purpose, which we also have).

Anyway, when you say:


    -> $a, $b, $c { ... }

it's almost exactly the same as if you'd said:


    sub ($a, $b, $c) { ... }

only without the parentheses, and with the cute arrow that indicates the direction of data flow to that part of your brain that consumes syntactic glucose at a prodigious rate.

Since the parentheses around the signature are missing, you can't specify anything that would ordinarily go outside the parentheses, such as the return type or other subroutine traits. But you may still put traits or zone markers on each individual formal parameter.

Also, as a "sub-less" declaration, you can't return from it using return, because despite being a closure, it's supposed to look like a bare Block embedded in a larger Routine, and users will expect return to exit from the "real" subroutine. All of which just means that, if you need those fancy extras, use a real sub sub, not a pointy one.

Placeholders

Also as discussed in Apocalypse 4, a bare block functioning as a closure can have its parameters declared internally. Such parameters are of the form:


    rule placeholder { <sigil> \^ <ident> }

Placeholder parameters are equivalent to required position parameters declared in alphabetical order. (Er, Unicodical order, really.) For example, the closure:


    { $^fred <=> $^barney }

has the same signature as the pointy sub:


    -> $barney, $fred { $fred <=> $barney }

or the standard anonymous sub:


    sub ($barney, $fred) { $fred <=> $barney }

On first hearing about the alphabetical sorting policy, some otherwise level-headed folks immediately panic, imagining all sorts of ways to abuse the mechanism for the purposes of obfuscation. And surely there are many ways to abuse many of the features in Perl, more so in Perl 6. The point of this mechanism, however, is to make it drop-dead easy to write small, self-contained closures with a small number of parameters that you'd probably give single-character alphabetical names to in any event. If you want to get fancier than that, you should probably be using a fancier kind of declaration. I define "small number" as approximately e ± π. But as is generally the case in Perl, you get to pick your own definition of "small number". (Or at the very least, you get to pick whether to work with a company that has already defined "small number" for you.)

As bare rvalue variables embedded in the code, you may not put any traits or zone markers on the placeholders. Again, the desire to do so indicates you should be using a fancier form of declaration.

Methods

Perl 5 just used subroutines for methods. This is okay as long as you don't want to declare any utility subroutines in your class. But as soon as you do, they're inherited in Perl 5, which is not what you want. In Perl 6, methods and subroutines still share the same namespace, but a method must be declared using the method keyword. This is good documentation in any event, and further allows us to intuit an invocant where none is declared. (And we know that none is declared if there's no colon after the first argument, at least in the case of an ordinary method.)

Submethods

There are certain implementation methods that want to be inherited in general so that you can specify a default implementation, but that you want the class to be able to override without letting derived classes inherit the overridden method from this class. That is, they are scoped like utility subroutines, but can be called as if they are methods, without being visible outside the class. We call these hybrids "submethods", and so there's a submethod keyword to declare them. Submethods are simultaneously subs and methods. You can also think of them as something less than a method, as the "sub" works in the word "subhuman". Or you can think of them as underneath in the infrastructural sense, as in "subterranean".

Routines that create, initialize, or destroy the current object tend to fall into this category. Hence, the BUILD routine we mentioned earlier is ordinarily declared as a submethod, if you don't want to inherit the standard BUILD method defined in the Object class. But if you override it, your children still inherit BUILD from Object.

Contrariwise, if you don't like Object's default BUILD method, you can define an entire new class of classes that all default to your own BUILD method, as long as those classes derive from your new base object with superior characteristics. Each of those derived classes could then define a submethod to override your method only for that class, while classes derived from those classes could still inherit your default.

And so on, ad OOium.

Multimethods

Some kinds of programming map easily onto the standard model in which a method has a single invocant. Other kinds of programming don't. Perl 6 supplies support for the latter kind of programming, where the relationships between classes are just as interesting as the classes themselves. In some languages, all methods are multimethods. Perl 6 doesn't go quite that far--you must declare your multimethods explicitly. To do so, use the multi keyword in place of method, and optionally place a colon after the list of invocants in the declaration, unless you want them all to be invocants. Then your multimethod will be registered globally as a being of interest to all the types of its invocants, and will participate in multimethod dispatch.

It is beyond the scope of this Apocalypse to specify exactly how multimethod dispatch works (see Apocalypse 12, someday), but we can tell you that, in general, you call a multimethod as if it were an ordinary subroutine, and the dispatcher figures out on your behalf how many of the arguments are invocants. This may sound fancy to you, but many of the functions that are built into Perl 5 are not built into Perl 6, at least, not as keywords. Instead they are either defined as global subroutines or as multimethods, single invocant multimethods in many cases. When you call a function like close($handle), it'll first look to see if there's a close subroutine defined in your scope, and if not, it will dispatch it as a multimethod. Likewise, for something like sysread, you can call it either as a method:


    sysread $handle: $buffer, $length

or as a function:


    sysread $handle, $buffer, $length

In the first case, it's explicitly dispatching on the handle, because a colon in place of the first comma indicates an invocant. (That's our new indirect object syntax, in fact. Perl 6 does not support the Perl 5 syntax of just leaving whitespace between the indirect object and the subsequent arguments.)

In the second case, it looks for a sysread subroutine, doesn't find it (we hope), and calls multimethod dispatch on it. And it happens that the multimethod dispatch is smart enough to find the ordinary single-invocant sysread method, even though it may not have been explicitly declared a multimethod. Multimethod dispatch happens to map directly onto ordinary method dispatch when there's only one invocant.

At least, that's how it works this week...

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

Next Pagearrow