error about optional-arguments in common-lisp
Asked Answered
S

3

5

SBCL 64bit, 1.1.7

If I want to create a package and use a little symbols from package :CL, I will create a package like this one:

(defpackage :foo
  (:import-from :cl 
                :defun :defmacro :in-package
                :null :car :cdr :cons :if
                :eq))

But, in this package, if I define a function with optional arguments and called it without providing the optional arguments, I always get an error:

(defun test (&optional a))

(test)

invalid number of arguments: 0
   [Condition of type SB-INT:SIMPLE-PROGRAM-ERROR]

Restarts:
 0: [RETRY] Retry SLIME interactive evaluation request.
 1: [*ABORT] Return to SLIME's top level.
 2: [REMOVE-FD-HANDLER] Remove #<SB-IMPL::HANDLER INPUT on descriptor 10: #<CLOSURE (COMMON-LISP:LABELS SWANK-BACKEND::RUN :IN SWANK-BACKEND:ADD-FD-HANDLER) {100490B95B}>>
 3: [ABORT] Exit debugger, returning to top level.

Define a macro get the same error, but with more information:

(defmacro test (&rest body))

(test)

error while parsing arguments to DEFMACRO TEST:
  invalid number of elements in
    ()
  to satisfy lambda list
    (&REST BODY):
  exactly 2 expected, but 0 found
   [Condition of type SB-KERNEL::ARG-COUNT-ERROR]

I think maybe it's because of lacking some symbols from :CL, so how to resolve this problem? Thanks.

Studding answered 30/5, 2013 at 13:20 Comment(0)
I
7

I believe this will shed some light on the problem ;)

CL-USER> (defpackage :foo
  (:import-from :cl 
                :defun :defmacro :in-package
                :null :car :cdr :cons :if
                :eq))
#<PACKAGE "FOO">
CL-USER> (in-package :foo)
#<COMMON-LISP:PACKAGE "FOO">
FOO> (defun bar (&optional baz))
; in: DEFUN BAR
;     (SB-INT:NAMED-LAMBDA FOO::BAR
;         (FOO::&OPTIONAL FOO::BAZ)
;       (BLOCK FOO::BAR))
; 
; caught COMMON-LISP:STYLE-WARNING:
;   suspicious variable in lambda list: &OPTIONAL.
; 
; caught COMMON-LISP:STYLE-WARNING:
;   suspicious variable in lambda list: &OPTIONAL.
; 
; caught COMMON-LISP:STYLE-WARNING:
;   The variable &OPTIONAL is defined but never used.
; 
; caught COMMON-LISP:STYLE-WARNING:
;   The variable BAZ is defined but never used.
; 
; compilation unit finished
;   caught 4 STYLE-WARNING conditions
BAR
FOO> (in-package :cl)
#<PACKAGE "COMMON-LISP">
CL> (defpackage :foo
  (:import-from :cl 
                :defun :defmacro :in-package :&optional
                :null :car :cdr :cons :if
                :eq))
Select a symbol to be made accessible in package FOO:
  1. COMMON-LISP:&OPTIONAL
  2. FOO::&OPTIONAL

Enter an integer (between 1 and 2): 1

#<PACKAGE "FOO">
CL> (in-package :foo)
#<COMMON-LISP:PACKAGE "FOO">
FOO> (defun bar (&optional baz))
; in: DEFUN BAR
;     (SB-INT:NAMED-LAMBDA FOO::BAR
;         (&OPTIONAL FOO::BAZ)
;       (BLOCK FOO::BAR))
; 
; caught COMMON-LISP:STYLE-WARNING:
;   The variable BAZ is defined but never used.
; 
; compilation unit finished
;   caught 1 STYLE-WARNING condition
COMMON-LISP:STYLE-WARNING: redefining FOO::BAR in DEFUN
BAR
FOO> 

&optional, &rest etc. are just symbols like any others, you'd need to import those too. But, maybe this isn't the best way to import from cl package... unless you are sure that's what you need. Just in case: you could :use the whole package instead of :import-from symbol by symbol.

Ianteen answered 30/5, 2013 at 13:40 Comment(0)
M
6

Interestingly, this might point to an obscure difference with SBCL and most other implementations.

If we create a new package in SBCL, the package does not use any other package by default.

So this leads to this funky difference with, say, GNU CLISP:

&optional is a so-called lambda list keyword.

This is GNU CLISP:

[1]> (defpackage :foo
               (:import-from :cl 
                :defun :defmacro :in-package
                :null :car :cdr :cons :if
                :eq))
#<PACKAGE FOO>
[2]> (in-package "FOO")
#<PACKAGE FOO>
FOO[3]> (defun test (&optional a))
TEST
FOO[4]> (test)
NIL

It works in CLISP (and most other CL implementation), because it uses by default the "CL" package. SBCL does not use the CL package. If you don't specify a package to use, SBCL uses none. Most other implementations create a new package with a default set of useful packages to inherit. SBCL developers thought that this is especially clever, but exposes incompatibilities - which I think is not so clever at all. The standard allows the SBCL interpretation of DEFPACKAGE, but it was changed to this knowing that other implementations don't do that.

In GNU CLISP (and most other CL implementations) your import statement does not have an effect, since the package uses all of the package "CL" anyway.

SBCL:

* (defpackage "BAR")

#<PACKAGE "BAR">

* (package-use-list "BAR")

NIL

Compare that with CLISP:

[1]> (defpackage "BAR")
#<PACKAGE BAR>
[2]> (package-use-list "BAR")
(#<PACKAGE COMMON-LISP>)

So you need to import the lambda list keywords in SBCL into your package.

It also means that you need to write your package declaration like this:

(defpackage :foo
  (:import-from :cl 
   :defun :defmacro :in-package
   :null :car :cdr :cons :if
   :eq
   :&optional)
  (:use))

Above adds the &optional lambda list keyword. You might want to add other lambda list keywords as well.

It also specifies explicitly to not use any package.

Mcbee answered 30/5, 2013 at 14:38 Comment(6)
It is an intentional choice. Most implementations will auto-import CL if no packages are specified, but not do it if at least one other package is specified. By having the list of imported packages empty when no package is specified, you tend to end up with fewer errors down the line.Dixson
@Vatine: adding this incompatibility seems to be worse. I want to be able to type (defpackage "FOO") at the REPL and get a useful package. SBCL is not the only CL implementation on the planet.Mcbee
It's not an incompatibility, it's a degree of implementor discretion explicitly allowed by the standard. If you want to make a portable program, you already had to explicitly provide a use list.Gynandromorph
@Xach: there is more to Common Lisp than an ANSI standard. I'm also against using every degree of freedom in the ANSI Standard. I'm especially against changing an implementation, knowing that it creates incompatibilities with other implementations. The usual behavior was 'create a package with the usual platform defaults'. Now SBCL thought this was stupid and changed it to 'create a package with no defaults'. Allowed by the standard - but creating incompatibilities with other implementations.Mcbee
Thanks for all your answers! I am reading "The little Schemer", and importing some basic symbols from :CL making definition of the rest freely.Studding
@RainerJoswig Prior to SBCl's choise to implement this (sometime in 2003, from memory), it was quite common for example code to do (defpackage :foo (:use :some-package)) and people being rather surprised that they didn't have CL available (because before they added (:use :somepackage) FOO did indeed have CL available). I have seen much less of that since. I do agree that there's some convenience in the "let no use mean (:use :cl)" in the REPL, but in code saved in files, I'd argue it's not good.Dixson
D
4

If you try the following in your package FOO (cl:symbol-package '&optional), you can see what package is used. Common Lisp expects that cl:&optional is used in lambda lists, but you are using foo::&optional and since that is a symbol without a semantic meaning, it's just a parameter like any other.

One option would be to add :&optional :&rest to the list of symbols you import from the CL package, another would be to whole-sale import it, while shadowing some symbols you want to re-define.

Dixson answered 30/5, 2013 at 14:3 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.