I'm writing some methods to emit HTML for various elements. Each method has the same output, but doesn't necessarily need the same input.

The method for echoing a game-board needs to take a player as well (because each player only sees their own pieces)

(defmethod echo ((board game-board) (p player)) ... )

Echoing a board space doesn't require changing per player (that dispatch is actually done in the game-board method, which later calls echo on a space). Ideally, I'd be able to do

(defmethod echo ((space board-space)) ... )
(defmethod echo ((space empty-space)) ... )

It's also conceivable that I later run into an object that will need to know more than just the player in order to display itself properly. Since there are already methods specializing on the same generic, though, that would give the error

The generic function #<STANDARD-GENERIC-FUNCTION ECHO (4)> takes 2 required arguments;

It seems less than ideal to go back and name these methods echo-space, echo-board and so on.

Is there a canonical way of varying other arguments based on the specialized object? Should I do something like

(defgeneric echo (thing &key player ...) ...)

or

(defgeneric echo (thing &rest other-args) ...)

? More generally, can anyone point me to a decent tutorial on defgeneric specifically? (I've read the relevant PCL chapters and some CLOS tutorials, but they don't cover the situation I'm asking about here).

有帮助吗?

解决方案

Generally speaking if interfaces of two functions are too different it indicates that they are not actually a specializations of the same operation and should not have the same name. If you only want to specialize on optional/key arguments the way to achieve that is to use a normal function which calls a generic function and provides it with default values for missing arguments to specialize on.

Keene's book is, I believe, the most comprehensive guide to CLOS. Unfortunately it seems to be available only in book form.

其他提示

It's easier whenever the methods take the same parameters but only differ on the data type. But in cases where the generic function and methods all use the same name (of course) yet have lambda lists that vary significantly, I personally tend to use &key parameters to make my intentions explicit, rather than using &optional parameters. I think it helps with readability later.

Try something like this (pretending we already have classes class-a and class-b):

(defgeneric foo (object &key &allow-other-keys)
  (:documentation "The generic function. Here is where the docstring is defined."))

(defmethod foo ((object class-a) &key &allow-other-keys)
  (print "Code goes here. We dispatched based on type Class A."))

(defmethod foo ((object class-b) &key (x 1) (y 2) &allow-other-keys)
  (print "Code goes here. We dispatched based on type Class B. We have args X and Y."))

Since "method combination" is involved, in order for the dispatching logic to "flow" through its possible choices of methods to use, we need to think of the method definitions somewhat like a chain where incompatible lambda lists (i.e. parameter lists) will break that chain. That's why there's the &key and &allow-other-keys in the method definitions that don't specifically need them. Putting them in DEFGENERIC and the method definitions allows us to have the method definition where we dispatch based on class-b.

DISCLAIMER: I'm a Common Lisp newbie myself, so take this with a grain of salt!

What about restructuring the objects/classes so that each object you want to echo has all required properties in its own (and inherited) slots?

That way you don't have to pass them as arguments when you call echo on the object, since what you need is already stored in the object.

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top