Mooncrater
Mooncrater

Reputation: 4861

Call to the next most specific method does not work

Consider the class account :

(defclass account ()
  ((name :initarg :name :reader name)
   (balance :initarg :balance :initform 0.00 :accessor balance)
   (interest-rate :allocation :class :initform 0.06
                  :reader interest-rate)))

For this class, we define a method withdraw :

(defmethod withdraw ((acct account) amt)
  (if (< amt (balance acct))
      (decf (balance acct) amt)
      'insufficient-funds))

And, another class password-account , that is a subclass of account:

(defclass password-account (account)
  ((password :initarg :password :reader password )))

And, the method withdraw, for this class:

(defmethod withdraw ((acct password-account) amt pass)
  (if (equal (password acct) pass)
      (call-next-method acct amt )
      'wrong-password))

But this gives an error :

The generic function
#<STANDARD-GENERIC-FUNCTION COMMON-LISP-USER::WITHDRAW (1)>
takes 2 required arguments; was asked to find a method with
specializers
(#<STANDARD-CLASS COMMON-LISP-USER::PASSWORD-ACCOUNT>
 #1=#<SB-PCL:SYSTEM-CLASS COMMON-LISP:T> #1#)
   [Condition of type SB-PCL::FIND-METHOD-LENGTH-MISMATCH]
See also:
  Common Lisp Hyperspec, FIND-METHOD [:function]

Restarts:
 0: [RETRY] Retry SLIME REPL evaluation request.
 1: [*ABORT] Return to SLIME's top level.
 2: [ABORT] abort thread (#<THREAD "repl-thread" RUNNING {1005308033}>)

Why is this happening? And what does

was asked to find a method with specializers

mean here?

Here, the primary withdraw function had two arguments acct and amt, so in order to call it from a more specific method, which uses 3 arguments instead of 2, we can provide call-next-method with the arguments of the less specific withdraw method. But this still isn't working.
Any help appreciated.

Upvotes: 0

Views: 173

Answers (1)

Rainer Joswig
Rainer Joswig

Reputation: 139261

Congruent lambda lists for generic functions

Methods of a generic function need to have congruent lambda lists. The language standard describes what that means: Congruent Lambda-lists for all Methods of a Generic Function.

As you can see the first rule says:

  • Each lambda list must have the same number of required parameters.

Required parameters tell us which arguments always have to be provided. Generic functions additionally allow optional, keyword and rest arguments. But there is no dispatch over these. The dispatch only works over the required arguments and all of those.

Having the same number of required parameters makes dispatch easier and allows the compiler to check for function calls with the wrong number of arguments.

Optional parameters need to be congruent, too

Note also that all methods of a generic function need to have the same number of optional parameters. See the second rule in the standard.

Wording

  • a parameter is something like a named variable in a lambda list
  • an argument is provided in a call to a function

Examples:

(defun foo (a b) (list a b))

a and b are parameters for the function foo.

(foo (+ 2 3) (* 4 5))

5 and 20 are the two arguments for the call of the function foo.

Upvotes: 7

Related Questions