factor/core/classes/mixin/mixin.factor

107 lines
3.3 KiB
Factor

! Copyright (C) 2004, 2009 Slava Pestov.
! See http://factorcode.org/license.txt for BSD license.
USING: classes classes.union words kernel sequences
definitions combinators arrays assocs generic accessors ;
IN: classes.mixin
PREDICATE: mixin-class < union-class "mixin" word-prop ;
M: mixin-class reset-class
[ call-next-method ] [ { "mixin" } reset-props ] bi ;
M: mixin-class rank-class drop 3 ;
: redefine-mixin-class ( class members -- )
[ (define-union-class) ]
[ drop t "mixin" set-word-prop ]
2bi ;
: define-mixin-class ( class -- )
dup mixin-class? [
drop
] [
[ { } redefine-mixin-class ]
[ H{ } clone "instances" set-word-prop ]
[ update-classes ]
tri
] if ;
TUPLE: check-mixin-class class ;
: check-mixin-class ( mixin -- mixin )
dup mixin-class? [
\ check-mixin-class boa throw
] unless ;
: if-mixin-member? ( class mixin true false -- )
[ check-mixin-class 2dup members memq? ] 2dip if ; inline
: change-mixin-class ( class mixin quot -- )
[ [ members swap bootstrap-word ] dip call ] [ drop ] 2bi
swap redefine-mixin-class ; inline
: update-classes/new ( mixin -- )
class-usages
[ [ update-class ] each ]
[ implementors [ remake-generic ] each ] bi ;
: (add-mixin-instance) ( class mixin -- )
[ [ suffix ] change-mixin-class ]
[ [ f ] 2dip "instances" word-prop set-at ]
2bi ;
GENERIC# add-mixin-instance 1 ( class mixin -- )
M: class add-mixin-instance
#! Note: we call update-classes on the new member, not the
#! mixin. This ensures that we only have to update the
#! methods whose specializer intersects the new member, not
#! the entire mixin (since the other mixin members are not
#! affected at all). Also, all usages of the mixin will get
#! updated by transitivity; the mixins usages appear in
#! class-usages of the member, now that it's been added.
[ 2drop ] [
[ (add-mixin-instance) ] 2keep
[ nip ] [ [ new-class? ] either? ] 2bi
[ update-classes/new ] [ update-classes ] if
] if-mixin-member? ;
: (remove-mixin-instance) ( class mixin -- )
[ [ swap remove ] change-mixin-class ]
[ "instances" word-prop delete-at ]
2bi ;
: remove-mixin-instance ( class mixin -- )
#! The order of the three clauses is important here. The last
#! one must come after the other two so that the entries it
#! adds to changed-generics are not overwritten.
[
[ (remove-mixin-instance) ]
[ nip update-classes ]
[ class-usages update-methods ]
2tri
] [ 2drop ] if-mixin-member? ;
M: mixin-class class-forgotten remove-mixin-instance ;
! Definition protocol implementation ensures that removing an
! INSTANCE: declaration from a source file updates the mixin.
TUPLE: mixin-instance class mixin ;
C: <mixin-instance> mixin-instance
: >mixin-instance< ( mixin-instance -- class mixin )
[ class>> ] [ mixin>> ] bi ; inline
M: mixin-instance where >mixin-instance< "instances" word-prop at ;
M: mixin-instance set-where >mixin-instance< "instances" word-prop set-at ;
M: mixin-instance definer drop \ INSTANCE: f ;
M: mixin-instance definition drop f ;
M: mixin-instance forget*
>mixin-instance<
dup mixin-class? [ remove-mixin-instance ] [ 2drop ] if ;