0

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.

Rainer Joswig
  • 136,269
  • 10
  • 221
  • 346
Mooncrater
  • 4,146
  • 4
  • 33
  • 62
  • 6
    You can't have a method with two required parameters and another one with three required arguments in the same generic function. CLOS does not allow that. The first method definition determines the the number of required arguments. The generic function in your example has two required arguments and you try to define a method with three required arguments. This is not supported and also not allowed. – Rainer Joswig Jun 18 '17 at 09:45
  • Thanks @RainerJoswig. Any particular reason for not supporting such methods? – Mooncrater Jun 18 '17 at 09:56
  • And what's the point of calling the next most specific method via `call-next-method` with arguments ? As all the methods would require the same number of arguments. (Thus the same arguments) – Mooncrater Jun 18 '17 at 10:05
  • 1
    A generic function needs to have one valid number of required arguments.If that were not the case, a compiler would not be able to check that the correct number of required arguments have been provided. – Rainer Joswig Jun 18 '17 at 10:08
  • 4
    No, not all methods require the same number of arguments. All what is required is that the methods have the same number of REQUIRED arguments. Methods can have additional arguments: optional arguments, keyword arguments, rest arguments. – Rainer Joswig Jun 18 '17 at 10:10
  • @RainerJoswig So, making the argument `pass` optional should do the trick. – Mooncrater Jun 18 '17 at 10:15

1 Answers1

7

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.

Rainer Joswig
  • 136,269
  • 10
  • 221
  • 346