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.

Private Classes

A class named with a single initial colon is a private class name:


    class :MyPrivateClass {...}

It is completely ignored outside of the current class. Since the name is useful only in the current package, it makes no sense to try to qualify it with a package name. While it's an inner class of sorts, it does not override any class name from any other class because it lives in its own namespace (a subnamespace of the current package), and there's no way to tell if the class you're deriving from declares its own private class of the same name (apart from digging through the reflection interfaces).

The colon is orthogonal to the scoping. What's actually going on in this example is that the name is stored in the package with the leading colon, because the colon is part of the name. But if you declared "my class :Golgi" the private name would go into the lexical namespace with the colon. The colon functions a bit like a "private" trait, but isn't really a trait. Wherever you might use a private name, the colon in the name effectively creates a private subspace of names, just as if you'd prefixed it with "_" in the good old days.

But if were only that, it would just be encapsulation by convention. We're trying to do a little better than that. So the language needs to actively prevent people from accessing that private subspace from outside the class. You might think that that's going to slow down all the dispatchers, but probably not. The ordinary dispatch of Class.method and $obj.method don't have to worry about it, because they use bare identifiers. It's only when people start doing ::($class) or $obj.$method that we have to trap illegal references to colonic names.

Even though the initial colon isn't really a trait, if you interrogate the ".private" property of the class, it will return true. You don't have to parse the name to get that info.

We'll make more of this when we talk about private methods and attributes. Speaking of methods...

Methods

Methods are the actions that a class knows how to invoke on behalf of an object of that type (or on behalf of itself, as a class object). But you knew that already.

As in Perl 5, a method is still "just a funny subroutine", but in Perl 6 we use a different keyword to declare it, both because it's better documentation, and because it captures the metadata for the class at compile time. Ordinary methods may be declared only within the scope of a class definition. (Multimethods are exempt from this restriction, however.)

Declaration of Methods

To declare a method, use the method keyword just as you would use sub for an ordinary subroutine. The declaration is otherwise almost identical:


    method doit ($a, $b, $c) { ... }

The one other difference is that a method has an invocant on behalf of which the method is called. In the declaration above, that invocant is implicit. (It is implicitly typed to be the same as the current surrounding class definition.) You may, however, explicitly declare the invocant as the first argument. The declaration knows you're doing that because you put a colon between the invocant and the rest of the arguments:


    method doit ($self: $a, $b, $c) { ... }

In this case, we didn't specify the type of $self, so it's an untyped variable. To make the exact equivalent of the implicit declaration, put the current class:


    method doit (MyClass $self: $a, $b, $c) { ... }

or more generically using the ::_ "current class" pronoun:


    method doit (::_ $self: $a, $b, $c) { ... }

In any case, the method sets the current invocant as the topic, which is also known as the $_ variable. However, the topic can change depending on the code inside the method. So you might want to declare an explicit invocant when the meaning of $_ might change. (For further discussion of topics see Apocalypse 4. For a small writeup on sub signatures see Apocalypse 6.)

A private method is declared with a colon on the front:


    method :think (Brain $self: $thought)

Private methods are callable only by the class itself, and by trusted "friends". More about that when we talk about attributes.

Use of Methods

As in Perl 5, there are two notations for calling ordinary methods. They are called the "dot" notation and the "indirect object" notation.

The dot notation

Perl 6's "dot" notation is just the industry-standard way to call a method these days. (This used to be -> in Perl 5.)


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

If the object in question is the current topic, $_, then you can use the unary form of the dot operator:


    for @objects {
        .doit("a", "b", "c");
    }

A simple variable may be used for an indirectly named method:


    my $dosomething = "doit";
    $object.$dosomething("a", "b", "c");

As in Perl 5, if you want to do anything fancier, use a temporary variable.

The parentheses may also be omitted when the following code is unambiguously a term or operator, so you can write things like this:


    @thumbs.each { .twiddle }   # same as @thumbs.each({.twiddle})
    $thumb.twiddle + 1          # same as $thumb.twiddle() + 1
    .mode 1                     # same as $_.mode(1)

(Parens are always required around the argument list when a method call with arguments is interpolated into a string.)

The parser will make use of whitespace at this point to decide some things. For instance


    $obj.method + 1

is obviously a method with no arguments, while


    $obj.method +1

is obviously a method with an argument. However, the dwimmery only goes as far as the typical person's visual intuition. Any construct too ambiguous is simply rejected. So


    $obj.method+1

produces a parse error.

In particular, curlies, brackets, or parens would be interpreted as postfix subscripts or argument lists if you leave out the space. In other words, Perl 6 distinguishes:


    $obj.method ($x + $y) + $z  # means $obj.method(($x + $y) + $z)

from


    $obj.method($x + $y) + $z   # means ($obj.method($x + $y)) + $z

Yes, this is different from Perl 5. And yes, I know certain people hate it. They can write their own grammar.

While it's always possible to disambiguate with parentheses, sometimes that is just too unsightly. Many methods want to be parsed as if they were list operators. So as an alternative to parenthesizing the entire argument list, you can disambiguate by putting a colon between the method call and the argument list:


    @thumbs.each: { .twiddle }  # same as @thumbs.each({.twiddle})
    $thumb.twiddle: + 1         # same as $thumb.twiddle(+ 1)
    .mode: 1                    # same as $_.mode(1)
    $obj.for: 1,2,3 -> $i { ... }

If a method is declared with the trait "is rw", it's an lvalue method, and you can assign to it just as if it were an ordinary variable:


    method mystate is rw () { return $:secretstate }

    $object.mystate = 42;
    print $object.mystate;      # prints 42

In fact, it's a general rule that you can use an argumentless "rw" method call anywhere you might use a variable:


    temp $state.pi = 3;
    $tailref = \$fido.tail;

(Though occasionally you might need to supply parentheses to disambiguate, since the compiler can't always know at compile time whether the method has any arguments.)

Method calls on container objects are obviously directed to the container object itself, not to the contents of the container:


    $elems = @array.elems;
    @keys  = %hash.keys;
    $sig   = &sub.signature;

However, with scalar variables, methods are always directed to the object pointed to by the reference contained in the scalar:


    $scalar = @array;           # (implied \ in scalar context)
    $elems = $scalar.elems;     # returns @array.elems

or for value types, the appropriate class is called as if the value were a reference to a "real" object.


    $scalar = "foo";
    $chars = $scalar.chars;     # calls Str::chars or some such

In order to talk to the scalar container itself, use the tied() pseudo-function as you would in Perl 5:


    if tied($scalar).constant {...}

(You may recall, however, that in Perl 6 it's illegal to tie any variable without first declaring it as tie-able, or (preferably) tying it directly in the variable's declaration. Otherwise the optimizer would have to assume that every variable has semantics that are unknowable in advance, and we would have to call it a pessimizer rather than an optimizer.)

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

Next Pagearrow