Improved multi-methods
parent
b61c41163b
commit
ede3f4d977
|
@ -0,0 +1 @@
|
||||||
|
Slava Pestov
|
|
@ -0,0 +1,83 @@
|
||||||
|
IN: temporary
|
||||||
|
USING: multi-methods tools.test kernel math arrays sequences
|
||||||
|
prettyprint strings classes hashtables assocs namespaces ;
|
||||||
|
|
||||||
|
[ { 1 2 3 4 5 6 } ] [
|
||||||
|
{ 6 4 5 1 3 2 } [ <=> ] topological-sort
|
||||||
|
] unit-test
|
||||||
|
|
||||||
|
[ -1 ] [
|
||||||
|
{ fixnum array } { number sequence } classes<
|
||||||
|
] unit-test
|
||||||
|
|
||||||
|
[ 0 ] [
|
||||||
|
{ number sequence } { number sequence } classes<
|
||||||
|
] unit-test
|
||||||
|
|
||||||
|
[ 1 ] [
|
||||||
|
{ object object } { number sequence } classes<
|
||||||
|
] unit-test
|
||||||
|
|
||||||
|
[
|
||||||
|
{
|
||||||
|
{ { object integer } [ 1 ] }
|
||||||
|
{ { object object } [ 2 ] }
|
||||||
|
{ { POSTPONE: f POSTPONE: f } [ 3 ] }
|
||||||
|
}
|
||||||
|
] [
|
||||||
|
{
|
||||||
|
{ { integer } [ 1 ] }
|
||||||
|
{ { } [ 2 ] }
|
||||||
|
{ { f f } [ 3 ] }
|
||||||
|
} congruify-methods
|
||||||
|
] unit-test
|
||||||
|
|
||||||
|
GENERIC: first-test
|
||||||
|
|
||||||
|
[ t ] [ \ first-test generic? ] unit-test
|
||||||
|
|
||||||
|
MIXIN: thing
|
||||||
|
|
||||||
|
TUPLE: paper ; INSTANCE: paper thing
|
||||||
|
TUPLE: scissors ; INSTANCE: scissors thing
|
||||||
|
TUPLE: rock ; INSTANCE: rock thing
|
||||||
|
|
||||||
|
GENERIC: beats?
|
||||||
|
|
||||||
|
METHOD: beats? { paper scissors } t ;
|
||||||
|
METHOD: beats? { scissors rock } t ;
|
||||||
|
METHOD: beats? { rock paper } t ;
|
||||||
|
METHOD: beats? { thing thing } f ;
|
||||||
|
|
||||||
|
: play ( obj1 obj2 -- ? ) beats? 2nip ;
|
||||||
|
|
||||||
|
[ { } 3 play ] unit-test-fails
|
||||||
|
[ t ] [ T{ paper } T{ scissors } play ] unit-test
|
||||||
|
[ f ] [ T{ scissors } T{ paper } play ] unit-test
|
||||||
|
|
||||||
|
[ t ] [ { beats? paper scissors } method-spec? ] unit-test
|
||||||
|
[ ] [ { beats? paper scissors } see ] unit-test
|
||||||
|
|
||||||
|
GENERIC: legacy-test
|
||||||
|
|
||||||
|
M: integer legacy-test sq ;
|
||||||
|
M: string legacy-test " hey" append ;
|
||||||
|
|
||||||
|
[ 25 ] [ 5 legacy-test ] unit-test
|
||||||
|
[ "hello hey" ] [ "hello" legacy-test ] unit-test
|
||||||
|
|
||||||
|
SYMBOL: some-var
|
||||||
|
|
||||||
|
HOOK: hook-test some-var
|
||||||
|
|
||||||
|
[ t ] [ \ hook-test hook-generic? ] unit-test
|
||||||
|
|
||||||
|
METHOD: hook-test { array array } reverse ;
|
||||||
|
METHOD: hook-test { array } class ;
|
||||||
|
METHOD: hook-test { hashtable number } assoc-size ;
|
||||||
|
|
||||||
|
{ 1 2 3 } some-var set
|
||||||
|
[ { f t t } ] [ { t t f } hook-test ] unit-test
|
||||||
|
[ fixnum ] [ 3 hook-test ] unit-test
|
||||||
|
5.0 some-var set
|
||||||
|
[ 0 ] [ H{ } hook-test ] unit-test
|
|
@ -1,7 +1,14 @@
|
||||||
|
! Copyright (C) 2008 Slava Pestov.
|
||||||
|
! See http://factorcode.org/license.txt for BSD license.
|
||||||
USING: kernel math sequences vectors classes combinators
|
USING: kernel math sequences vectors classes combinators
|
||||||
generic.standard arrays words combinators.lib assocs parser ;
|
arrays words assocs parser namespaces definitions
|
||||||
|
prettyprint prettyprint.backend quotations ;
|
||||||
IN: multi-methods
|
IN: multi-methods
|
||||||
|
|
||||||
|
TUPLE: method loc def ;
|
||||||
|
|
||||||
|
: <method> { set-method-def } \ method construct ;
|
||||||
|
|
||||||
: maximal-element ( seq quot -- n elt )
|
: maximal-element ( seq quot -- n elt )
|
||||||
dupd [
|
dupd [
|
||||||
swapd [ call 0 < ] 2curry subset empty?
|
swapd [ call 0 < ] 2curry subset empty?
|
||||||
|
@ -23,31 +30,156 @@ IN: multi-methods
|
||||||
} cond 2nip
|
} cond 2nip
|
||||||
] 2map [ zero? not ] find nip 0 or ;
|
] 2map [ zero? not ] find nip 0 or ;
|
||||||
|
|
||||||
|
: picker ( n -- quot )
|
||||||
|
{
|
||||||
|
{ 0 [ [ dup ] ] }
|
||||||
|
{ 1 [ [ over ] ] }
|
||||||
|
{ 2 [ [ pick ] ] }
|
||||||
|
[ 1- picker [ >r ] swap [ r> swap ] 3append ]
|
||||||
|
} case ;
|
||||||
|
|
||||||
: multi-predicate ( classes -- quot )
|
: multi-predicate ( classes -- quot )
|
||||||
dup length <reversed> [
|
dup length <reversed> [
|
||||||
>r "predicate" word-prop r>
|
>r "predicate" word-prop r>
|
||||||
(picker) swap append
|
picker swap [ not ] 3append [ f ] 2array
|
||||||
] 2map [ && ] curry ;
|
] 2map [ t ] swap alist>quot ;
|
||||||
|
|
||||||
|
: method-defs ( methods -- methods' )
|
||||||
|
[ method-def ] assoc-map ;
|
||||||
|
|
||||||
: multi-dispatch-quot ( methods -- quot )
|
: multi-dispatch-quot ( methods -- quot )
|
||||||
[ >r multi-predicate r> ] assoc-map
|
[ >r multi-predicate r> ] assoc-map
|
||||||
[ "No method" throw ] swap reverse alist>quot ;
|
[ "No method" throw ] swap reverse alist>quot ;
|
||||||
|
|
||||||
: sorted-methods ( word -- methods )
|
: methods ( word -- alist )
|
||||||
"multi-methods" word-prop >alist
|
"multi-methods" word-prop >alist ;
|
||||||
|
|
||||||
|
: congruify-methods ( alist -- alist' )
|
||||||
|
dup empty? [
|
||||||
|
dup [ first length ] map supremum [
|
||||||
|
swap >r object pad-left [ \ f or ] map r>
|
||||||
|
] curry assoc-map
|
||||||
|
] unless ;
|
||||||
|
|
||||||
|
: sorted-methods ( alist -- alist' )
|
||||||
[ [ first ] 2apply classes< ] topological-sort ;
|
[ [ first ] 2apply classes< ] topological-sort ;
|
||||||
|
|
||||||
|
GENERIC: perform-combination ( word combination -- quot )
|
||||||
|
|
||||||
|
TUPLE: standard-combination ;
|
||||||
|
|
||||||
|
: standard-combination ( methods -- quot )
|
||||||
|
congruify-methods sorted-methods multi-dispatch-quot ;
|
||||||
|
|
||||||
|
M: standard-combination perform-combination
|
||||||
|
drop methods method-defs standard-combination ;
|
||||||
|
|
||||||
|
TUPLE: hook-combination var ;
|
||||||
|
|
||||||
|
M: hook-combination perform-combination
|
||||||
|
hook-combination-var [ get ] curry
|
||||||
|
swap methods method-defs [ [ drop ] swap append ] assoc-map
|
||||||
|
standard-combination append ;
|
||||||
|
|
||||||
: make-generic ( word -- )
|
: make-generic ( word -- )
|
||||||
dup sorted-methods multi-dispatch-quot define ;
|
dup dup "multi-combination" word-prop perform-combination
|
||||||
|
define ;
|
||||||
|
|
||||||
|
: init-methods ( word -- )
|
||||||
|
dup "multi-methods" word-prop
|
||||||
|
H{ } assoc-like
|
||||||
|
"multi-methods" set-word-prop ;
|
||||||
|
|
||||||
|
: define-generic ( word combination -- )
|
||||||
|
dupd "multi-combination" set-word-prop
|
||||||
|
dup init-methods
|
||||||
|
make-generic ;
|
||||||
|
|
||||||
|
: define-standard-generic ( word -- )
|
||||||
|
T{ standard-combination } define-generic ;
|
||||||
|
|
||||||
: GENERIC:
|
: GENERIC:
|
||||||
CREATE
|
CREATE define-standard-generic ; parsing
|
||||||
dup H{ } clone "multi-methods" set-word-prop
|
|
||||||
make-generic ; parsing
|
|
||||||
|
|
||||||
: add-method ( quot classes word -- )
|
: define-hook-generic ( word var -- )
|
||||||
[ "multi-methods" word-prop set-at ] keep make-generic ;
|
hook-combination construct-boa define-generic ;
|
||||||
|
|
||||||
|
: HOOK:
|
||||||
|
CREATE scan-word define-hook-generic ; parsing
|
||||||
|
|
||||||
|
: method ( classes word -- method )
|
||||||
|
"multi-methods" word-prop at ;
|
||||||
|
|
||||||
|
: with-methods ( word quot -- )
|
||||||
|
over >r >r "multi-methods" word-prop
|
||||||
|
r> call r> make-generic ; inline
|
||||||
|
|
||||||
|
: add-method ( method classes word -- )
|
||||||
|
[ set-at ] with-methods ;
|
||||||
|
|
||||||
|
: forget-method ( classes word -- )
|
||||||
|
[ delete-at ] with-methods ;
|
||||||
|
|
||||||
|
: parse-method ( -- method classes word method-spec )
|
||||||
|
parse-definition 2 cut
|
||||||
|
over >r
|
||||||
|
>r first2 swap r> <method> -rot
|
||||||
|
r> first2 swap add* >array ;
|
||||||
|
|
||||||
: METHOD:
|
: METHOD:
|
||||||
parse-definition unclip swap unclip swap spin
|
location
|
||||||
add-method ; parsing
|
>r parse-method >r add-method r> r>
|
||||||
|
remember-definition ; parsing
|
||||||
|
|
||||||
|
! For compatibility
|
||||||
|
: M:
|
||||||
|
scan-word 1array scan-word parse-definition <method>
|
||||||
|
-rot add-method ; parsing
|
||||||
|
|
||||||
|
! Definition protocol. We qualify core generics here
|
||||||
|
USE: qualified
|
||||||
|
QUALIFIED: syntax
|
||||||
|
|
||||||
|
PREDICATE: word generic
|
||||||
|
"multi-combination" word-prop >boolean ;
|
||||||
|
|
||||||
|
PREDICATE: word standard-generic
|
||||||
|
"multi-combination" word-prop standard-combination? ;
|
||||||
|
|
||||||
|
PREDICATE: word hook-generic
|
||||||
|
"multi-combination" word-prop hook-combination? ;
|
||||||
|
|
||||||
|
syntax:M: standard-generic definer drop \ GENERIC: f ;
|
||||||
|
|
||||||
|
syntax:M: standard-generic definition drop f ;
|
||||||
|
|
||||||
|
syntax:M: hook-generic definer drop \ HOOK: f ;
|
||||||
|
|
||||||
|
syntax:M: hook-generic definition drop f ;
|
||||||
|
|
||||||
|
syntax:M: hook-generic synopsis*
|
||||||
|
dup seeing-word \ HOOK: pprint-word dup pprint-word
|
||||||
|
dup "multi-combination" word-prop
|
||||||
|
hook-combination-var pprint-word stack-effect. ;
|
||||||
|
|
||||||
|
PREDICATE: array method-spec
|
||||||
|
unclip generic? >r [ class? ] all? r> and ;
|
||||||
|
|
||||||
|
syntax:M: method-spec where
|
||||||
|
dup unclip method method-loc [ ] [ second where ] ?if ;
|
||||||
|
|
||||||
|
syntax:M: method-spec set-where
|
||||||
|
unclip method set-method-loc ;
|
||||||
|
|
||||||
|
syntax:M: method-spec definer
|
||||||
|
drop \ METHOD: \ ; ;
|
||||||
|
|
||||||
|
syntax:M: method-spec definition
|
||||||
|
unclip method method-def ;
|
||||||
|
|
||||||
|
syntax:M: method-spec synopsis*
|
||||||
|
dup definer drop pprint-word
|
||||||
|
unclip pprint* pprint* ;
|
||||||
|
|
||||||
|
syntax:M: method-spec forget
|
||||||
|
unclip [ delete-at ] with-methods ;
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
Experimental multiple dispatch implementation
|
Loading…
Reference in New Issue