common-lisppackagepractical-common-lisp

Clauses in ecase macro take on package prefix


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 exmh-users-admin@redhat.com 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.


Solution

  • 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.