How to require keyword arguments in Common Lisp?
Asked Answered
C

2

6

Given

(defun show-arg (a)
  (format t "a is ~a~%" a))

(defun show-key (&key a)
  (format t "a is ~a~%" a))

evaluating

(show-arg)

will lead to an error saying "invalid number of arguments: 0", where

(show-key)

will display a is NIL

How can I get SHOW-KEY to signal an error like SHOW-ARG does? Is there a way other than using (unless a (error "a is required")) in the function body? I am very fond of keyword arguments and use them constantly, and almost always want them to be required.

Clearcole answered 25/5, 2016 at 4:55 Comment(0)
S
10

Keyword arguments are always optional, so you do need to manually check if they're given and signal an error if needed. It would be better to not require keyword arguments though. The compiler won't recognize them as required and thus won't given you an error message for missing arguments at compile time.

If you do want to require them, you can specify the arguments with a three element list; the first element being the argument, the second is the default value and the third is a variable that will be true if the argument was given. Checking the third element is better than checking the keyword itself, because then you can tell the difference between a NIL that was the default, and a NIL that the user gave as an argument.

(defun foo (&key (keyarg nil keyargp))
  (unless keyargp (error "KEYARG is required."))
  (* keyarg 2))

Edit

Now that I think about this a bit more, there actually is a way to get compile time errors for missing keyword arguments. Define a compiler macro for the function:

(defun foo (&key a b c d)
  (* a b c d))

(define-compiler-macro foo (&whole whole &key (a nil ap) (b nil bp)
                                   (c nil cp) (d nil dp))
  (declare (ignore a b c d))
  (unless (and ap bp cp dp)
    (error "Missing arguments..."))
  whole)
Spire answered 25/5, 2016 at 5:27 Comment(2)
"It would be better to not require keyword arguments though." What would be the suggested way to e.g. initialize a record with many required fields? E.g. how can I increase the readability of something like (make-album "Led Zeppelin" "Physical Graffiti" 1979 nil t 'rock) ?Clearcole
@leo-the-manic Usually you would try to provide default values that make sense, and the user will only override them when necessary. In that example the first two could be the only required arguments, and the rest can be left as NIL. In most real programs this isn't really a problem for readability, because it's rare to initialize records with literal values. You would normally be reading the values from a file/database/user and the variable names will make it clear to a reader what they mean. They can also use their development environment like SLIME to see the argument list.Spire
S
9

One possibility would be:

(defun foo (&key (arg1 (error "missing arg1 in call to function foo")))
   arg1)

Using it:

CL-USER 80 > (foo)

Error: missing arg1 in call to function foo
  1 (abort) Return to level 0.
  2 Return to top loop level 0.

This will give an error at runtime, unfortunately not at compile time.

Synsepalous answered 25/5, 2016 at 10:10 Comment(1)
A little variant on this is wrapping the signaling of the error into a little function so that you can say &key (foo (required)).Skillet

© 2022 - 2024 — McMap. All rights reserved.