summaryrefslogtreecommitdiff
path: root/pod/perlboot.pod
diff options
context:
space:
mode:
authorMichael Witten <mfwitten@gmail.com>2009-04-07 14:59:25 -0500
committerYves Orton <demerphq@gmail.com>2009-04-07 23:41:31 +0200
commitc7b05296e4d91f7340a7dd0fea0074b185981c1d (patch)
tree25ea38f99394f7a50cb5bb57e8cbea3955efaaa7 /pod/perlboot.pod
parent549d696aa7d29aae84a712be503ee044e3453020 (diff)
downloadperl-c7b05296e4d91f7340a7dd0fea0074b185981c1d.tar.gz
Docs: Better[?] development of SUPER
Signed-off-by: Michael Witten <mfwitten@gmail.com>
Diffstat (limited to 'pod/perlboot.pod')
-rw-r--r--pod/perlboot.pod90
1 files changed, 54 insertions, 36 deletions
diff --git a/pod/perlboot.pod b/pod/perlboot.pod
index 03b25827b1..cdd1e02817 100644
--- a/pod/perlboot.pod
+++ b/pod/perlboot.pod
@@ -315,7 +315,7 @@ should always act like an C<Animal>).
So, let's make C<Mouse> an C<Animal>!
-First, we can invoke the C<Animal::speak> method directly:
+The obvious solution is to invoke C<Animal::speak> directly:
# Animal package from before
{ package Mouse;
@@ -328,29 +328,41 @@ First, we can invoke the C<Animal::speak> method directly:
}
}
-Note that we have to include the C<$class> parameter (almost surely
-the value of C<"Mouse">) as the first parameter to C<Animal::speak>,
-since we've stopped using the method arrow. Why did we stop? Well,
-if we invoke C<< Animal->speak >> there, the first parameter to the
-method will be C<"Animal"> not C<"Mouse">, and when time comes for it
-to call for the C<sound>, it won't have the right class to come back
-to this package.
-
-Invoking C<Animal::speak> directly is a mess, however. What if
-C<Animal::speak> didn't exist before, and was being inherited from a
-class mentioned in C<@Animal::ISA>? Because we are no longer using
-the method arrow, we get one and only one chance to hit the right
-subroutine.
-
-Also note that the C<Animal> classname is now hardwired into the
-subroutine selection. This is a mess if someone maintains the code,
-changing C<@ISA> for C<Mouse> and didn't notice C<Animal> there in
-C<speak>. So, this is probably not the right way to go.
+Note that we're using C<Animal::speak>. If we were to invoke
+C<< Animal->speak >> instead, the first parameter to C<Animal::speak>
+would automatically be C<"Animal"> rather than C<"Mouse">, so that
+the call to C<< $class->sound >> in C<Animal::speak> would become
+C<< Animal->sound >> rather than C<< Mouse->sound >>.
+
+Also, without the method arrow C<< -> >>, it becomes necessary to specify
+the first parameter to C<Animal::speak> ourselves, which is why C<$class>
+is explicitly passed: C<Animal::speak($class)>.
+
+However, invoking C<Animal::speak> directly is a mess: Firstly, it assumes
+that the C<speak> method is a member of the C<Animal> class; what if C<Animal>
+actually inherits C<speak> from its own base? Because we are no longer using
+C<< -> >> to access C<speak>, the special method look up mechanism wouldn't be
+used, so C<speak> wouldn't even be found!
+
+The second problem is more subtle: C<Animal> is now hardwired into the subroutine
+selection. Let's assume that C<Animal::speak> does exist. What happens when,
+at a later time, someone expands the class hierarchy by having C<Mouse>
+inherit from C<Mus> instead of C<Animal>. Unless the invocation of C<Animal::speak>
+is also changed to an invocation of C<Mus::speak>, centuries worth of taxonomical
+classification could be obliterated!
+
+What we have here is a fragile or leaky abstraction; it is the beginning of a
+maintenance nightmare. What we need is the ability to search for the right
+method wih as few assumptions as possible.
=head2 Starting the search from a different place
-A better solution is to tell Perl to search from a higher place
-in the inheritance chain:
+A I<better> solution is to tell Perl where in the inheritance chain to begin searching
+for C<speak>. This can be achieved with a modified version of the method arrow C<< -> >>:
+
+ ClassName->FirstPlaceToLook::method
+
+So, the improved C<Mouse> class is:
# same Animal as before
{ package Mouse;
@@ -362,22 +374,20 @@ in the inheritance chain:
}
}
-Ahh. This works. Using this syntax, we start with C<Animal> to find
-C<speak>, and use all of C<Animal>'s inheritance chain if not found
-immediately. And yet the first parameter will be C<$class>, so the
-found C<speak> method will get C<Mouse> as its first entry, and
-eventually work its way back to C<Mouse::sound> for the details.
+Using this syntax, we start with C<Animal> to find C<speak>, and then
+use all of C<Animal>'s inheritance chain if it is not found immediately.
+As usual, the first parameter to C<speak> would be C<$class>, so we no
+longer need to pass C<$class> explicitly to C<speak>.
-But this isn't the best solution. We still have to keep the C<@ISA>
-and the initial search package coordinated. Worse, if C<Mouse> had
-multiple entries in C<@ISA>, we wouldn't necessarily know which one
-had actually defined C<speak>. So, is there an even better way?
+But what about the second problem? We're still hardwiring C<Animal> into
+the method lookup.
=head2 The SUPER way of doing things
-By changing the C<Animal> class to the C<SUPER> class in that
-invocation, we get a search of all of our super classes (classes
-listed in C<@ISA>) automatically:
+If C<Animal> is replaced with the special placeholder C<SUPER> in that
+invocation, then the contents of C<Mouse>'s C<@ISA> are used for the
+search, beginning with C<$ISA[0]>. So, all of the problems can be fixed
+as follows:
# same Animal as before
{ package Mouse;
@@ -389,9 +399,17 @@ listed in C<@ISA>) automatically:
}
}
-So, C<SUPER::speak> means look in the current package's C<@ISA> for
-C<speak>, invoking the first one found. Note that it does I<not> look in
-the C<@ISA> of C<$class>.
+In general, C<SUPER::speak> means look in the current package's C<@ISA>
+for a class that implements C<speak>, and invoke the first one found.
+The placeholder is called C<SUPER>, because many other languages refer
+to base classes as "I<super>classes", and Perl likes to be eclectic.
+
+Note that a call such as
+
+ $class->SUPER::method;
+
+does I<not> look in the C<@ISA> of C<$class> unless C<$class> happens to
+be the current package.
=head2 Where we're at so far...