version with the better diagnostics
[gitmo/Class-C3.git] / lib / Class / C3.pm
CommitLineData
95bebf8c 1
2package Class::C3;
3
4use strict;
5use warnings;
6
95bebf8c 7use Scalar::Util 'blessed';
8
4e47d2a4 9our $VERSION = '0.06';
d401eda1 10
11# this is our global stash of both
12# MRO's and method dispatch tables
13# the structure basically looks like
14# this:
15#
16# $MRO{$class} = {
17# MRO => [ <class precendence list> ],
18# methods => {
19# orig => <original location of method>,
20# code => \&<ref to original method>
21# }
22# }
23#
95bebf8c 24my %MRO;
25
d0e2efe5 26# use these for debugging ...
d401eda1 27sub _dump_MRO_table { %MRO }
d401eda1 28our $TURN_OFF_C3 = 0;
29
95bebf8c 30sub import {
31 my $class = caller();
d401eda1 32 # skip if the caller is main::
33 # since that is clearly not relevant
95bebf8c 34 return if $class eq 'main';
d401eda1 35 return if $TURN_OFF_C3;
36 # make a note to calculate $class
37 # during INIT phase
95bebf8c 38 $MRO{$class} = undef;
39}
40
d401eda1 41## initializers
42
43# NOTE:
44# this will not run under the following
45# conditions:
46# - mod_perl
47# - require Class::C3;
48# - eval "use Class::C3"
49# in all those cases, you need to call
50# the initialize() function manually
51INIT { initialize() }
52
53sub initialize {
54 # why bother if we don't have anything ...
55 return unless keys %MRO;
56 _calculate_method_dispatch_tables();
57 _apply_method_dispatch_tables();
5d5c86d9 58 %next::METHOD_CACHE = ();
d401eda1 59}
60
d0e2efe5 61sub uninitialize {
62 # why bother if we don't have anything ...
63 return unless keys %MRO;
64 _remove_method_dispatch_tables();
5d5c86d9 65 %next::METHOD_CACHE = ();
d0e2efe5 66}
67
68sub reinitialize {
69 uninitialize();
70 # clean up the %MRO before we re-initialize
71 $MRO{$_} = undef foreach keys %MRO;
72 initialize();
73}
74
d401eda1 75## functions for applying C3 to classes
76
77sub _calculate_method_dispatch_tables {
95bebf8c 78 foreach my $class (keys %MRO) {
d401eda1 79 _calculate_method_dispatch_table($class);
95bebf8c 80 }
d401eda1 81}
82
83sub _calculate_method_dispatch_table {
84 my $class = shift;
85 no strict 'refs';
86 my @MRO = calculateMRO($class);
87 $MRO{$class} = { MRO => \@MRO };
88 my %methods;
89 # NOTE:
90 # we do @MRO[1 .. $#MRO] here because it
91 # makes no sense to interogate the class
92 # which you are calculating for.
93 foreach my $local (@MRO[1 .. $#MRO]) {
94 foreach my $method (grep { defined &{"${local}::$_"} } keys %{"${local}::"}) {
95 # skip if already overriden in local class
96 next unless !defined *{"${class}::$method"}{CODE};
97 $methods{$method} = {
98 orig => "${local}::$method",
99 code => \&{"${local}::$method"}
100 } unless exists $methods{$method};
95bebf8c 101 }
d401eda1 102 }
103 # now stash them in our %MRO table
104 $MRO{$class}->{methods} = \%methods;
105}
106
107sub _apply_method_dispatch_tables {
108 foreach my $class (keys %MRO) {
109 _apply_method_dispatch_table($class);
110 }
95bebf8c 111}
112
d401eda1 113sub _apply_method_dispatch_table {
114 my $class = shift;
115 no strict 'refs';
116 foreach my $method (keys %{$MRO{$class}->{methods}}) {
117 *{"${class}::$method"} = $MRO{$class}->{methods}->{$method}->{code};
118 }
119}
120
d0e2efe5 121sub _remove_method_dispatch_tables {
122 foreach my $class (keys %MRO) {
123 _remove_method_dispatch_table($class);
124 }
125}
126
127sub _remove_method_dispatch_table {
128 my $class = shift;
129 no strict 'refs';
130 foreach my $method (keys %{$MRO{$class}->{methods}}) {
131 delete ${"${class}::"}{$method};
132 }
133}
134
d401eda1 135## functions for calculating C3 MRO
136
137# this function is a perl-port of the
138# python code on this page:
139# http://www.python.org/2.3/mro.html
95bebf8c 140sub _merge {
141 my (@seqs) = @_;
4e47d2a4 142 my $class_being_merged = $seqs[0]->[0];
95bebf8c 143 my @res;
144 while (1) {
145 # remove all empty seqences
146 my @nonemptyseqs = (map { (@{$_} ? $_ : ()) } @seqs);
147 # return the list if we have no more no-empty sequences
148 return @res if not @nonemptyseqs;
4e47d2a4 149 my $reject;
95bebf8c 150 my $cand; # a canidate ..
151 foreach my $seq (@nonemptyseqs) {
152 $cand = $seq->[0]; # get the head of the list
153 my $nothead;
154 foreach my $sub_seq (@nonemptyseqs) {
155 # XXX - this is instead of the python "in"
156 my %in_tail = (map { $_ => 1 } @{$sub_seq}[ 1 .. $#{$sub_seq} ]);
157 # NOTE:
158 # jump out as soon as we find one matching
159 # there is no reason not too. However, if
160 # we find one, then just remove the '&& last'
161 $nothead++ && last if exists $in_tail{$cand};
162 }
163 last unless $nothead; # leave the loop with our canidate ...
4e47d2a4 164 $reject = $cand;
95bebf8c 165 $cand = undef; # otherwise, reject it ...
166 }
4e47d2a4 167 die "Inconsistent hierarchy found while merging '$class_being_merged':\n\t" .
168 "current merge results [\n\t\t" . (join ",\n\t\t" => @res) . "\n\t]\n\t" .
169 "mergeing failed on '$reject'\n" if not $cand;
95bebf8c 170 push @res => $cand;
171 # now loop through our non-empties and pop
172 # off the head if it matches our canidate
173 foreach my $seq (@nonemptyseqs) {
174 shift @{$seq} if $seq->[0] eq $cand;
175 }
176 }
177}
178
179sub calculateMRO {
180 my ($class) = @_;
181 no strict 'refs';
182 return _merge(
183 [ $class ], # the class we are linearizing
184 (map { [ calculateMRO($_) ] } @{"${class}::ISA"}), # the MRO of all the superclasses
185 [ @{"${class}::ISA"} ] # a list of all the superclasses
186 );
187}
188
5d5c86d9 189package # hide me from PAUSE
190 next;
191
192use strict;
193use warnings;
194
195use Scalar::Util 'blessed';
196
197our $VERSION = '0.03';
198
199our %METHOD_CACHE;
200
201sub method {
202 my @label = (split '::', (caller(1))[3]);
203 my $label = pop @label;
204 my $caller = join '::' => @label;
205 my $self = $_[0];
206 my $class = blessed($self) || $self;
207
208 goto &{ $METHOD_CACHE{"$class|$caller|$label"} ||= do {
209
210 my @MRO = Class::C3::calculateMRO($class);
211
212 my $current;
213 while ($current = shift @MRO) {
214 last if $caller eq $current;
215 }
216
217 no strict 'refs';
218 my $found;
219 foreach my $class (@MRO) {
220 last if (defined ($found = *{$class . '::' . $label}{CODE}));
221 }
222
223 die "No next::method '$label' found for $self" unless $found;
224
225 $found;
226 } };
227}
228
95bebf8c 2291;
230
231__END__
232
233=pod
234
235=head1 NAME
236
237Class::C3 - A pragma to use the C3 method resolution order algortihm
238
239=head1 SYNOPSIS
240
241 package A;
242 use Class::C3;
243 sub hello { 'A::hello' }
244
245 package B;
246 use base 'A';
247 use Class::C3;
248
249 package C;
250 use base 'A';
251 use Class::C3;
252
253 sub hello { 'C::hello' }
254
255 package D;
256 use base ('B', 'C');
257 use Class::C3;
258
259 # Classic Diamond MI pattern
d401eda1 260 # <A>
261 # / \
262 # <B> <C>
263 # \ /
264 # <D>
95bebf8c 265
266 package main;
267
268 print join ', ' => Class::C3::calculateMRO('Diamond_D') # prints D, B, C, A
269
270 print D->hello() # prints 'C::hello' instead of the standard p5 'A::hello'
271
272 D->can('hello')->(); # can() also works correctly
273 UNIVERSAL::can('D', 'hello'); # as does UNIVERSAL::can()
274
275=head1 DESCRIPTION
276
277This is currently an experimental pragma to change Perl 5's standard method resolution order
278from depth-first left-to-right (a.k.a - pre-order) to the more sophisticated C3 method resolution
279order.
280
281=head2 What is C3?
282
283C3 is the name of an algorithm which aims to provide a sane method resolution order under multiple
284inheritence. It was first introduced in the langauge Dylan (see links in the L<SEE ALSO> section),
285and then later adopted as the prefered MRO (Method Resolution Order) for the new-style classes in
286Python 2.3. Most recently it has been adopted as the 'canonical' MRO for Perl 6 classes, and the
287default MRO for Parrot objects as well.
288
289=head2 How does C3 work.
290
291C3 works by always preserving local precendence ordering. This essentially means that no class will
292appear before any of it's subclasses. Take the classic diamond inheritence pattern for instance:
293
d401eda1 294 <A>
295 / \
296 <B> <C>
297 \ /
298 <D>
95bebf8c 299
300The standard Perl 5 MRO would be (D, B, A, C). The result being that B<A> appears before B<C>, even
301though B<C> is the subclass of B<A>. The C3 MRO algorithm however, produces the following MRO
302(D, B, C, A), which does not have this same issue.
303
304This example is fairly trival, for more complex examples and a deeper explaination, see the links in
305the L<SEE ALSO> section.
306
307=head2 How does this module work?
308
309This module uses a technique similar to Perl 5's method caching. During the INIT phase, this module
310calculates the MRO of all the classes which called C<use Class::C3>. It then gathers information from
311the symbol tables of each of those classes, and builds a set of method aliases for the correct
312dispatch ordering. Once all these C3-based method tables are created, it then adds the method aliases
313into the local classes symbol table.
314
315The end result is actually classes with pre-cached method dispatch. However, this caching does not
316do well if you start changing your C<@ISA> or messing with class symbol tables, so you should consider
317your classes to be effectively closed. See the L<CAVEATS> section for more details.
318
d401eda1 319=head1 OPTIONAL LOWERCASE PRAGMA
320
321This release also includes an optional module B<c3> in the F<opt/> folder. I did not include this in
322the regular install since lowercase module names are considered I<"bad"> by some people. However I
323think that code looks much nicer like this:
324
325 package MyClass;
326 use c3;
327
328The the more clunky:
329
330 package MyClass;
331 use Class::C3;
332
333But hey, it's your choice, thats why it is optional.
334
95bebf8c 335=head1 FUNCTIONS
336
337=over 4
338
339=item B<calculateMRO ($class)>
340
341Given a C<$class> this will return an array of class names in the proper C3 method resolution order.
342
d401eda1 343=item B<initialize>
344
345This can be used to initalize the C3 method dispatch tables. You need to call this if you are running
346under mod_perl, or in any other environment which does not run the INIT phase of the perl compiler.
347
348NOTE:
d0e2efe5 349This can B<not> be used to re-load the dispatch tables for all classes. Use C<reinitialize> for that.
350
351=item B<uninitialize>
352
353Calling this function results in the removal of all cached methods, and the restoration of the old Perl 5
354style dispatch order (depth-first, left-to-right).
355
356=item B<reinitialize>
357
358This effectively calls C<uninitialize> followed by C<initialize> the result of which is a reloading of
359B<all> the calculated C3 dispatch tables.
360
361It should be noted that if you have a large class library, this could potentially be a rather costly
362operation.
d401eda1 363
95bebf8c 364=back
365
5d5c86d9 366=head1 METHOD REDISPATCHING
367
368It is always useful to be able to re-dispatch your method call to the "next most applicable method". This
369module provides a pseudo package along the lines of C<SUPER::> or C<NEXT::> which will re-dispatch the
370method along the C3 linearization. This is best show with an examples.
371
372 # a classic diamond MI pattern ...
373 <A>
374 / \
375 <B> <C>
376 \ /
377 <D>
378
379 package A;
380 use c3;
381 sub foo { 'A::foo' }
382
383 package B;
384 use base 'A';
385 use c3;
386 sub foo { 'B::foo => ' . (shift)->next::method() }
387
388 package B;
389 use base 'A';
390 use c3;
391 sub foo { 'C::foo => ' . (shift)->next::method() }
392
393 package D;
394 use base ('B', 'C');
395 use c3;
396 sub foo { 'D::foo => ' . (shift)->next::method() }
397
398 print D->foo; # prints out "D::foo => B::foo => C::foo => A::foo"
399
400A few things to note. First, we do not require you to add on the method name to the C<next::method>
401call (this is unlike C<NEXT::> and C<SUPER::> which do require that). This helps to enforce the rule
402that you cannot dispatch to a method of a different name (this is how C<NEXT::> behaves as well).
403
404The next thing to keep in mind is that you will need to pass all arguments to C<next::method> it can
405not automatically use the current C<@_>.
406
95bebf8c 407=head1 CAVEATS
408
409Let me first say, this is an experimental module, and so it should not be used for anything other
410then other experimentation for the time being.
411
412That said, it is the authors intention to make this into a completely usable and production stable
413module if possible. Time will tell.
414
415And now, onto the caveats.
416
417=over 4
418
419=item Use of C<SUPER::>.
420
421The idea of C<SUPER::> under multiple inheritence is ambigious, and generally not recomended anyway.
422However, it's use in conjuntion with this module is very much not recommended, and in fact very
5d5c86d9 423discouraged. The recommended approach is to instead use the supplied C<next::method> feature, see
424more details on it's usage above.
95bebf8c 425
426=item Changing C<@ISA>.
427
428It is the author's opinion that changing C<@ISA> at runtime is pure insanity anyway. However, people
429do it, so I must caveat. Any changes to the C<@ISA> will not be reflected in the MRO calculated by this
d0e2efe5 430module, and therefor probably won't even show up. If you do this, you will need to call C<reinitialize>
431in order to recalulate B<all> method dispatch tables. See the C<reinitialize> documentation and an example
432in F<t/20_reinitialize.t> for more information.
95bebf8c 433
434=item Adding/deleting methods from class symbol tables.
435
436This module calculates the MRO for each requested class during the INIT phase by interogatting the symbol
437tables of said classes. So any symbol table manipulation which takes place after our INIT phase is run will
d0e2efe5 438not be reflected in the calculated MRO. Just as with changing the C<@ISA>, you will need to call
439C<reinitialize> for any changes you make to take effect.
95bebf8c 440
95bebf8c 441=back
442
15eeb546 443=head1 TODO
444
445=over 4
446
447=item More tests
448
449You can never have enough tests :)
450
5d5c86d9 451=back
15eeb546 452
5d5c86d9 453=head1 CODE COVERAGE
15eeb546 454
5d5c86d9 455I use B<Devel::Cover> to test the code coverage of my tests, below is the B<Devel::Cover> report on this module's test suite.
456
457 ---------------------------- ------ ------ ------ ------ ------ ------ ------
458 File stmt bran cond sub pod time total
459 ---------------------------- ------ ------ ------ ------ ------ ------ ------
460 Class/C3.pm 99.2 93.3 66.7 96.0 100.0 92.8 96.3
461 ---------------------------- ------ ------ ------ ------ ------ ------ ------
462 Total 99.2 93.3 66.7 96.0 100.0 92.8 96.3
463 ---------------------------- ------ ------ ------ ------ ------ ------ ------
15eeb546 464
95bebf8c 465=head1 SEE ALSO
466
467=head2 The original Dylan paper
468
469=over 4
470
471=item L<http://www.webcom.com/haahr/dylan/linearization-oopsla96.html>
472
473=back
474
475=head2 The prototype Perl 6 Object Model uses C3
476
477=over 4
478
479=item L<http://svn.openfoundry.org/pugs/perl5/Perl6-MetaModel/>
480
481=back
482
483=head2 Parrot now uses C3
484
485=over 4
486
487=item L<http://aspn.activestate.com/ASPN/Mail/Message/perl6-internals/2746631>
488
489=item L<http://use.perl.org/~autrijus/journal/25768>
490
491=back
492
493=head2 Python 2.3 MRO related links
494
495=over 4
496
497=item L<http://www.python.org/2.3/mro.html>
498
499=item L<http://www.python.org/2.2.2/descrintro.html#mro>
500
501=back
502
503=head2 C3 for TinyCLOS
504
505=over 4
506
507=item L<http://www.call-with-current-continuation.org/eggs/c3.html>
508
509=back
510
511=head1 AUTHOR
512
d401eda1 513Stevan Little, E<lt>stevan@iinteractive.comE<gt>
95bebf8c 514
515=head1 COPYRIGHT AND LICENSE
516
517Copyright 2005 by Infinity Interactive, Inc.
518
519L<http://www.iinteractive.com>
520
521This library is free software; you can redistribute it and/or modify
522it under the same terms as Perl itself.
523
524=cut