Can't variables be used within generic function methods? (CLOS/LISP)

306 views Asked by At

I'm learning about generic functions in CLOS.

Because of the type of examples I find in textbooks and online, I'm getting very confused. The examples always use the fact that there is multiple dispatch. Based on the argument type, a different calculation is performed. However, why are the arguments themselves never used in the examples?

Example code from Wikipedia

; declare the common argument structure prototype
(defgeneric f (x y)) 

; define an implementation for (f integer t), where t matches all types
(defmethod f ((x integer) y) 1) 

(f 1 2.0) => 1

; define an implementation for (f integer real)
(defmethod f ((x integer) (y real)) 2) 

(f 1 2.0) => 2 ; dispatch changed at runtime

In the examples above, you can see the methods themselves never actually use the x or y variables. Is it a coincidence that all of these examples never use the variables? Can they be used?

Also, it's written on Wikipedia:

Methods are defined separately from classes, and they have no special access (e.g. "this", "self", or "protected") to class slots.

Alright, so methods do not have a "this" because they do not belong to a class. But why can generic-function methods have a receiver then? Isn't the receiver similar to the 'this' in a class?

2

There are 2 answers

1
Rainer Joswig On BEST ANSWER

Sure you can access the variables from the parameter list. The wikipedia example is only there to illustrate which method returns the value.

But why can generic-function methods have a receiver then? Isn't the receiver similar to the 'this' in a class?

CLOS generic functions don't have a single receiver, thus it makes no sense to use the word receiver. The implementation you mention likely does not implement full CLOS, but a variant without multiple dispatch.

CLOS Example:

CL-USER 8 > (defmethod plus ((s1 string) (s2 string))
              (concatenate 'string s1 s2))
#<STANDARD-METHOD PLUS NIL (STRING STRING) 4020001E6B>

CL-USER 9 > (plus "foo" "bar")
"foobar"

You see that the both variables s1 and s2 are used. It just makes no sense to name one of them receiver.

But you can name the variables as you like and when your application only uses dispatch over the first argument, you might want to call that variable receiver, but the name has no semantic for CLOS. It is just another name.

Generally for CLOS code it is preferred to give the arguments useful names.

This is misleading, because we are not doing message passing in CLOS:

(defmethod plus ((receiver string) (argument string))
   (concatenate 'string receiver argument))

This is more useful:

(defmethod plus ((string-1 string) (string-2 string))
   (concatenate 'string string-1 string-2))
0
Barmar On

The examples are just showing how dispatching works based on types, so they don't bother to use the variables. But you certainly could, e.g.

(defmethod f ((x integer) (y symbol))
    (* x x))

(f 3 'foo) => 9

The use of a receiver is just a convention. If you want to use CLOS similarly to other OOP languages, you might just dispatch on the type of the first argument. And you could call it this or self to make the intent clear.