Home > database >  Understanding Generic Functions in Common Lisp?
Understanding Generic Functions in Common Lisp?

Time:11-17

In this answer, the user gave a very clear example on how classes and methods work together.

I will reprint the example here:


(defclass human () ())
(defclass dog () ())

(defmethod greet ((thing human))
  (print "Hi human!"))

(defmethod greet ((thing dog))
  (print "Wolf-wolf dog!"))

(defparameter Anna (make-instance 'human))
(defparameter Rex (make-instance 'dog))

(greet Anna) ;; => "Hi human"
(greet Rex)  ;; => "Wolf-wolf dog!"

My question is, using the same example:

  1. What value would creating a generic functions add?
  2. Why are generic functions useful? Are they like instances in other OO languages that provide structure?

It seems that generic functions are created in the background implicitly (not 100% sure). I notice that when I play with this example, if I create a method that has a different param structure than the first instance of the method, I get a generic function error.

CodePudding user response:

DEFMETHOD creates a generic function, if none exists.

Define generic functions explicitly let's one some options.

There are rules that the parameter lists of methods have to use. For example the parameter lists of methods of a specific generic functions all need to have the same number of required parameters.

Generic Functions and their are the basic construct for OO behavior in CLOS. Instead of virtual methods or messages, CLOS uses functions, which better fits into Lisp.

CodePudding user response:

What value would creating a generic functions add?

I like to declare the generic function explicitly because you can add documentation and specify other details if necessary, like method combination (a.k.a. when you have multiple methods applicable for a given call, this defines which are executed and in which order). Here for example I can define talk as having a progn method combination (all methods are executed as-if encapsulated in a progn form:

(defgeneric talk (subject)
  (:documentation "Say something to standard output")
  (:method-combination progn))

This is a bit contrived example, but here we go:

(defclass human () ())
(defmethod talk progn ((a human))
  (print "hello"))

(defclass wolf () ())
(defmethod talk progn ((a wolf))
  (print "owooooo!"))

(defclass werewolf (human wolf) ())

Defining a class that inherits from both means that a call to talk for an instance of the class can execute two methods (sorted in a somewhat topological order called the Method Resolution Order). So with this method combination all the methods are executed:

* (talk (make-instance 'werewolf))
"hello" 
"owooooo!"

But, I would say that being able to document the generic function is by itself a good enough reason to declare it with defgeneric.

Why are generic functions useful?

If you define talk as a generic function you allow any class to participate in code that calls talk (e.g. a library), this is a way to allow extensions without having to close the set of possible values, unlike using something like typecase in a function where you can only list a predefined set of cases.

For example the standard functions print-object is called at various times in your Lisp implementation (in the inspector, the REPL, the debugger), if you want you can implement a method for your custom types without having to hack the internals of your environment.

Are they like instances in other OO languages that provide structure?

Generic functions are, unlike in other OO languages, not tied to a single class or instance. They can be specialized on more than one argument, which means none of them "owns" the generic function.

  • Related