How do multimethods solve the namespace issue?

Dynamic dispatch and namespace resolution are two different things. In many object systems classes are also used for namespaces. Also note that often both the class and the namespace are tied to a file. So these object systems conflate at least three things:

  • class definitions with their slots and methods
  • the namespace for identifiers
  • the storage unit of source code

Common Lisp and its object system (CLOS) works differently:

  • classes don’t form a namespace
  • generic functions and methods don’t belong to classes and thus are not defined inside classes
  • generic functions are defined as top-level functions and thus are not nested or local
  • identifiers for generic functions are symbols
  • symbols have their own namespace mechanism called packages
  • generic functions are ‘open’. One can add or delete methods at any time
  • generic functions are first-class objects
  • mathods are first-class objects
  • classes and generic functions are also not conflated with files. You can define multiple classes and multiple generic functions in one file or in as many files as you want. You can also define classes and methods from running code (thus not tied to files) or something like a REPL (read eval print loop).

Style in CLOS:

  • if a functionality needs dynamic dispatch and the functionality is closely related, then use one generic function with different methods
  • if there are many different functionalities, but with a common name, don’t put them in the same generic function. Create different generic functions.
  • generic functions with the same name, but where the name is in different packages are different generic functions.

Example:

(defpackage "ANIMAL" (:use "CL")) 
(in-package "ANIMAL")

(defclass animal () ())
(deflcass dog (animal) ())
(deflcass cat (animal) ()))

(defmethod bark ((an-animal dog)) (print 'woof))
(defmethod bark ((an-animal cat)) (print 'meow)) 

(bark (make-instance 'dog))
(bark (make-instance 'dog))

Note that the class ANIMAL and the package ANIMAL have the same name. But that is not necessary so. The names are not connected in any way.
The DEFMETHOD implicitly creates a corresponding generic function.

If you add another package (for example GAME-ANIMALS), then the BARK generic function will be different. Unless these packages are related (for example one package uses the other).

From a different package (symbol namespace in Common Lisp), one can call these:

(animal:bark some-animal)

(game-animal:bark some-game-animal)

A symbol has the syntax

PACKAGE-NAME::SYMBOL-NAME

If the package is the same as the current package, then it can be omitted.

  • ANIMAL::BARK refers to the symbol named BARK in the package ANIMAL. Note that there are two colons.
  • AINMAL:BARK refers to the exported symbol BARK in the package ANIMAL. Note that there is only one colon. Exporting, importing and using are mechanisms defined for packages and their symbols. Thus they are independent of classes and generic functions, but it can be used to structure the namespace for the symbols naming those.

The more interesting case is when multimethods are actually used in generic functions:

(defmethod bite ((some-animal cat) (some-human human))
  ...)

(defmethod bite ((some-animal dog) (some-food bone))
  ...)

Above uses the classes CAT, HUMAN, DOG and BONE. Which class should the generic function belong to? What would the special namespace look like?

Since generic functions dispatch over all arguments, it does not make direct sense to conflate the generic function with a special namespace and make it a definition in a single class.

Motivation:

Generic functions were added in the 80s to Lisp by developers at Xerox PARC (for Common LOOPS) and at Symbolics for New Flavors. One wanted to get rid of an additional calling mechanism (message passing) and bring dispatch to ordinary (top-level) functions. New Flavors had single dispatch, but generic functions with multiple arguments. The research into Common LOOPS then brought multiple dispatch. New Flavors and Common LOOPS were then replaced by the standardized CLOS. These ideas then were brought to other languages like Dylan.

Since the example code in the question does not use anything generic functions have to offer, it looks like one has to give up something.

When single dispatch, message passing and single inheritance is sufficient, then generic functions may look like a step back. The reason for this is, as mentioned, that one does not want to put all kinds of similar named functionality into one generic function.

When

(defmethod bark ((some-animal dog)) ...)
(defmethod bark ((some-tree oak)) ...)

look similar, they are two conceptually different actions.

But more:

(defmethod bark ((some-animal dog) tone loudness duration)
   ...)

(defmethod bark ((some-tree oak)) ...)

Now suddenly the parameter lists for the same named generic function looks different. Should that be allowed to be one generic function? If not, how do we call BARK on various objects in a list of things with the right parameters?

In real Lisp code generic functions usually look much more complicated with several required and optional arguments.

In Common Lisp generic functions also not only have a single method type. There are different types of methods and various ways to combine them. It makes only sense to combine them, when they really belong to a certain generic function.

Since generic functions are also first class objects, they can be passed around, returned from functions and stored in data structures. At this point the generic function object itself is important, not its name anymore.

For the simple case where I have an object, which has x and y coordinates and can act as a point, I would inherit for the objects’s class from a POINT class (maybe as some mixin). Then I would import the GET-X and GET-Y symbols into some namespace – where necessary.

There are other languages which are more different from Lisp/CLOS and which attempt(ed) to support multimethods:

  • MultiJava
  • Runabout
  • C#
  • Fortress

There seems to be many attempts to add it to Java.

Leave a Comment

Hata!: SQLSTATE[HY000] [1045] Access denied for user 'divattrend_liink'@'localhost' (using password: YES)