0.02 release
[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
d401eda1 9our $VERSION = '0.02';
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();
58}
59
d0e2efe5 60sub uninitialize {
61 # why bother if we don't have anything ...
62 return unless keys %MRO;
63 _remove_method_dispatch_tables();
64}
65
66sub reinitialize {
67 uninitialize();
68 # clean up the %MRO before we re-initialize
69 $MRO{$_} = undef foreach keys %MRO;
70 initialize();
71}
72
d401eda1 73## functions for applying C3 to classes
74
75sub _calculate_method_dispatch_tables {
95bebf8c 76 foreach my $class (keys %MRO) {
d401eda1 77 _calculate_method_dispatch_table($class);
95bebf8c 78 }
d401eda1 79}
80
81sub _calculate_method_dispatch_table {
82 my $class = shift;
83 no strict 'refs';
84 my @MRO = calculateMRO($class);
85 $MRO{$class} = { MRO => \@MRO };
86 my %methods;
87 # NOTE:
88 # we do @MRO[1 .. $#MRO] here because it
89 # makes no sense to interogate the class
90 # which you are calculating for.
91 foreach my $local (@MRO[1 .. $#MRO]) {
92 foreach my $method (grep { defined &{"${local}::$_"} } keys %{"${local}::"}) {
93 # skip if already overriden in local class
94 next unless !defined *{"${class}::$method"}{CODE};
95 $methods{$method} = {
96 orig => "${local}::$method",
97 code => \&{"${local}::$method"}
98 } unless exists $methods{$method};
95bebf8c 99 }
d401eda1 100 }
101 # now stash them in our %MRO table
102 $MRO{$class}->{methods} = \%methods;
103}
104
105sub _apply_method_dispatch_tables {
106 foreach my $class (keys %MRO) {
107 _apply_method_dispatch_table($class);
108 }
95bebf8c 109}
110
d401eda1 111sub _apply_method_dispatch_table {
112 my $class = shift;
113 no strict 'refs';
114 foreach my $method (keys %{$MRO{$class}->{methods}}) {
115 *{"${class}::$method"} = $MRO{$class}->{methods}->{$method}->{code};
116 }
117}
118
d0e2efe5 119sub _remove_method_dispatch_tables {
120 foreach my $class (keys %MRO) {
121 _remove_method_dispatch_table($class);
122 }
123}
124
125sub _remove_method_dispatch_table {
126 my $class = shift;
127 no strict 'refs';
128 foreach my $method (keys %{$MRO{$class}->{methods}}) {
129 delete ${"${class}::"}{$method};
130 }
131}
132
d401eda1 133## functions for calculating C3 MRO
134
135# this function is a perl-port of the
136# python code on this page:
137# http://www.python.org/2.3/mro.html
95bebf8c 138sub _merge {
139 my (@seqs) = @_;
140 my @res;
141 while (1) {
142 # remove all empty seqences
143 my @nonemptyseqs = (map { (@{$_} ? $_ : ()) } @seqs);
144 # return the list if we have no more no-empty sequences
145 return @res if not @nonemptyseqs;
146 my $cand; # a canidate ..
147 foreach my $seq (@nonemptyseqs) {
148 $cand = $seq->[0]; # get the head of the list
149 my $nothead;
150 foreach my $sub_seq (@nonemptyseqs) {
151 # XXX - this is instead of the python "in"
152 my %in_tail = (map { $_ => 1 } @{$sub_seq}[ 1 .. $#{$sub_seq} ]);
153 # NOTE:
154 # jump out as soon as we find one matching
155 # there is no reason not too. However, if
156 # we find one, then just remove the '&& last'
157 $nothead++ && last if exists $in_tail{$cand};
158 }
159 last unless $nothead; # leave the loop with our canidate ...
160 $cand = undef; # otherwise, reject it ...
161 }
162 die "Inconsistent hierarchy" if not $cand;
163 push @res => $cand;
164 # now loop through our non-empties and pop
165 # off the head if it matches our canidate
166 foreach my $seq (@nonemptyseqs) {
167 shift @{$seq} if $seq->[0] eq $cand;
168 }
169 }
170}
171
172sub calculateMRO {
173 my ($class) = @_;
174 no strict 'refs';
175 return _merge(
176 [ $class ], # the class we are linearizing
177 (map { [ calculateMRO($_) ] } @{"${class}::ISA"}), # the MRO of all the superclasses
178 [ @{"${class}::ISA"} ] # a list of all the superclasses
179 );
180}
181
1821;
183
184__END__
185
186=pod
187
188=head1 NAME
189
190Class::C3 - A pragma to use the C3 method resolution order algortihm
191
192=head1 SYNOPSIS
193
194 package A;
195 use Class::C3;
196 sub hello { 'A::hello' }
197
198 package B;
199 use base 'A';
200 use Class::C3;
201
202 package C;
203 use base 'A';
204 use Class::C3;
205
206 sub hello { 'C::hello' }
207
208 package D;
209 use base ('B', 'C');
210 use Class::C3;
211
212 # Classic Diamond MI pattern
d401eda1 213 # <A>
214 # / \
215 # <B> <C>
216 # \ /
217 # <D>
95bebf8c 218
219 package main;
220
221 print join ', ' => Class::C3::calculateMRO('Diamond_D') # prints D, B, C, A
222
223 print D->hello() # prints 'C::hello' instead of the standard p5 'A::hello'
224
225 D->can('hello')->(); # can() also works correctly
226 UNIVERSAL::can('D', 'hello'); # as does UNIVERSAL::can()
227
228=head1 DESCRIPTION
229
230This is currently an experimental pragma to change Perl 5's standard method resolution order
231from depth-first left-to-right (a.k.a - pre-order) to the more sophisticated C3 method resolution
232order.
233
234=head2 What is C3?
235
236C3 is the name of an algorithm which aims to provide a sane method resolution order under multiple
237inheritence. It was first introduced in the langauge Dylan (see links in the L<SEE ALSO> section),
238and then later adopted as the prefered MRO (Method Resolution Order) for the new-style classes in
239Python 2.3. Most recently it has been adopted as the 'canonical' MRO for Perl 6 classes, and the
240default MRO for Parrot objects as well.
241
242=head2 How does C3 work.
243
244C3 works by always preserving local precendence ordering. This essentially means that no class will
245appear before any of it's subclasses. Take the classic diamond inheritence pattern for instance:
246
d401eda1 247 <A>
248 / \
249 <B> <C>
250 \ /
251 <D>
95bebf8c 252
253The standard Perl 5 MRO would be (D, B, A, C). The result being that B<A> appears before B<C>, even
254though B<C> is the subclass of B<A>. The C3 MRO algorithm however, produces the following MRO
255(D, B, C, A), which does not have this same issue.
256
257This example is fairly trival, for more complex examples and a deeper explaination, see the links in
258the L<SEE ALSO> section.
259
260=head2 How does this module work?
261
262This module uses a technique similar to Perl 5's method caching. During the INIT phase, this module
263calculates the MRO of all the classes which called C<use Class::C3>. It then gathers information from
264the symbol tables of each of those classes, and builds a set of method aliases for the correct
265dispatch ordering. Once all these C3-based method tables are created, it then adds the method aliases
266into the local classes symbol table.
267
268The end result is actually classes with pre-cached method dispatch. However, this caching does not
269do well if you start changing your C<@ISA> or messing with class symbol tables, so you should consider
270your classes to be effectively closed. See the L<CAVEATS> section for more details.
271
d401eda1 272=head1 OPTIONAL LOWERCASE PRAGMA
273
274This release also includes an optional module B<c3> in the F<opt/> folder. I did not include this in
275the regular install since lowercase module names are considered I<"bad"> by some people. However I
276think that code looks much nicer like this:
277
278 package MyClass;
279 use c3;
280
281The the more clunky:
282
283 package MyClass;
284 use Class::C3;
285
286But hey, it's your choice, thats why it is optional.
287
95bebf8c 288=head1 FUNCTIONS
289
290=over 4
291
292=item B<calculateMRO ($class)>
293
294Given a C<$class> this will return an array of class names in the proper C3 method resolution order.
295
d401eda1 296=item B<initialize>
297
298This can be used to initalize the C3 method dispatch tables. You need to call this if you are running
299under mod_perl, or in any other environment which does not run the INIT phase of the perl compiler.
300
301NOTE:
d0e2efe5 302This can B<not> be used to re-load the dispatch tables for all classes. Use C<reinitialize> for that.
303
304=item B<uninitialize>
305
306Calling this function results in the removal of all cached methods, and the restoration of the old Perl 5
307style dispatch order (depth-first, left-to-right).
308
309=item B<reinitialize>
310
311This effectively calls C<uninitialize> followed by C<initialize> the result of which is a reloading of
312B<all> the calculated C3 dispatch tables.
313
314It should be noted that if you have a large class library, this could potentially be a rather costly
315operation.
d401eda1 316
95bebf8c 317=back
318
319=head1 CAVEATS
320
321Let me first say, this is an experimental module, and so it should not be used for anything other
322then other experimentation for the time being.
323
324That said, it is the authors intention to make this into a completely usable and production stable
325module if possible. Time will tell.
326
327And now, onto the caveats.
328
329=over 4
330
331=item Use of C<SUPER::>.
332
333The idea of C<SUPER::> under multiple inheritence is ambigious, and generally not recomended anyway.
334However, it's use in conjuntion with this module is very much not recommended, and in fact very
335discouraged. In the future I plan to support a C<NEXT::> style interface to be used to move to the
336next most appropriate method in the MRO.
337
338=item Changing C<@ISA>.
339
340It is the author's opinion that changing C<@ISA> at runtime is pure insanity anyway. However, people
341do it, so I must caveat. Any changes to the C<@ISA> will not be reflected in the MRO calculated by this
d0e2efe5 342module, and therefor probably won't even show up. If you do this, you will need to call C<reinitialize>
343in order to recalulate B<all> method dispatch tables. See the C<reinitialize> documentation and an example
344in F<t/20_reinitialize.t> for more information.
95bebf8c 345
346=item Adding/deleting methods from class symbol tables.
347
348This module calculates the MRO for each requested class during the INIT phase by interogatting the symbol
349tables of said classes. So any symbol table manipulation which takes place after our INIT phase is run will
d0e2efe5 350not be reflected in the calculated MRO. Just as with changing the C<@ISA>, you will need to call
351C<reinitialize> for any changes you make to take effect.
95bebf8c 352
95bebf8c 353=back
354
15eeb546 355=head1 TODO
356
357=over 4
358
359=item More tests
360
361You can never have enough tests :)
362
15eeb546 363=item call-next-method / NEXT:: / next METHOD
364
365I am contemplating some kind of psudeo-package which can dispatch to the next most relevant method in the
366MRO. This should not be too hard to implement when the time comes.
367
15eeb546 368=back
369
95bebf8c 370=head1 SEE ALSO
371
372=head2 The original Dylan paper
373
374=over 4
375
376=item L<http://www.webcom.com/haahr/dylan/linearization-oopsla96.html>
377
378=back
379
380=head2 The prototype Perl 6 Object Model uses C3
381
382=over 4
383
384=item L<http://svn.openfoundry.org/pugs/perl5/Perl6-MetaModel/>
385
386=back
387
388=head2 Parrot now uses C3
389
390=over 4
391
392=item L<http://aspn.activestate.com/ASPN/Mail/Message/perl6-internals/2746631>
393
394=item L<http://use.perl.org/~autrijus/journal/25768>
395
396=back
397
398=head2 Python 2.3 MRO related links
399
400=over 4
401
402=item L<http://www.python.org/2.3/mro.html>
403
404=item L<http://www.python.org/2.2.2/descrintro.html#mro>
405
406=back
407
408=head2 C3 for TinyCLOS
409
410=over 4
411
412=item L<http://www.call-with-current-continuation.org/eggs/c3.html>
413
414=back
415
416=head1 AUTHOR
417
d401eda1 418Stevan Little, E<lt>stevan@iinteractive.comE<gt>
95bebf8c 419
420=head1 COPYRIGHT AND LICENSE
421
422Copyright 2005 by Infinity Interactive, Inc.
423
424L<http://www.iinteractive.com>
425
426This library is free software; you can redistribute it and/or modify
427it under the same terms as Perl itself.
428
429=cut