Is there a naming convention for methods in Common Lisp?

I found it painful to do object-oriented programming in Common Lisp. The main problem is the naming of methods.Please see the code below.

(defclass foo () ())

(defmethod action ((object foo))
  (write-line "hello"))


(defclass bar () ())

(defmethod action ((object bar) boo)
  t)


(print
 (action (make-instance 'foo)))

(print
 (action (make-instance 'bar) 1))

The two action methods are not different implementations for a same generic function. They have the same name by accident. But Common Lisp requires all methods with the same name to have the same numbers of parameters.

In order to avoid the name conflicts, I usually prefix the methods' names with the class names, such as foo-action and bar-action. But that causes the code to be quite verbose in real program, e.g. (lengthy-class-name-do-something some-variable).

Other object-oriented programming languages, like C++ and Java, do not have such problems. You can write that like some_variable.do_something() with no name conflicts in them.

So I'd like to know if there exists any better solution to the problem described above?


Solution 1:

The key difference is indeed that methods are methods on generic functions. They are not methods on classes.

This may sound like words for words' sake. But it should highly influence your naming choices. "Action" is a pretty bland name for a generic function. When foo acts, what does it actually do? When bar acts, what does it actually do?

If action truly is the best name, would having a foo:action and a bar:action make sense (that is, an action in the foo package, and one in the bar package)? They are different GF's. With, of course, the drawback that it is no longer as easy as just calling action on them.

And that hints towards "you don't need a method, you can just use a function", because you don't need a method to do things with classes in CL.

Solution 2:

Those two methods are methods of the same generic function, and so they absolutely must have compatible lambda lists. Otherwise, there is no guarantee that the resolution of the effective method combination (which happens at runtime, as this is runtime polymorphism) has an unambiguous result.

You omitted the defgeneric form, which should give you a warning from the compiler.

This only works in Java as you described because you conventionally put each class into its own namespace. But here, you are working in one namespace and expect the same name (symbol) to have two entirely different meanings. This doesn't work.

In Java parlance, every generic function is its own interface.

Aside, to illustrate the advantage: you don't need to attach implementations of this interface to a class, but you can have even multiple classes to dispatch (at runtime) on. This makes e. g. the notoriously complex Visitor pattern obsolete.

Imagine for a moment such a syntax in Java at toplevel (i. e. as an alternative to class or interface:

method IAction (Foo foo, Bar bar) {
  action (Foo foo, Bar bar) {
    return something(...);
  }
}

This method would be invoked when at runtime a call to IAction#action gets objects of class Foo and Bar. Of course, this doesn't exist in Java (and seems prohibitively difficult to add).