Clauses in ecase macro take on package prefix

386 views Asked by At

I am trying to work through Practical Common Lisp. I'm a lisp beginner.

I've defined a package using "quicklisp" I load the package with (ql:quickload :spam filter)

One of the functions in this package looks like this:

    (defun increment-count (feature type)
     (ecase type
      (ham (incf (ham-count feature)))
      (spam (incf (spam-count feature)))))

When I try to call a function that calls this function in slime the case statement fails. I think it fails because the compiler has added the package name as a prefix to the clauses.

Here is the error I'm seeing:

HAM fell through ECASE expression. Wanted one of (HAM SPAM). [Condition of type SB-KERNEL:CASE-FAILURE]

Restarts: 0: [RETRY] Retry SLIME REPL evaluation request. 1: [*ABORT] Return to SLIME's top level. 2: [ABORT] Abort thread (#)

Backtrace: 0: (SB-KERNEL:CASE-FAILURE ECASE HAM (SPAMFILTER::HAM SPAMFILTER::SPAM)) 1: (SPAMFILTER:INCREMENT-COUNT # HAM) 2: (SPAMFILTER:TRAIN "From [email protected] Mon Sep 23 12:06:27 2002 ..) 3: (SPAMFILTER::TRAIN-FROM-CORPUS #>((#P"/Users/jh/src/lisp/spamfilter/mail/easy_ham/1205.f9d66868c52039f7a147d9e2b4b05e1f" HAM) (#P"/Users/jh/src/lisp/spamfilter/mail/easy_ham/0090.314ec4268af7a3a1974d5e.. 4: (SPAMFILTER:TEST-CLASSIFIER #((#P"/Users/jh/src/lisp/spamfilter/mail/easy_ham/0001.ea7e79d3153e7469e7a9c3e0af6a357e" HAM) (#P"/Users/jh/src/lisp/spamfilter/mail/easy_ham/0002.b3120c4bcbf3101e661161ee7.. 5: (SB-INT:SIMPLE-EVAL-IN-LEXENV (SPAMFILTER:TEST-CLASSIFIER SPAMFILTER:CORPUS 0.1) #) 6: (EVAL (SPAMFILTER:TEST-CLASSIFIER SPAMFILTER:CORPUS 0.1)) --more--

I'm basing my conjecture that the compiler has added the prefix on Backtrace 0:

0: (SB-KERNEL:CASE-FAILURE ECASE HAM (SPAMFILTER::HAM SPAMFILTER::SPAM))

I'm sure I'm missing something obvious.

Thanks.

2

There are 2 answers

0
Joshua Taylor On BEST ANSWER

This REPL transcript may be instructive. This doesn't have anything to do with the compiler adding anything, but that symbols in different packages aren't necessarily the same.

First, define a package and some a function using symbols in the spam-filter package:

CL-USER> (defpackage #:spam-filter
           (:use "COMMON-LISP"))
#<PACKAGE "SPAM-FILTER">
CL-USER> (in-package #:spam-filter)
#<PACKAGE "SPAM-FILTER">
SPAM-FILTER> (defun test (x)
               (ecase x
                 (ham "ham")
                 (spam "spam")))
TEST

Let's check that it works:

SPAM-FILTER> (test 'spam)
"spam"

OK, now let's go back to CL-USER:

SPAM-FILTER> (in-package "CL-USER")
#<PACKAGE "COMMON-LISP-USER">

Now let's try to call spam-filter::test:

CL-USER> (spam-filter::test 'spam)
; Evaluation aborted on #<SB-KERNEL:CASE-FAILURE expected-type:
                         (MEMBER SPAM-FILTER::HAM SPAM-FILTER::SPAM)
                         datum: SPAM>.

We get an error because the current package is CL-USER, so the reader reads the characters spam and interns "SPAM" to get the symbol cl-user::spam, which is not the same as spam-filter::spam. Let's try calling it with spam-filter::spam:

CL-USER> (spam-filter::test 'spam-filter::spam)
"spam"

And surely enough, it still works. For more about what's going on here, you might enjoy Ron Garret's The Complete Idiot’s Guide to Common Lisp Packages.

1
Rainer Joswig On

Ways to solve trying to compare symbols from different packages:

  • Usually it makes sense to make sure the symbols are in the right package. Normalize them into one package.

  • use keyword symbols

  • only compare the symbol names, but then you need to use something else than ECASE.

  • use strings, but then you need to use something else than ECASE.