Version: 4.1.4.1

Multimethods

    1 Ad-Hoc Hierarchies of Values

    2 Sets of Overloaded Methods

    3 Multimethods

      3.1 Procedural Protocol

      3.2 Syntactic Sugar

    4 License

 (require (planet "main.ss" ("murphy" "multimethod.plt" 2 0)))

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-method: 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:

  > (parameterize ([global-hierarchy (derive (global-hierarchy)
                                             'marble 'rock)])
      (beats? 'marble 'scissors))

  #t

  > (beats? 'marble 'scissors)

  find-method: no method for signature #(marble scissors)

Since multimethods store their method overload sets in parameters, their behaviour can also be changed temporarily:

  > (parameterize-multimethod (beats?)
      (define-method beats? :: #(rock scissors)
        #f)
      (beats? 'limestone 'scissors))

  #f

  > (beats? 'limestone 'scissors)

  #t

  > (parameterize-multimethod (beats?)
      (define-method (beats? a b) :: #(limestone paper)
        #t)
      (beats? 'limestone 'paper))

  #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-method: ambiguous methods for signature #(rock

  scissors)

This can be resolved by preferences or more specific definitions:

  > (parameterize-multimethod (beats?)
      (define-preference beats? :: (< #(rock scissors) #(sedimentary scissors)))
      (beats? 'limestone 'scissors))

  #t

  > (parameterize-multimethod (beats?)
      (define-preference beats? :: (> #(rock scissors) #(sedimentary scissors)))
      (beats? 'limestone 'scissors))

  #f

  > (parameterize-multimethod (beats?)
      (define-method (beats? a b) :: #(limestone scissors)
        'foobar)
      (beats? 'limestone 'scissors))

  foobar