0

I am trying to understand why when I declare types, things seem to go very wrong in SBCL.

Here's a little file I created to reproduce the problem:

(defpackage p1 (:use :cl) (:export #:do-it #:keytype))
(in-package :p1)

(deftype keytype ()
  `(member :white :black nil))

(declaim (ftype (function (&key (foo keytype))) do-it))
(defun do-it (&key foo)
  (format T "key foo: ~A~%" foo))

(defpackage p2 (:use :cl :p1))
(in-package :p2)

(do-it :foo :black)

(loop for i from 0 to 2
      do (loop for j from 0 to 3
               for flag = (evenp i) then (not flag)
               do (do-it :foo (if flag :white :black))))

When I load this for the first time in SLIME, I get this:

; SLIME 
; file: /Users/renatoathaydes/programming/projects/format-ansi/tests/checking.lisp
; in: DEFUN DO-IT
;     (DEFUN P1:DO-IT (&KEY P1::FOO) (FORMAT T "key foo: ~A~%" P1::FOO))
; 
; caught STYLE-WARNING:
;   Defining a :FOO keyword not present in previous declaration.
; 
; caught STYLE-WARNING:
;   The definition lacks the FOO key present in previous declaration.
; 
; compilation unit finished
;   caught 2 STYLE-WARNING conditions
key foo: BLACK

; file: /Users/renatoathaydes/programming/projects/format-ansi/tests/checking.lisp
; in: LOOP FOR
;     (P1:DO-IT :FOO
;               (IF P2::FLAG
;                   :WHITE
;                   :BLACK))
; 
; caught WARNING:
;   :FOO is not a known argument keyword.
; 
; compilation unit finished
;   caught 1 WARNING condition
key foo: WHITE
key foo: BLACK
key foo: WHITE
key foo: BLACK
key foo: BLACK
key foo: WHITE
key foo: BLACK
key foo: WHITE
key foo: WHITE
key foo: BLACK
key foo: WHITE
key foo: BLACK

CL-USER> 

So, the code works fine. But I get lots of warnings that I just don't understand. Which previous definition?? I made sure to delete any FASL files I may have laying around, but this seems to always happen anyway.

If I run this directly from the command line with --script, it's fewer warnings:

➜  tests sbcl --script checking.lisp 
key foo: BLACK

; file: /Users/renatoathaydes/programming/projects/format-ansi/tests/checking.lisp
; in: LOOP FOR
;     (P1:DO-IT :FOO
;               (IF P2::FLAG
;                   :WHITE
;                   :BLACK))
; 
; caught WARNING:
;   :FOO is not a known argument keyword.
; 
; compilation unit finished
;   caught 1 WARNING condition
key foo: WHITE
key foo: BLACK
key foo: WHITE
key foo: BLACK
key foo: BLACK
key foo: WHITE
key foo: BLACK
key foo: WHITE
key foo: WHITE
key foo: BLACK
key foo: WHITE
key foo: BLACK

In my real application, I get no warning when calling a similarly defined function like this:

(format-ansi T "Basic examples:" :bg-color :black)

But on this code just below that:

(loop for i from 0 to 10
      do (loop for j from 0 to 10
               for flag = (evenp i) then (not flag)
               do (format-ansi T "  " :bg-color (if flag :white :black)))
         (newline))

It says:

; in: LOOP FOR
;     (FORMAT-ANSI T "  " :BG-COLOR
;                  (IF FLAG
;                      :WHITE
;                      :BLACK))
; 
; caught WARNING:
;   :BG-COLOR is not a known argument keyword.
; 
; compilation unit finished

What's going on here?

2
  • What about (declaim (ftype (function (&key (:foo keytype))) do-it))? Commented Nov 9 at 18:50
  • 1
    Oh man, you saved my life :D. Thank you, just submit this as an answer as it fixed the problem. Commented Nov 9 at 19:08

1 Answer 1

0

Let's look at the documentation for the System Class FUNCTION. We find this:

The &key parameters should be supplied as lists of the form (keyword type). The keyword must be a valid keyword-name symbol as must be supplied in the actual arguments of a call. This is usually a symbol in the KEYWORD package but can be any symbol.

So it should be a keyword symbol, since in your code you need to call it like this: (do-it :foo :black). Here :foo is a keyword symbol. It's possible to specify a function definition such that the symbol some-package::foo would be expected, but in your case it must be a keyword symbol.

Using a non-keyword-symbol leads to an error in a call:

CL-USER 13 > (foo :foo 42)
42

CL-USER 14 > (foo 'foo 42)

Error: Unexpected keyword FOO which is not one of (:FOO).
  1 (continue) Ignore the unknown keyword FOO.
  2 (abort) Return to top loop level 0.

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

CL-USER 15 : 1 > 

As I mentioned, it's possible to define a function such that the key argument is not a keyword symbol -> thus not a symbol in the KEYWORD package. Here we can use a specific symbol from a defined package, which is not the KEYWORD package. Note the specific mapping from the symbol foo for the function call, to the internal symbol foo for the variable:

CL-USER 16 > (defun foo (&key ((foo foo) 'bar)) foo)
FOO

CL-USER 17 > (foo 'foo 42)
42

CL-USER 18 > (foo :foo 42)

Error: Unexpected keyword :FOO which is not one of (FOO).
  1 (continue) Ignore the unknown keyword :FOO.
  2 (abort) Return to top loop level 0.

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

CL-USER 19 : 1 > 

Using (defun foo (&key (foo 'bar)) foo) is just a shorter version of (defun foo (&key ((:foo foo) 'bar)) foo).

Sign up to request clarification or add additional context in comments.

2 Comments

The actual problem I had was in the function type. The answer was just your one-line comment.
this explains why the error was in the FUNCTION type and that the local variable name is not the interface symbol name, where the latter must be used in the FUNCTION type declaration. It also explains that it is possible to declare non-keyword-symbol keyword args

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.