Having programmed in Kawa for over a year now, and having programmed in Guile for some time before, I have some thoughts regarding #Java vs Common #Lisp.

I haven't used Java all that much, and Common Lisp even less, but I think that Guile's #GOOPS system is similar to #CLOS - and likewise, the OO system in Kawa literally is that of Java.

I remember the time when I was making set of GUI widgets for my humanoid robot pose editor. I was using GOOPS for that, and I feel that it was a total mess. Well, it did work, but I don't want to go back to that code.

I was using GOOPS in many other ways, for example - to overload various math operations for matrices and quaternions. The code I wrote back then can still be admired, say, here:

https://github.com/panicz/slayer/blob/master/guile-modules/extra/math.scm

I think it was today that I got enlightened on what I really didn't like about the GOOPS/CLOS approach with generic and multimethods. It may seem like extremely flexible system, but I think that multimethods combined with subclasses are a terrible idea in practice.

Suppose that we have a base class <A>

(define-class <A> ())
(define a (make <A>))

and its two subclasses:

(define-class <B> (<A>))
(define b (make <B>))

(define-class <C> (<A>))
(define c (make <C>))

Now, suppose that I also have a generic method:

(define-method (m (x <A>) (y <A>)) 'aa)

and its two specializations

(define-method (m (x <B>) (y <A>)) 'ba)

(define-method (m (x <A>) (y <C>)) 'ac)

Now, what is going to happen when I invoke

(m b c)

?

I checked it with Guile, and it chose the 'ba implementation. I don't know if that's because it was defined earlier, or because the method specialization is defined to be resolved from left to right - and frankly, I don't care.

Imagine that one programmer wrote the 'ac implementation and had some working code. Now, if another programmer comes and defines the 'ba variant, they would break the existing code. And this is the fundamental problem.

slayer/math.scm at master · panicz/slayer

Contribute to panicz/slayer development by creating an account on GitHub.

GitHub

@PaniczGodek CLOS says 'ba as well, because method specialization is indeed resolved from left to right:

https://people.cs.georgetown.edu/~maloof/cltl/clm/node284.html#SECTION003217100000000000000

Adding methods to an existing class and method hierarchy can indeed break existing code. But that can happen with single-argument methods as well. And with single inheritance. I have done it in Smalltalk.

28.1.7.1. Determining the Effective Method

@khinsen can you show an example of such a breakage in a single-dispatch system?

@PaniczGodek Here is a Lisp version of what I did in Smalltalk:

(defclass a () ())
(defclass b (a) ())
(defclass c (b) ())

(defgeneric m (x))
(defmethod m ((x a))
'a)
(defmethod m ((x c))
(cons 'c (call-next-method)))

;; Let's use this:

(m (make-instance 'a))
;; ==> A

(m (make-instance 'c))
;; ==> (C . A)

;; Then add:

(defmethod m ((x b))
(cons 'b (call-next-method)))

;; Surprise:

(m (make-instance 'c))
;; ==> (C B . A)

@PaniczGodek In Common Lisp, (call-next-method) is the equivalent of "super" in Smalltalk: it calls the method implementation for the immediate superclass.