0.02 release
[gitmo/Class-C3.git] / lib / Class / C3.pm
1
2 package Class::C3;
3
4 use strict;
5 use warnings;
6
7 use Scalar::Util 'blessed';
8
9 our $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 #
24 my %MRO;
25
26 # use these for debugging ...
27 sub _dump_MRO_table { %MRO }
28 our $TURN_OFF_C3 = 0;
29
30 sub import {
31     my $class = caller();
32     # skip if the caller is main::
33     # since that is clearly not relevant
34     return if $class eq 'main';
35     return if $TURN_OFF_C3;
36     # make a note to calculate $class 
37     # during INIT phase
38     $MRO{$class} = undef;
39 }
40
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
51 INIT { initialize() }
52
53 sub 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
60 sub uninitialize {
61     # why bother if we don't have anything ...
62     return unless keys %MRO;    
63     _remove_method_dispatch_tables();    
64 }
65
66 sub reinitialize {
67     uninitialize();
68     # clean up the %MRO before we re-initialize
69     $MRO{$_} = undef foreach keys %MRO;
70     initialize();
71 }
72
73 ## functions for applying C3 to classes
74
75 sub _calculate_method_dispatch_tables {
76     foreach my $class (keys %MRO) {
77         _calculate_method_dispatch_table($class);
78     }
79 }
80
81 sub _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};
99         }
100     }    
101     # now stash them in our %MRO table
102     $MRO{$class}->{methods} = \%methods;    
103 }
104
105 sub _apply_method_dispatch_tables {
106     foreach my $class (keys %MRO) {
107         _apply_method_dispatch_table($class);
108     }     
109 }
110
111 sub _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
119 sub _remove_method_dispatch_tables {
120     foreach my $class (keys %MRO) {
121         _remove_method_dispatch_table($class);
122     }       
123 }
124
125 sub _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
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
138 sub _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
172 sub 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
182 1;
183
184 __END__
185
186 =pod
187
188 =head1 NAME
189
190 Class::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
213     #    <A>
214     #   /   \
215     # <B>   <C>
216     #   \   /
217     #    <D>
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
230 This is currently an experimental pragma to change Perl 5's standard method resolution order 
231 from depth-first left-to-right (a.k.a - pre-order) to the more sophisticated C3 method resolution
232 order. 
233
234 =head2 What is C3?
235
236 C3 is the name of an algorithm which aims to provide a sane method resolution order under multiple
237 inheritence. It was first introduced in the langauge Dylan (see links in the L<SEE ALSO> section),
238 and then later adopted as the prefered MRO (Method Resolution Order) for the new-style classes in 
239 Python 2.3. Most recently it has been adopted as the 'canonical' MRO for Perl 6 classes, and the 
240 default MRO for Parrot objects as well.
241
242 =head2 How does C3 work.
243
244 C3 works by always preserving local precendence ordering. This essentially means that no class will 
245 appear before any of it's subclasses. Take the classic diamond inheritence pattern for instance:
246
247      <A>
248     /   \
249   <B>   <C>
250     \   /
251      <D>
252
253 The standard Perl 5 MRO would be (D, B, A, C). The result being that B<A> appears before B<C>, even 
254 though 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
257 This example is fairly trival, for more complex examples and a deeper explaination, see the links in
258 the L<SEE ALSO> section.
259
260 =head2 How does this module work?
261
262 This module uses a technique similar to Perl 5's method caching. During the INIT phase, this module 
263 calculates the MRO of all the classes which called C<use Class::C3>. It then gathers information from 
264 the symbol tables of each of those classes, and builds a set of method aliases for the correct 
265 dispatch ordering. Once all these C3-based method tables are created, it then adds the method aliases
266 into the local classes symbol table. 
267
268 The end result is actually classes with pre-cached method dispatch. However, this caching does not
269 do well if you start changing your C<@ISA> or messing with class symbol tables, so you should consider
270 your classes to be effectively closed. See the L<CAVEATS> section for more details.
271
272 =head1 OPTIONAL LOWERCASE PRAGMA
273
274 This release also includes an optional module B<c3> in the F<opt/> folder. I did not include this in 
275 the regular install since lowercase module names are considered I<"bad"> by some people. However I
276 think that code looks much nicer like this:
277
278   package MyClass;
279   use c3;
280   
281 The the more clunky:
282
283   package MyClass;
284   use Class::C3;
285   
286 But hey, it's your choice, thats why it is optional.
287
288 =head1 FUNCTIONS
289
290 =over 4
291
292 =item B<calculateMRO ($class)>
293
294 Given a C<$class> this will return an array of class names in the proper C3 method resolution order.
295
296 =item B<initialize>
297
298 This can be used to initalize the C3 method dispatch tables. You need to call this if you are running
299 under mod_perl, or in any other environment which does not run the INIT phase of the perl compiler.
300
301 NOTE: 
302 This 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
306 Calling this function results in the removal of all cached methods, and the restoration of the old Perl 5
307 style dispatch order (depth-first, left-to-right). 
308
309 =item B<reinitialize>
310
311 This effectively calls C<uninitialize> followed by C<initialize> the result of which is a reloading of
312 B<all> the calculated C3 dispatch tables. 
313
314 It should be noted that if you have a large class library, this could potentially be a rather costly 
315 operation.
316
317 =back
318
319 =head1 CAVEATS
320
321 Let me first say, this is an experimental module, and so it should not be used for anything other 
322 then other experimentation for the time being. 
323
324 That said, it is the authors intention to make this into a completely usable and production stable 
325 module if possible. Time will tell.
326
327 And now, onto the caveats.
328
329 =over 4
330
331 =item Use of C<SUPER::>.
332
333 The idea of C<SUPER::> under multiple inheritence is ambigious, and generally not recomended anyway.
334 However, it's use in conjuntion with this module is very much not recommended, and in fact very 
335 discouraged. In the future I plan to support a C<NEXT::> style interface to be used to move to the 
336 next most appropriate method in the MRO.
337
338 =item Changing C<@ISA>.
339
340 It is the author's opinion that changing C<@ISA> at runtime is pure insanity anyway. However, people
341 do it, so I must caveat. Any changes to the C<@ISA> will not be reflected in the MRO calculated by this
342 module, and therefor probably won't even show up. If you do this, you will need to call C<reinitialize> 
343 in order to recalulate B<all> method dispatch tables. See the C<reinitialize> documentation and an example
344 in F<t/20_reinitialize.t> for more information.
345
346 =item Adding/deleting methods from class symbol tables.
347
348 This module calculates the MRO for each requested class during the INIT phase by interogatting the symbol
349 tables of said classes. So any symbol table manipulation which takes place after our INIT phase is run will
350 not be reflected in the calculated MRO. Just as with changing the C<@ISA>, you will need to call 
351 C<reinitialize> for any changes you make to take effect.
352
353 =back
354
355 =head1 TODO
356
357 =over 4
358
359 =item More tests
360
361 You can never have enough tests :)
362
363 =item call-next-method / NEXT:: / next METHOD
364
365 I am contemplating some kind of psudeo-package which can dispatch to the next most relevant method in the 
366 MRO. This should not be too hard to implement when the time comes.
367
368 =back
369
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
418 Stevan Little, E<lt>stevan@iinteractive.comE<gt>
419
420 =head1 COPYRIGHT AND LICENSE
421
422 Copyright 2005 by Infinity Interactive, Inc.
423
424 L<http://www.iinteractive.com>
425
426 This library is free software; you can redistribute it and/or modify
427 it under the same terms as Perl itself. 
428
429 =cut