Multimethods
(require (planet "main.ss" ("murphy" "multimethod.plt" 2 1))) |
Provides procedures with versatile dispatch on any number of their arguments.
Inheritance hierarchies for method signatures can be defined in an ad-hoc fashion or class and interface hierarchies from scheme/class can be used.
This main module re-exports all bindings from "hierarchy.ss", "overloads.ss" and "multimethod.ss".
The usage of the module is best demonstrated with some examples before one reads the API reference. We start with the definition of a global hierarchy:
(derive 'rock 'item) |
(derive 'paper 'item) |
(derive 'scissors 'item) |
> (derived? 'rock 'item) |
#t |
> (derived? 'rock 'rock) |
#t |
> (derived? 'rock 'paper) |
#f |
> (descendants 'item) |
#hash((rock . #t) (paper . #t) (scissors . #t)) |
Given this hierarchy of objects we can easily define a multimethod reflecting the rules of a well known game:
(define-multimethod (beats? a b) :: (vector-immutable a b)) |
(define-method (beats? a b) :: #(item item) |
#f) |
(define-method (beats? a b) :: #(rock scissors) |
#t) |
(define-method (beats? a b) :: #(paper rock) |
#t) |
(define-method (beats? a b) :: #(scissors paper) |
#t) |
> (beats? 'rock 'paper) |
#f |
> (beats? 'rock 'scissors) |
#t |
Unknown objects are not recognized, of course:
> (ancestors 'limestone) |
#hash() |
> (beats? 'limestone 'scissors) |
find-methods: no method for signature #(limestone scissors) |
But the hierarchy is dynamic and we can fix this:
(derive 'limestone 'rock)
> (ancestors 'limestone) |
#hash((item . #t) (rock . #t)) |
> (beats? 'limestone 'scissors) |
#t |
The global hierarchy can be parameterized:
| |||
#t | |||
> (beats? 'marble 'scissors) | |||
find-methods: no method for signature #(marble scissors) |
Since multimethods store their method overload sets in parameters, their behaviour can also be changed temporarily:
| ||||
#f | ||||
> (beats? 'limestone 'scissors) | ||||
#t | ||||
| ||||
#t | ||||
> (beats? 'limestone 'paper) | ||||
#f |
Occasionally it may happen that the type hierarchy and method definitions result in an ambiguous situation:
(derive 'limestone 'sedimentary) |
(define-method (beats? a b) :: #(sedimentary scissors) |
#f) |
> (beats? 'limestone 'scissors) |
find-methods: ambiguous methods for signature #(limestone |
scissors), possible matches (#(rock scissors) #(sedimentary |
scissors)) |
This can be resolved by preferences or more specific definitions:
| ||||
#t | ||||
| ||||
#f | ||||
| ||||
foobar |
Methods for more specific signatures may also call the next less specific method, iff that method is uniquely determined:
(define-method (beats? a b) :: #(limestone scissors) |
(and (call-next-method) 'foobar)) |
> (beats? 'limestone 'scissors) | |||
multimethod: no unique next method for signature | |||
#(limestone scissors) | |||
| |||
foobar | |||
| |||
#f |