| From: |
| Tom Christiansen <tchrist-AT-perl.com> |
| To: |
| Jesse Luehrs <doy-AT-tozt.net> |
| Subject: |
| Re: indirect object syntax (was Re: Revising the OO docs - take 2) |
| Date: |
| Sun, 10 Jul 2011 21:25:56 -0600 |
| Message-ID: |
| <22081.1310354756@chthon> |
| Cc: |
| perl5-porters-AT-perl.org |
| Archive-link: |
| Article, Thread
|
Please do not try to excise dative syntax for the documentation.
We certainly shall not be doing so in the 4th edition of the Camel.
In 3E, the section on method invocation had these subsections,
which I've numbered here for your convenience:
Method Invocation
1. Method Invocation Using the Arrow Operator
INVOCANT->METHOD(LIST)
INVOCANT->METHOD
2. Method Invocation Using Indirect Objects
METHOD INVOCANT (LIST)
METHOD INVOCANT LIST
METHOD INVOCANT
3. Syntactic Snafus with Indirect Objects
move $party->{LEADER}; # probably wrong!
move $riders[$i]; # probably wrong!
4. Package-Quoted Classes
$obj = method CLASS::; # forced to be "CLASS"->method
I had initially wanted to get rid of the whole kit&kaboodle, but
Larry talked me out of it, pointing out that if you follow normal
practices, you won't be getting yourself into trouble. The only one
that bothers me is that you can't easily chain them. Apart from
that, I kinda like putting my commands before their objects.
Plus I don't have to figure out whether to use vocative or ablative. :)
[vocative] O Object: do this thing!
[ablative] With this Object, do this thing.
I'm used to putting the command first in any imperative programming
language, or in English imperative statements. I'm a verb?first
commander who finds that
Take charge of your own affairs.
Get thee to a nunnery.
Go to hell.
say STDERR "You are expected to enjoy this."
@nums = sort { $b <=> $a } @nums;
consider $him "méfiant";
all read better in English than do these reformulations:
Of your own affairs take charge.
To a nunnery get thee.
To hell go.
STDERR->say("You are expected to enjoy this.");
@nums = { $b <=> $a }->sort(@nums);
$him->consider("méfiant");
See what I mean? It screws things up because it demotes the most
important element from its natural position right at the front,
essentially leaving us nonsense the likes of
"In its interior the part of a sentence
that's important hide should not one!"
That's how this stuff all reads to me. In other words, pretending
that our (S)VO language is an OV one makes it gratuitously hard to read.
Must we really do that? This shouldn't be Latin, dangnabbit!
Programming is tough enough; don't go putting pretending the object is
more important than the verb, for goodness' sake! Verbs verbs verbs.
[
Just *what* kind of object that is, I leave to your own meditation.
Grammatically, it is seldom a direct object in the accusative case. It is
more apt to be something like an indirect object in the dative case, but
many times it is actually a "dative of interest", which is something of a
vestige of Latin's ablative case or the old PIE intrumental case, neither of
which is familiar to most modern readers of English who couldn't tell a
dative from an accusative if it threw them out the window a new curve ball.
A Spaniard for example doesn't drop a plate on the floor; rather,
the plate lets itself fall off the table to the floor on him, where
the him part there is something of a dative of interest. "Se me
cayó el plato" is literally "The plate fell itself on/to/at/from
me." It's just like how we in English are wont to say "It broke
on me" instead of saying "I broke it". Guilt avoidance, eh. :)
]
It's bad enough that we go swapping around VO into OV, but it's still worse
that we turn VID into IVD (where I=indirect object and D=direct object)
Nobody talks that way in English, which makes our programs read *really*
poorly: it makes me feel like I'm Latin or German reading. It's just
plain unnatural.
I feel the perils of using a Perl object or class to be overstated.
Yes, they *can* happen if you do things in an abnormal fashion,
but using the infix arrow does not solve those problems!
*That's* why you always need to use package-quoting notation for your class
names, even *with* an infix arrow. Nobody but me does this, alas. But
because I do, the following has absolutely no ambiguities in it:
my $collator = new Unicode::Collate::
level => 1,
normalization => undef,
## variable => "non-ignorable",
### pick from these 4 possible values:
### "non-ignorable",
### "blanked",
### "shifted",
### "shift-trimmed",
;
I rather like the way it looks declarative with the double?colon postfix
string?quoter there. I'm big on vertical alignment, in case you hadn't
noticed this in my various code snipulets yet. I find it guides the eye
and thence the mind to a quicker and more accurate understanding. There
are a lot such examples in my Unicode scripts examples programs I include
with my OSCON talks, although admittedly some of those are of venerable
vintage and are not so homogeneous in style as I'd've preferred.
Using that collator, when I wrote this:
next unless my @hits = gmatch $collator $name => $search;
there is still zero ambiguity. And it puts the "globally match"
verb in sentence-initial position where it belongs in English
imperatives. It's also less punctuation/symbol intensive.
So what's the gripe, anyway? SHEESH!
The most infamous corner case is stuff like:
new Something;
clone $whatever;
where clone or new is already an in?scope sub by the same name as some
clone or new method belonging to $whatever. The problem disappears if
you provide arguments:
clone $whatever accuracy => "98%";
Also, anybody who isn't package-quoting is playing with fire,
and the arrow-thingie won't help you!
Plus what you're all calling "indirect object syntax" is wrong anyway.
Here in all cases we have an identifier that represents an object directly:
print STDERR "my stuff here\n"; # not an indirect object
STDERR->print("my stuff here\n"); # not an indirect object
STDERR->$meth("my stuff here\n"); # direct object w/indirect method
Those are both direct uses of an object, and the arrow is immaterial to
that. It's the dollar sign that matters. That's why in the final case,
it's the method that's indirected, not the object, which is still not an
indirect one.
Here again in all cases we have an identifier that represents an
object indirectly:
print $fh "my stuff here\n"; # this is an indirect object
$fh->print("my stuff here\n"); # this is an indirect object
$fh->$meth("my stuff here\n"); # indirect object w/indirect method
Again, those are indirect uses of the object, again irrespective of the bloody arrow.
Which is why I'm changing the documentation to stop lying about
what is and what is not an "indirect" object. Whether you get
"dative syntax" instead is uncertain. If I get irritated it might
just be instrumentive or something.
Another pet peeve is dying the death this release. Yes, that's right:
"lexical filehandles" are also getting the axe, because they're a lie.
*THIS* is a lexical filehandle:
my $fh = \*STDERR;
And this is *not* a lexical filehandle:
open($main::FAIL = undef, "> /dev/null");
I'll let you see what they really get called when the book sees
print. But at least it won't be lying the way so much of the
documentation does. Say what you mean and mean what you say.
Bedtime.
--tom
=============================
Camel3 Excerpt on This Matter
=============================
Here follows the pod2texted section that talks about putting the
command-method before the object. This is expected to be updated,
but not deleted.
Method Invocation
If you were to boil down all of object-oriented programming into one
quintessential notion, it would be *abstraction*. It's the single
underlying theme you'll find running through all those 10-dollar words
that OO enthusiasts like to bandy about, like polymorphism and
inheritance and encapsulation. We believe in those fancy words, but
we'll address them from the practical viewpoint of what it means to
invoke methods. Methods lie at the heart of the object system because
they provide the abstraction layer needed to implement all these fancy
terms. Instead of directly accessing a piece of data sitting in an
object, you invoke an instance method. Instead of directly calling a
subroutine in some package, you invoke a class method. By interposing
this level of indirection between class use and class implementation,
the program designer remains free to tinker with the internal workings
of the class, with little risk of breaking programs that use it.
Perl supports two different syntactic forms for invoking methods. One
uses a familiar style you've already seen elsewhere in Perl, and the
second is a form you may recognize from other programming languages. No
matter which form of method invocation is used, the subroutine
constituting the method is always passed an extra initial argument. If a
class is used to invoke the method, that argument will be the name of
the class. If an object is used to invoke the method, that argument will
be the reference to the object. Whichever it is, we'll call it the
method's *invocant*. For a class method, the invocant is the name of a
package. For an instance method, the invocant is a reference that
specifies an object.
In other words, the invocant is whatever the method was invoked *with*.
Some OO literature calls this the method's *agent* or its *actor*.
Grammatically, the invocant is neither the subject of the action nor the
receiver of that action. It's more like an indirect object, the
beneficiary on whose behalf the action is performed--just like the word
"me" in the command, "Forge me a sword!" Semantically, you can think of
the invocant as either an invoker or an invokee, whichever fits better
into your mental apparatus. We're not going to tell you how to think.
(Well, not about that.)
Most methods are invoked explicitly, but methods may also be invoked
implicitly when triggered by object destructors, overloaded operators,
or tied variables. Properly speaking, these are not regular subroutine
calls, but rather method invocations automatically triggered by Perl on
behalf of an object. Destructors are described later in this chapter,
overloading is described in CHP-13, and ties are described in CHP-14.
One difference between methods and regular subroutines is when their
packages are resolved--that is, how early (or late) Perl decides which
code should be executed for the method or subroutine. A subroutine's
package is resolved during compilation, before your program begins to
run.More precisely, the subroutine call is resolved down to a particular
typeglob, a reference to which is stuffed into the compiled opcode tree.
The meaning of that typeglob is negotiable even at run time--this is how
"AUTOLOAD" can autoload a subroutine for you. Normally, however, the
meaning of the typeglob is also resolved at compile time by the
definition of an appropriately named subroutine. In contrast, a method's
package isn't resolved until it is actually invoked. (Prototypes are
checked at compile time, which is why regular subroutines can use them
but methods can't.)
The reason a method's package can't be resolved earlier is relatively
straightforward: the package is determined by the class of the invocant,
and the invocant isn't known until the method is actually invoked. At
the heart of OO is this simple chain of logic: once the invocant is
known, the invocant's class is known, and once the class is known, the
class's inheritance is known, and once the class's inheritance is known,
the actual subroutine to call is known.
The logic of abstraction comes at a price. Because of the late
resolution of methods, an object-oriented solution in Perl is likely to
run slower than the corresponding non-OO solution. For some of the
fancier techniques described later, it could be a *lot* slower. However,
many common problems are solved not by working faster, but by working
smarter. That's where OO shines.
Method Invocation Using the Arrow Operator
We mentioned that there are two styles of method invocation. The first
style for invoking a method looks like this:
INVOCANT->METHOD(LIST)
INVOCANT->METHOD
For obvious reasons, this style is usually called the arrow form of
invocation. (Do not confuse "->" with "=>", the "double-barreled" arrow
used as a fancy comma.) Parentheses are required if there are any
arguments. When executed, the invocation first locates the subroutine
determined jointly by the class of the INVOCANT and the METHOD name, and
then calls that subroutine, passing INVOCANT as its first argument.
When INVOCANT is a reference, we say that METHOD is invoked as an
instance method, and when INVOCANT is a package name, we say that METHOD
is invoked as a class method. There really is no difference between the
two, other than that the package name is more obviously associated with
the class itself than with the objects of the class. You'll have to take
our word for it that the objects also know their class. We'll tell you
in a bit how
to associate an object with a class name, but you can use objects
without knowing that.
For example, to construct an object using the class method "summon" and
then invoke the instance method "speak" on the resulting object, you
might say this:
$mage = Wizard->summon("Gandalf"); # class method
$mage->speak("friend"); # instance method
The "summon" and "speak" methods are defined by the "Wizard" class--or
one of the classes from which it inherits. But you shouldn't worry about
that. Do not meddle in the affairs of Wizards.
Since the arrow operator is left associative (see CHP-3), you can even
combine the two statements into one:
Wizard->summon("Gandalf")->speak("friend");
Sometimes you want to invoke a method without knowing its name ahead of
time. You can use the arrow form of method invocation and replace the
method name with a simple scalar variable:
$method = "summon";
$mage = Wizard->$method("Gandalf"); # Invoke Wizard->summon
$travel = $companion eq "Shadowfax" ? "ride" : "walk";
$mage->$travel("seven leagues"); # Invoke $mage->ride or $mage->walk
Although you're using the name of the method to invoke it indirectly,
this usage is not forbidden by "use strict 'refs'", since *all* method
calls are in fact looked up symbolically at the time they're resolved.
In our example, we stored the name of a subroutine in $travel, but you
could also store a subroutine reference. This bypasses the method lookup
algorithm, but sometimes that's exactly what you want to do. See both
the section "Private Methods" and the discussion of the "can" method in
the section "UNIVERSAL: The Ultimate Ancestor Class". To create a
reference to a particular method being called on a particular instance,
see the section "Closures" in CHP-8.
Method Invocation Using Indirect Objects
The second style of method invocation looks like this:
METHOD INVOCANT (LIST)
METHOD INVOCANT LIST
METHOD INVOCANT
The parentheses around LIST are optional; if omitted, the method acts as
a list operator. So you can have statements like the following, all of
which use this style of method call:
$mage = summon Wizard "Gandalf";
$nemesis = summon Balrog home => "Moria", weapon => "whip";
move $nemesis "bridge";
speak $mage "You cannot pass";
break $staff; # safer to use: break $staff ();
The list operator syntax should be familiar to you; it's the same style
used for passing filehandles to "print" or "printf":
print STDERR "help!!!\n";
It's also similar to English sentences like "Give Gollum the
Preciousss", so we call it the *indirect object* form. The invocant is
expected in the *indirect object slot*. When you read about passing a
built-in function like "system" or "exec" something in its "indirect
object slot", this means that you're supplying this extra, comma-less
argument in the same place you would when you invoke a method using the
indirect object syntax.
The indirect object form even permits you to specify the INVOCANT as a
BLOCK that evaluates to an object (reference) or class (package). This
lets you combine those two invocations into one statement this way:
speak { summon Wizard "Gandalf" } "friend";
Syntactic Snafus with Indirect Objects
One syntax will often be more readable than the other. The indirect
object syntax is less cluttered, but suffers from several forms of
syntactic ambiguity. The first is that the LIST part of an indirect
object invocation is parsed the same as any other list operator. Thus,
the parentheses of:
enchant $sword ($pips + 2) * $cost;
are assumed to surround all the arguments, regardless of what comes
afterward. It would therefore be equivalent to this:
($sword->enchant($pips + 2)) * $cost;
That's unlikely to do what you want: "enchant" is only being called with
"$pips + 2", and the method's return value is then multiplied by $cost.
As with other list operators, you must also be careful of the precedence
of "&&" and "||" versus "and" and "or".
For example, this:
name $sword $oldname || "Glamdring"; # can't use "or" here!
becomes the intended:
$sword->name($oldname || "Glamdring");
but this:
speak $mage "friend" && enter(); # should've been "and" here!
becomes the dubious:
$mage->speak("friend" && enter());
which could be fixed by rewriting into one of these equivalent forms:
enter() if $mage->speak("friend");
$mage->speak("friend") && enter();
speak $mage "friend" and enter();
The second syntactic infelicity of the indirect object form is that its
INVOCANT is limited to a name, an unsubscripted scalar variable, or a
block.Attentive readers will recall that this is precisely the same list
of syntactic items that are allowed after a funny character to indicate
a variable dereference--for example, @ary, @$aryref, or "@{$aryref}". As
soon as the parser sees one of these, it has its INVOCANT, so it starts
looking for its LIST. So these invocations:
move $party->{LEADER}; # probably wrong!
move $riders[$i]; # probably wrong!
actually parse as these:
$party->move->{LEADER};
$riders->move([$i]);
rather than what you probably wanted:
$party->{LEADER}->move;
$riders[$i]->move;
The parser only looks a little ways ahead to find the invocant for an
indirect object, not even as far as it would look for a unary operator.
This oddity does not arise with the first notation, so you might wish to
stick with the arrow as your weapon of choice.
Even English has a similar issue here. Think about the command, "Throw
your cat out the window a toy mouse to play with." If you parse that
sentence too quickly, you'll end up throwing the cat, not the mouse
(unless you notice that the cat is already out the window). Like Perl,
English has two different syntaxes for expressing the agent: "Throw your
cat the mouse" and "Throw the mouse to your cat." Sometimes the longer
form is clearer and more natural, and sometimes the shorter one is. At
least in Perl, you're required to use braces around any complicated
indirect object.
Package-Quoted Classes
The final syntactic ambiguity with the indirect object style of method
invocation is that it may not be parsed as a method call at all, because
the current package may have a subroutine of the same name as the
method. When using a class method with a literal package name as the
invocant, there is a way to resolve this ambiguity while still keeping
the indirect object syntax: package-quote the classname by appending a
double colon to it.
$obj = method CLASS::; # forced to be "CLASS"->method
This is important because the commonly seen notation:
$obj = new CLASS; # might not parse as method
will not always behave properly if the current package has a subroutine
named "new" or CLASS. Even if you studiously use the arrow form instead
of the indirect object form to invoke methods, this can, on rare
occasion, still be a problem. At the cost of extra punctuation noise,
the CLASS"::" notation guarantees how Perl will parse your method
invocation. The first two examples below do not always parse the same
way, but the second two do:
$obj = new ElvenRing; # could be new("ElvenRing")
# or even new(ElvenRing())
$obj = ElvenRing->new; # could be ElvenRing()->new()
$obj = new ElvenRing::; # always "ElvenRing"->new()
$obj = ElvenRing::->new; # always "ElvenRing"->new()
This package-quoting notation can be made prettier with some creative
alignment:
$obj = new ElvenRing::
name => "Narya",
owner => "Gandalf",
domain => "fire",
stone => "ruby";
Still, you may say, "Oh, ugh!" at that double colon, so we'll tell you
that you can almost always get away with a bare class name, provided two
things are true. First, there is no subroutine of the same name as the
class. (If you follow the convention that subroutine names like "new"
start lowercase and class names like "ElvenRing" start uppercase, this
is never a problem.) Second, the class has been loaded with one of:
use ElvenRing;
require ElvenRing;
Either of these declarations ensures that Perl knows "ElvenRing" is a
module name, which forces any bare name like "new" before the class name
"ElvenRing" to be interpreted as a method call, even if you happen to
have declared a "new" subroutine of your own in the current package.
People don't generally get into trouble with indirect objects unless
they start cramming multiple classes into the same file, in which case
Perl might not know that a particular package name was supposed to be a
class name. People who name subroutines with names that look like
"ModuleNames" also come to grief eventually.
(
Log in to post comments)