From: Michael A. Koerber
Subject: ASSOC usage within PACKAGES...
Date: 
Message-ID: <3AF71D96.2FDD14F9@ll.mit.edu>
I need some education about packages and ASSOC-lists.  Basically
(ASSOC 'SYMBOL A-LIST) doesn't seem to work inside a package.
This is true for CMUCL 18 and CLISP, so I suppose thats the
way it is supposed to be...so

1.  Why?
2.  Is there a simple alternative?

Detail Example
--------------

Suppose I have a file with an assoc list in it.
E.g., ((A . 10) (B . 20) (C . 30)) is the only thing in
the file tmp.alist.

Then consider the function to get the association with B...

code: (defun get-b (filename )
code:   (let (al)
code:     (with-open-file (in filename :direction :input) (setf al (read
in)))
code:     (assoc 'b al)))

Now suppose that I put this function in a package like this...

code: (defpackage "TMPTEST"
code:   (:documentation "Just testing")
code:   (:export "ANOTHER-GET-B"))
code: 
code: (in-package "TMPTEST")
code: 
code: (defun another-get-b (filename )
code:   "Another version of get-g"
code:   (let (al)
code:     (with-open-file (in filename :direction :input) (setf al (read
in)))
code:     (assoc 'b al)))

If I type

test: * (get-b "tmp.alist")
test: (B . 20) 

but if I type

test: * (tmptest:another-get-b "tmp.alist")
test: NIL

If I play with the debugger I note that the A-list was read in as shown
below and the KEYS are marked as belonging to the COMMON-LISP-USER
package.  Note: I have run this experiment on much longer association
lists and have seen that not ALL of the keys are marked this way; some
seem to belong to the package in which they where read, i.e., TMPTEST.

debug: 6] l
debug: TMPTEST::AL  =  ((A . 10) (B . 20) (C . 30))
debug: TMPTEST::FILENAME  =  "tmp.alist"
debug: TMPTEST::IN  =  #<Stream for file "tmp.alist">
debug: 
debug: 6] (in-package tmptest)
debug: #<The TMPTEST package, 4/9 internal, 1/2 external>
debug: 6] l
debug: AL  =  ((COMMON-LISP-USER::A . 10) (COMMON-LISP-USER::B . 20)
debug:            (COMMON-LISP-USER::C . 30))
debug: FILENAME  =  "tmp.alist"
debug: IN  =  #<Stream for file "tmp.alist">


tnx

-------------------------------------------------------------------
Dr Michael A. Koerber       "Men occasionally stumble over the 
MIT/Lincoln Laboratory      truth, but most of them pick themselves
···@ll.mit.edu              up and hurry off as if nothing had
			    happened.", Winston Churchill

From: Michael A. Koerber
Subject: Re: ASSOC usage within PACKAGES...
Date: 
Message-ID: <3AF721AA.C57DABB7@ll.mit.edu>
"Michael A. Koerber" wrote:

> code: (defpackage "TMPTEST"
> code:   (:documentation "Just testing")
> code:   (:export "ANOTHER-GET-B"))
> code:
> code: (in-package "TMPTEST")
> code:
> code: (defun another-get-b (filename )
> code:   "Another version of get-g"
> code:   (let (al)
> code:     (with-open-file (in filename :direction :input) (setf al (read
> in)))
> code:     (assoc 'b al)))

I have found that changing the last line to: (assoc "B" al :test
#'string=)))
provides the desired result...but I still don't understand the logic.

mak
-- 
-------------------------------------------------------------------
Dr Michael A. Koerber       "Men occasionally stumble over the 
MIT/Lincoln Laboratory      truth, but most of them pick themselves
···@ll.mit.edu              up and hurry off as if nothing had
			    happened.", Winston Churchill
From: Kent M Pitman
Subject: Re: ASSOC usage within PACKAGES...
Date: 
Message-ID: <sfw3dagzuwe.fsf@world.std.com>
"Michael A. Koerber" <···@ll.mit.edu> writes:

> I need some education about packages and ASSOC-lists.

These are very different things.

What you need to know are these things:

ASSOC-lists are, by default, keyed on EQL (approximate meaning:
"pointer identity").  Symbols make good keys because they are interned
and can be compared with pointer identity correctly and efficiently.
 (eql 'a 'a) => true
Strings make bad keys because they are not interned and so
 (eql "a" "a") => false
However, assoc can be used to compare using other tests. e.g., since
 (string-equal "a" "a")
and
 (string= "a" "a")
you cannot use 
 (assoc "a" '(("a" . 1) ("b" . 2)))
to find the first entry in the given alist, but you can use
 (assoc "a" '(("a" . 1) ("b" . 2)) :test #'string-equal)
And since string= and string-equal both take symbols as arguments
(symbols being something called "string designators"; see "designator"
in the CLHS glossary), you can also do
 (assoc 'a '(("a" . 1) ("b" . 2)) :test #'string-equal)
or
 (assoc "a" '((a . 1) ("b" . 2)) :test #'string-equal)
or
 (assoc "A" '((a . 1) ("b" . 2)) :test #'string-equal)
and, more relevant to your problem,
 (assoc 'foo::a '((bar::a . 1) (bar::b . 2)) :test #'string-equal)
 => (bar::a . 1)

HOWEVER, you really don't want to be looking for foo::a in a list of
symbols interned in the BAR question, so the question of real
relevance is *why* the alist is in the BAR package.  

In your case, you wrote your second code, TMPTEST:ANOTHER-GET-B
in the TMPTEST package, but it does *not* read its data in
that package. You call READ in that function, but READ uses not
the package that its surrounding code was read in, but rather
the dynamic value of *PACKAGE* at the time of invocation.  So when
you called TMPTEST:ANOTHER-GET-B, you're interning your data in
some random package.  Worse still, if you write it back out, it will
go out with package prefixes and be stuck forever in this randomly
chosen package.  

Moral: You should *always* surround READ or PRINT with some sort of
attempt to establish a known package. e.g., 


 (defvar *this-package* *package*) ;remember the package that was present
                                   ;at *definition* time, not call time.

 (defun yet-another-get-b (filename)
   (let ((*package* *this-package*)) ;<-- I ADDED THIS TO YOUR CODE
     (let (al)
       (with-open-file (in filename :direction :input)
         (setf al (read in)))
       (assoc 'b al))))

Be sure to also use this same when you write the data out.
It must be consistent between reading and writing.  You don't 
have to use any particular package, it just has to always be the same
when you print and read os you get consistency.  But since you are looking
for 'b, you'll be wanting it to be the current package, or else you have
to say 'cl-user::b or whatever.  

> Basically
> (ASSOC 'SYMBOL A-LIST) doesn't seem to work inside a package.

This is meaningless.  You are never just "in" a package.  Individual
expressions get read into a package, but after that they are just
structure.  When you call the code, there is another package, possibly
the same, possibly different, that is current.  That package affects
dynamic calls to READ.  You are confused about the difference between
the environment in which the call is done to READ by the interpreter
or compiler in order to absorb your function definition, and the call
that is done to READ because you directed another such call yourself
from within your program.  Each of those uses the value of *package*
but it can be different at different times.  And it is in your example
because you are not controlling this.

Note that LOAD binds *package*, so that when IN-PACKAGE sets the value
of *package, the effect lasts only until the end of the call to LOAD.
Ditto for COMPILE-FILE.  Loading a file will not change the value of 
*PACKAGE* you are using interactively.  Your examples would work very
differently if you'd done interactive calls to IN-PACKAGE after loading
the new package.

> This is true for CMUCL 18 and CLISP, so I suppose thats the
> way it is supposed to be...so

Well, the conceptual problems you have are manifested equivalently in the
various properly-working implementations that you have.

> 1.  Why?

I hope I have explained this somewhat.

> 2.  Is there a simple alternative?

Depends on what you wanted to achieve.
From: Tim Moore
Subject: Re: ASSOC usage within PACKAGES...
Date: 
Message-ID: <9d75vh$m1m$0@216.39.145.192>
On Mon, 7 May 2001, Michael A. Koerber wrote:

> I need some education about packages and ASSOC-lists.  Basically
> (ASSOC 'SYMBOL A-LIST) doesn't seem to work inside a package.

Of course they work "inside a package."  The problem with your
another-get-b function is that, when you run it, the current package is
not the package TMPTEST, it's COMMON-LISP-USER.  Therefore your alist is
read in in the COMMON-LISP-USER package.  Assoc is passed TMPTEST::B, so
the test fails.

Try this instead:
(defun another-get-b (filename )
  "Another version of get-b"
  (let ((al nil)
	(*package* (find-package "TMPTEST")))
    (with-open-file (in filename :direction :input)
      (setf al (read in)))
    (assoc 'b al)))

Tim

 > This is true for CMUCL 18 and CLISP, so I suppose thats the
> way it is supposed to be...so
> 
> 1.  Why?
> 2.  Is there a simple alternative?
> 
> Detail Example
> --------------
> 
> Suppose I have a file with an assoc list in it.
> E.g., ((A . 10) (B . 20) (C . 30)) is the only thing in
> the file tmp.alist.
> 
> Then consider the function to get the association with B...
> 
> code: (defun get-b (filename )
> code:   (let (al)
> code:     (with-open-file (in filename :direction :input) (setf al (read
> in)))
> code:     (assoc 'b al)))
> 
> Now suppose that I put this function in a package like this...
> 
> code: (defpackage "TMPTEST"
> code:   (:documentation "Just testing")
> code:   (:export "ANOTHER-GET-B"))
> code: 
> code: (in-package "TMPTEST")
> code: 
> code: (defun another-get-b (filename )
> code:   "Another version of get-g"
> code:   (let (al)
> code:     (with-open-file (in filename :direction :input) (setf al (read
> in)))
> code:     (assoc 'b al)))
> 
> If I type
> 
> test: * (get-b "tmp.alist")
> test: (B . 20) 
> 
> but if I type
> 
> test: * (tmptest:another-get-b "tmp.alist")
> test: NIL
> 
> If I play with the debugger I note that the A-list was read in as shown
> below and the KEYS are marked as belonging to the COMMON-LISP-USER
> package.  Note: I have run this experiment on much longer association
> lists and have seen that not ALL of the keys are marked this way; some
> seem to belong to the package in which they where read, i.e., TMPTEST.
> 
> debug: 6] l
> debug: TMPTEST::AL  =  ((A . 10) (B . 20) (C . 30))
> debug: TMPTEST::FILENAME  =  "tmp.alist"
> debug: TMPTEST::IN  =  #<Stream for file "tmp.alist">
> debug: 
> debug: 6] (in-package tmptest)
> debug: #<The TMPTEST package, 4/9 internal, 1/2 external>
> debug: 6] l
> debug: AL  =  ((COMMON-LISP-USER::A . 10) (COMMON-LISP-USER::B . 20)
> debug:            (COMMON-LISP-USER::C . 30))
> debug: FILENAME  =  "tmp.alist"
> debug: IN  =  #<Stream for file "tmp.alist">
> 
> 
> tnx
> 
> -------------------------------------------------------------------
> Dr Michael A. Koerber       "Men occasionally stumble over the 
> MIT/Lincoln Laboratory      truth, but most of them pick themselves
> ···@ll.mit.edu              up and hurry off as if nothing had
> 			    happened.", Winston Churchill
> 
> 
From: Michael A. Koerber
Subject: Re: ASSOC usage within PACKAGES...
Date: 
Message-ID: <3AF7DEF9.BBF76E27@ll.mit.edu>
Tnx to all those responding.  I appreciate the clarity and completeness
of your responses.  

mike