N. Böck
N. Böck

Reputation: 77

Common Lisp: Passing an object to a method

I have a problem with the behaviour of objects (class instances).

A code sample:

(defclass game-cards ()
  ((card-symbol :initarg :card-symbol :accessor card-symbol)
   (colour      :initarg :colour      :accessor colour)))

(defvar *king-hearts* (make-instance 'game-cards
                                     :card-symbol 'King
                                     :colour 'hearts))
(defvar *ace-spades*  (make-instance 'game-cards
                                     :card-symbol 'Ace
                                     :colour 'spades))

(defclass game-states ()  
  ((my-cards    :initarg :my-cards    :accessor my-cards)                                
   (other-cards :initarg :other-cards :accessor other-cards)))

(defparameter *state-1*
    (make-instance 'game-states
                   :my-cards    '(*king-hearts* *ace-spades*)
                   :other-cards ()))


(defmethod play-game ((state game-states))
  (some-job (first (my-cards state))))

(defmethod some-job ((card game-cards))
  (colour card))

When some-job is used with a game-cards object in the parameter list, it works like I expected.

CL-USER>  (some-job  *king-hearts*)
HEARTS
CL-USER> 

Also this works:

CL-USER> (first (my-cards *state-1*))
*KING-HEARTS*
CL-USER> 

When I try this:

(some-job (first (my-cards *state-1*)))

I get the following error message:

There is no applicable method for the generic function
  #<STANDARD-GENERIC-FUNCTION COMMON-LISP-USER::SOME-JOB (1)>
when called with arguments
  (*KING-HEARTS*).
   [Condition of type SIMPLE-ERROR]

When I define some-job as a function:

(defun some-job-1 (card)
  (colour card)) 

the same behaviour occurs.

The error message is now:

There is no applicable method for the generic function
  #<STANDARD-GENERIC-FUNCTION COMMON-LISP-USER::COLOUR (1)>
when called with arguments
  (*KING-HEARTS*).
   [Condition of type SIMPLE-ERROR]

It seems that *king-hearts* is now not distinguished as an instance of game-cards by some-job and colour.

What is the reason? Tank you for your answers.

Upvotes: 1

Views: 444

Answers (1)

Rainer Joswig
Rainer Joswig

Reputation: 139411

Quoted data is not evaluated. That's a basic Lisp evaluation rule:

CL-USER 1 > pi
3.141592653589793D0

CL-USER 2 > 'pi
PI

CL-USER 3 > '(pi pi)
(PI PI)

CL-USER 4 > (list pi pi)
(3.141592653589793D0 3.141592653589793D0)

CL-USER 5 > (list 'pi 'pi)
(PI PI)

Here PI is a symbol and not a number:

CL-USER 6 > (type-of 'pi)
SYMBOL

CL-USER 7 > (type-of pi)
DOUBLE-FLOAT

Thus we can define a method for a number:

CL-USER 8 > (defmethod square ((n number)) (* n n))
#<STANDARD-METHOD SQUARE NIL (NUMBER) 402005F60B>

CL-USER 9 > (square pi)
9.869604401089358D0

But the call for a symbol does not work, since there is only a method for a number:

CL-USER 10 > (square 'pi)

Error: No applicable methods for #<STANDARD-GENERIC-FUNCTION SQUARE 4060010C1C> with args (PI)
  1 (continue) Call #<STANDARD-GENERIC-FUNCTION SQUARE 4060010C1C> again
  2 (abort) Return to top loop level 0.

Type :b for backtrace or :c <option number> to proceed.
Type :bug-form "<subject>" for a bug report template or :? for other options.

We can define a method for a symbol in the debugger:

CL-USER 11 : 1 > (defmethod square ((n symbol))
                   (let ((n (symbol-value n)))
                     (* n n)))
#<STANDARD-METHOD SQUARE NIL (SYMBOL) 4020285ED3>

And then we re-invoke the call:

CL-USER 12 : 1 > :c 1
9.869604401089358D0

How to solve your problem:

  • Either create a list of CLOS objects using LIST
  • or retrieve the CLOS objects from the global variable using SYMBOL-VALUE.

The latter usually makes less sense.

Upvotes: 3

Related Questions