From: danb
Subject: Error reading delimited list
Date: 
Message-ID: <a35a44bd-0774-41e7-bf77-80f3ceaf7716@p25g2000hsf.googlegroups.com>
I have a read macro that calls a function that calls read-delimited-
list.
It keeps signaling an error saying the list has no cadr, in fact it
says
the whole list is empy:

(eval-when (:compile-toplevel :load-toplevel :execute)
  (defun |read-[2-fix]| (stream char)
    (declare (ignore char))
    (let* ((list (read-delimited-list #\] stream t))
           (op (cadr list)))
        (unless op (error "No operator in 2-fix: ~S" list))
        (list* op (car list) (cddr list))))
  (set-macro-character #\[ #'|read-[2-fix]|)
  (set-macro-character #\] (get-macro-character #\) nil)))

;;Error message trying to use it:
;;No operator in 2-fix: NIL
;;   [Condition of type SIMPLE-ERROR]

;;Example calls from the same file:
;;  [n * *node-size*]
;;  [[parent has-child-w/info]
;;     or (progn
;;           (delete-children parent)
;;           (delete-node parent))]

What did I do wrong?
Here's a function that does *not* generate the error message:

(defun incf-nodes (n) (incf *total-units* [n * *node-size*]))

And here are two that *do*:

(defun make-room-1 (node n)
  (let ((max-total [*max-units* - [n * *node-size*]]))
    (while (> *total-units* max-total)
      (when (not (delete-wrst-node))
	return nil))
    t))

(defun make-room-2 (node n)
  (let ((max-total (- *max-units* (* n *node-size*))))
    (while [*total-units* > max-total]
      (when (not (delete-wrst-node))
	return nil))
    t))

--Dan

------------------------------------------------
Dan Bensen
http://www.prairienet.org/~dsb/

From: Pascal Bourguignon
Subject: Re: Error reading delimited list
Date: 
Message-ID: <87prtzwwtq.fsf@thalassa.informatimago.com>
danb <·········@gmail.com> writes:

> I have a read macro that calls a function that calls read-delimited-
> list.
> It keeps signaling an error saying the list has no cadr, in fact it
> says
> the whole list is empy:
>
> (eval-when (:compile-toplevel :load-toplevel :execute)
>   (defun |read-[2-fix]| (stream char)
>     (declare (ignore char))
>     (let* ((list (read-delimited-list #\] stream t))
>            (op (cadr list)))
>         (unless op (error "No operator in 2-fix: ~S" list))
>         (list* op (car list) (cddr list))))
>   (set-macro-character #\[ #'|read-[2-fix]|)
>   (set-macro-character #\] (get-macro-character #\) nil)))
                                                     ^    |
                                                     |    |
                                                     +----+

C/USER[285]> 
(eval-when (:compile-toplevel :load-toplevel :execute)
  (defun |read-[2-fix]| (stream char)
    (declare (ignore char))
    (let* ((list (read-delimited-list #\] stream t))
           (op (cadr list)))
        (unless op (error "No operator in 2-fix: ~S" list))
        (list* op (car list) (cddr list))))
  (set-macro-character #\[ #'|read-[2-fix]|)
  (set-macro-character #\] (get-macro-character #\)) nil))
T
C/USER[286]> 
(defun make-room-1 (node n)
  (let ((max-total [*max-units* - [n * *node-size*]]))
    (while (> *total-units* max-total)
      (when (not (delete-wrst-node))
	return nil))
    t))
MAKE-ROOM-1
C/USER[287]> (function-lambda-expression *)
(LAMBDA (NODE N) (DECLARE (SYSTEM::IN-DEFUN MAKE-ROOM-1))
 (BLOCK MAKE-ROOM-1 (LET ((MAX-TOTAL (- *MAX-UNITS* (* N *NODE-SIZE*)))) 
     (WHILE (> *TOTAL-UNITS* MAX-TOTAL) (WHEN (NOT (DELETE-WRST-NODE)) RETURN NIL)) T))) ;
T ;
MAKE-ROOM-1
C/USER[288]> 

-- 
__Pascal_Bourguignon__               _  Software patents are endangering
()  ASCII ribbon against html email (o_ the computer industry all around
/\  1962:DO20I=1.100                //\ the world http://lpf.ai.mit.edu/
    2001:my($f)=`fortune`;          V_/   http://petition.eurolinux.org/
From: danb
Subject: Re: Error reading delimited list
Date: 
Message-ID: <0a469a1e-738e-4332-b75a-92322570b507@k13g2000hse.googlegroups.com>
On Mar 12, 2:45 pm, Pascal Bourguignon <····@informatimago.com> wrote:
> C/USER[287]> (function-lambda-expression *)
> (LAMBDA (NODE N) (DECLARE (SYSTEM::IN-DEFUN MAKE-ROOM-1))
>  (BLOCK MAKE-ROOM-1 (LET ((MAX-TOTAL (- *MAX-UNITS* (* N *NODE-SIZE*))))
>      (WHILE (> *TOTAL-UNITS* MAX-TOTAL) (WHEN (NOT (DELETE-WRST-NODE)) RETURN NIL)) T))) ;
> T ;
> MAKE-ROOM-1

Hi Pascal, thanks for trying that.

There must be something strange going on.  The problem is actually
intermittent,
or at least dependent on something that I don't think it should depend
on.
After compiling successfully with one chunk of code that uses the read
macro,
I added another chunk and got the error, ... BUT ... the new code
didn't even
use the macro!  I had earlier converted the macro syntax in that new
piece of
code to regular syntax to see if the error went away.

So now I can turn the error on by uncommenting a piece of code that
doesn't
even use that syntax, and turn the error off by commenting it. (by
moving a #|
before or after it)  Is there a way to figure out what's going on?
I'm using
slime/sbcl/linux.  Also, the definition and uses of the read macro are
in
different packages, and I didn't export the function used by the
macro.

--Dan

------------------------------------------------
Dan Bensen
http://www.prairienet.org/~dsb/
From: Pascal Bourguignon
Subject: Re: Error reading delimited list
Date: 
Message-ID: <87abl3wrok.fsf@thalassa.informatimago.com>
danb <·········@gmail.com> writes:

> On Mar 12, 2:45 pm, Pascal Bourguignon <····@informatimago.com> wrote:
>> C/USER[287]> (function-lambda-expression *)
>> (LAMBDA (NODE N) (DECLARE (SYSTEM::IN-DEFUN MAKE-ROOM-1))
>>  (BLOCK MAKE-ROOM-1 (LET ((MAX-TOTAL (- *MAX-UNITS* (* N *NODE-SIZE*))))
>>      (WHILE (> *TOTAL-UNITS* MAX-TOTAL) (WHEN (NOT (DELETE-WRST-NODE)) RETURN NIL)) T))) ;
>> T ;
>> MAKE-ROOM-1
>
> Hi Pascal, thanks for trying that.
>
> There must be something strange going on.  

Indeed.  When I first read the make-room-1 definition, it was too
long, so I thought it was waiting for some closing parenthesis and I
stopped it with Control-C, but it may just be because of swapping; and
then I passed nil explicitely to set-macro-character, and it went
well, but indeed, it should make no difference.

So I can only say that I've got no problem with your code on clisp
with inferior-lisp.

> The problem is actually
> intermittent, or at least dependent on something that I don't think
> it should depend on.  After compiling successfully with one chunk of
> code that uses the read macro, I added another chunk and got the
> error, ... BUT ... the new code didn't even use the macro!  I had
> earlier converted the macro syntax in that new piece of code to
> regular syntax to see if the error went away.

In general, I try to avoid modifying the current readtable, but bind
temporarily *readtable* just to read what needs it:
   (let ((*readtable* *strange-readtable*)) (read strange-stream))

You can reset it with: (setf *readtable* (copy-readtable nil))

> So now I can turn the error on by uncommenting a piece of code that
> doesn't even use that syntax, and turn the error off by commenting
> it. (by moving a #| before or after it)  Is there a way to figure
> out what's going on?  I'm using slime/sbcl/linux.  
>
> Also, the
> definition and uses of the read macro are in different packages, and
> I didn't export the function used by the macro.

This doesn't matter. What matters, is the binding of the *readtable*
variable, and the reader macro functions bound to characters in that
read table, at read time.

-- 
__Pascal Bourguignon__                     http://www.informatimago.com/

NEW GRAND UNIFIED THEORY DISCLAIMER: The manufacturer may
technically be entitled to claim that this product is
ten-dimensional. However, the consumer is reminded that this
confers no legal rights above and beyond those applicable to
three-dimensional objects, since the seven new dimensions are
"rolled up" into such a small "area" that they cannot be
detected.
From: danb
Subject: Re: Error reading delimited list
Date: 
Message-ID: <1697aca4-e432-492b-b389-1aa0090ab21f@2g2000hsn.googlegroups.com>
On Mar 12, 4:36 pm, Pascal Bourguignon <····@informatimago.com> wrote:
> So I can only say that I've got no problem with your code on clisp
> with inferior-lisp.
> ...

Thanks for the help, Pascal.  I'll do without the
error checking for now, and maybe try clisp and/or
post to the sbcl mailing list later.

--Dan

------------------------------------------------
Dan Bensen
http://www.prairienet.org/~dsb/
From: Richard M Kreuter
Subject: Re: Error reading delimited list
Date: 
Message-ID: <8763vq3a3k.fsf@progn.net>
danb <·········@gmail.com> writes:

> On Mar 12, 4:36 pm, Pascal Bourguignon <····@informatimago.com> wrote:
>> So I can only say that I've got no problem with your code on clisp
>> with inferior-lisp.
>> ...
>
> Thanks for the help, Pascal.  I'll do without the
> error checking for now, and maybe try clisp and/or
> post to the sbcl mailing list later.

FWIW, I didn't have any trouble with your code under SBCL, either.

Could it be that you're hosing the readtable that SLIME uses, and that
the errors you're seeing are a result of that?

--
RmK
From: danb
Subject: Re: Error reading delimited list
Date: 
Message-ID: <54456b5c-b02d-4933-bfc9-38e8a1bfcdd3@59g2000hsb.googlegroups.com>
On Mar 13, 10:41 am, Richard M Kreuter <·······@progn.net> wrote:
> FWIW, I didn't have any trouble with your code under SBCL, either.
> Could it be that you're hosing the readtable that SLIME uses,
> and that the errors you're seeing are a result of that?

I have no idea.  I edited the text of the reader function's
error message, and the updated message was displayed.
So it's definitely the most recent version of the function
that's getting called.  And the list is just the output of
read-delimited-list, which is being fed the terminal char
explicitly.  Why would the list returned be nil?  Wouldn't
R-D-L have to be exiting immediately?  Alternatively, is
there a way to check Slime's readtable?

--Dan

------------------------------------------------
Dan Bensen
http://www.prairienet.org/~dsb/
From: danb
Subject: Re: Error reading delimited list
Date: 
Message-ID: <30bcbe2b-468d-4e27-b642-22e13840658a@s50g2000hsb.googlegroups.com>
On Mar 12, 4:36 pm, Pascal Bourguignon <····@informatimago.com> wrote:
> In general, I try to avoid modifying the current readtable, but bind
> temporarily *readtable* just to read what needs it:
>    (let ((*readtable* *strange-readtable*)) (read strange-stream))

Is there a way to associate readtables with packages?  It would be
nice to define a readtable in a library and have all packages and/or
files that use that library inherit the library's readtable along with
its functions etc., but for the system to revert to its previous
readtable once the current file is read.

--Dan

------------------------------------------------
Dan Bensen
http://www.prairienet.org/~dsb/
From: Tobias C. Rittweiler
Subject: Re: Error reading delimited list
Date: 
Message-ID: <874pbaev5v.fsf@freebits.de>
danb <·········@gmail.com> writes:

> On Mar 12, 4:36 pm, Pascal Bourguignon <····@informatimago.com> wrote:
> > In general, I try to avoid modifying the current readtable, but bind
> > temporarily *readtable* just to read what needs it:
> >    (let ((*readtable* *strange-readtable*)) (read strange-stream))
>
> Is there a way to associate readtables with packages?  It would be
> nice to define a readtable in a library and have all packages and/or
> files that use that library inherit the library's readtable along with
> its functions etc., but for the system to revert to its previous
> readtable once the current file is read.

Yes, it is in SLIME; look at SWANK:*readtable-alist*.

A more coherent way of defining readtables is on its way, see

  http://groups.google.com/group/comp.lang.lisp/msg/21d290d6b0d8238b?dmode=source

and

  http://tinyurl.com/2nq65p

 -T.
From: Pascal J. Bourguignon
Subject: Re: Error reading delimited list
Date: 
Message-ID: <7ctzjapy6p.fsf@pbourguignon.anevia.com>
danb <·········@gmail.com> writes:

> On Mar 12, 4:36 pm, Pascal Bourguignon <····@informatimago.com> wrote:
>> In general, I try to avoid modifying the current readtable, but bind
>> temporarily *readtable* just to read what needs it:
>>    (let ((*readtable* *strange-readtable*)) (read strange-stream))
>
> Is there a way to associate readtables with packages?  It would be
> nice to define a readtable in a library and have all packages and/or
> files that use that library inherit the library's readtable along with
> its functions etc., but for the system to revert to its previous
> readtable once the current file is read.

There's one thing CL does to help you, it's that LOAD does the equivalent of:

(defun load (...)
  (let ((*readtable* (copy-readtable *readtable*))
        (*package*   *package*))
    (loop (eval (read ...)))))

so you can change the *package* and the *readtable* all you want,
INSIDE the files, and only for the duration of the file.

;;;; -*- mode:common-lisp; package:example; coding:utf-8 -*-

(in-package "EXAMPLE")
(eval-when (:compile-toplevel :load-toplevel) (setf *readtable* *my-own-specific-readtable*))

-- Here you can use your own specific reader macros.

;;;; THE END ;;;;

-- 
__Pascal Bourguignon__
From: Kent M Pitman
Subject: Re: Error reading delimited list
Date: 
Message-ID: <u3aqup78u.fsf@nhplace.com>
···@informatimago.com (Pascal J. Bourguignon) writes:

> There's one thing CL does to help you, it's that LOAD does the equivalent of:
> 
> (defun load (...)
>   (let ((*readtable* (copy-readtable *readtable*))
>         (*package*   *package*))
>     (loop (eval (read ...)))))
> 
> so you can change the *package* and the *readtable* all you want,
> INSIDE the files, and only for the duration of the file.

Two things, importantly, not just one.  COMPILE-FILE conspires in the game,
too.  If it didn't, you'd have to worry about 
  (eval-when (... :compile-toplevel ...) ...)
causing a problem.

But indeed, what you say subsequently is correct, and follows from the
cooperative treatment of both LOAD and COMPILE-FILE.

> so you can change the *package* and the *readtable* all you want,
> INSIDE the files, and only for the duration of the file.
> 
> ;;;; -*- mode:common-lisp; package:example; coding:utf-8 -*-
> 
> (in-package "EXAMPLE")
> (eval-when (:compile-toplevel :load-toplevel) (setf *readtable* *my-own-specific-readtable*))
> 
> -- Here you can use your own specific reader macros.
> 
> ;;;; THE END ;;;;

I personally to use a macro to hide that idiom, though.

(defmacro in-syntax (variable)
  ;; This check is to force me to write a separate definition of the
  ;; readtable in a variable to use in various places.
  (check-type variable (and symbol
                            (not (satisfies constantp))
                            (satisfies boundp))
             "a defined variable")
  `(eval-when (:execute :compile-toplevel :load-toplevel)
     (setq *readtable* ,variable)))

which allows you to do some foothold stuff:

 ----------- package.lisp ---------------- 

 (defpackage "MY-PACKAGE" ...)

 ----------- reader.lisp ----------------

 (in-package "MY-PACKAGE")
 (defvar *my-readtable* ...)

 ----------------------------------------

and then another file that uses reader.lisp:

 ----------- .lisp ----------------

 (in-package "MY-PACKAGE")
 (in-syntax *my-readtable*)

 ... stuff that uses the readtable ...

----------------------------------------
 
Ok, beyond this point in this message, I'm not discussing CL any more,
except in the context of history.  

Symbolics Genera users (and history buffs) may also want to see:

  http://groups.google.com/group/comp.lang.lisp/msg/d32bfc611fa10277?

for a discussion of what has been done historically in this area
[but is not part of CL], particularly the mention of "package universes"
toward the end, since the issue of hierarchical packages is related, but
not really the topic at hand.

Each readtable had an associated "syntax" which is an actual first
class thing, containing the thing referred to in the file's attribute
as a "Syntax:" as in
   -*- Mode: LISP; Syntax: ANSI-Common-Lisp; Package: CL-USER; -*- 
The syntax object has a "package universe" [see above-quoted message]
against which all packages are found, so each syntax (Zetalisp,
Common-Lisp, ANSI-Common-Lisp, CLTL, CLOE, Lisp+C, and so on--I think
there were seven dialects in the standard distribution, each with
subtlely different and conflicting sets of initial package names ... I
can't recall the seventh, but it might have been another CLTL variant)
has, potentially, its own set of private package names (and then
package name search falls through to the shared system ones if the
private ones are not needed).

This is how ZL:::USER (globally named ZETALISP-USER) and CL:::USER
(globally named COMMON-LISP-USER) were both able to be called the
"USER" package within their respective packages.  It wasn't really
done by having the FIND-PACKAGE function examine the readtable, but by
having the readtable determine which VERSION of FIND-PACKAGE was
available, so that ZL programs used ZL:::USER:FIND-PACKAGE (that is,
USER:FIND-PACKAGE from the FIND-PACKAGE found on the package named
USER that goes with the ZL syntax) and CL programs used
CL:::USER:FIND-PACKAGE (that is, USER:FIND-PACKAGE from the
FIND-PACKAGE found on the package named USER that goes with the CL
syntax), and consequently programs in both languages were effectively
"closed over" a package universe even if the package changed between.
A hidden functionality was available to allow you to look up relative
to the current readtable's theory of which package universe to use,
but it was assumed users would confuse themselves (and in any case it
would have been non-conforming) for the FIND-PACKAGE function to be
sensitive to this.

On Genera, it was really quite tricky shoehorning so many incompatible
dialects of Lisp gracefully into a single address space.  I didn't
come up with the idea of doing multiple syntaxes, but I did take the
two original ones (CL and ZL) and generalize it to arbitrary syntaxes,
implementing the hairy web of syntaxes and "inventing" (or snatching)
the triple-colon notation for accessing syntaxes (since CL had left
that notation reserved).  Doing so allowed us to easily implement This
was a lot of behind-the-scenes work that theoretically was visible to
users, but it was one of those things where finding it was obscure,
and I had bet people mostly wouldn't trip over it (other than by
browsing sources); and I think my bet was right on that one.
From: Lars Brinkhoff
Subject: Re: Error reading delimited list
Date: 
Message-ID: <854pb9zfpd.fsf@junk.nocrew.org>
···@informatimago.com (Pascal J. Bourguignon) writes:
> There's one thing CL does to help you, it's that LOAD does the
> equivalent of:
>
> (defun load (...)
>   (let ((*readtable* (copy-readtable *readtable*))
>         (*package*   *package*))
>     (loop (eval (read ...)))))

As far as I can tell from the CLHS page on LOAD, you don't get the
standard readtable, but the current one, i.e.
  (let ((*readtable* *readtable*) ...
From: Pascal J. Bourguignon
Subject: Re: Error reading delimited list
Date: 
Message-ID: <7cod9ho3v0.fsf@pbourguignon.anevia.com>
Lars Brinkhoff <·········@nocrew.org> writes:

> ···@informatimago.com (Pascal J. Bourguignon) writes:
>> There's one thing CL does to help you, it's that LOAD does the
>> equivalent of:
>>
>> (defun load (...)
>>   (let ((*readtable* (copy-readtable *readtable*))
>>         (*package*   *package*))
>>     (loop (eval (read ...)))))
>
> As far as I can tell from the CLHS page on LOAD, you don't get the
> standard readtable, but the current one, i.e.
>   (let ((*readtable* *readtable*) ...


Correct, the loaded file is allowed to modify the *readtable* object.


It's up to the file to do:

     (setf *readtable* (copy-readtable nil)) 
     (set-macro-character ...)

to avoid leaking its reader macros to the global environment.


-- 
__Pascal Bourguignon__
From: Steven M. Haflich
Subject: Re: Error reading delimited list
Date: 
Message-ID: <X3ICj.19819$Ej5.17392@newssvr29.news.prodigy.net>
Pascal J. Bourguignon wrote:

> It's up to the file to do:
> 
>      (setf *readtable* (copy-readtable nil)) 
>      (set-macro-character ...)
> 
> to avoid leaking its reader macros to the global environment.

It is, I think, a good thing that load lambda binds *package* and 
*readtable* so that the values of these variables will not inadvertently 
be modified, but I consider it a botch in the language that we of X3J13 
didn't provide some way to set these variables outside the call to load 
if that is indeed what the file really intends to do.  Too many times 
one wants something like a configuration file to do this.

Here is an interesting hack programming challenge, if anyone wants to 
attempt a solution:

Despite the lambda binding of *package* and *readtable* by load, load 
does not prevent to load-time code from modifying the package or 
readtable object that was the value of *package* or *readtable* when 
load was called.  The challenge is to write a function that can be 
called at load time that liberally uses copy-readtable, rename-package, 
and the other readtable and package modifying functions that modify the 
current readtable or package to be equivalent to some other desired 
readtable or package, including in the package case giving the package 
the name of the new package.

I believe both of these functions are computable, but only with 
difficulty.  They are _not_ semantically transparent, of course, because 
functions and variables may have already captured various package and 
readtable objects, and the captured objects will not be eql to the newly 
modified objects that are the value of *package* and *readtable*.
From: Kent M Pitman
Subject: Re: Error reading delimited list
Date: 
Message-ID: <ulk4kctkj.fsf@nhplace.com>
"Steven M. Haflich" <···@alum.mit.edu> writes:

> It is, I think, a good thing that load lambda binds *package* and
> *readtable* so that the values of these variables will not
> inadvertently be modified,

As you surely recall, Steve, the X3J13 issue that changed this wasn't
written to keep users from changing it inadvertently, but rather to 
allow them to change it advertently. (wow--I looked and that's really
a word.. I never knew.. I had assumed inadvertently had no inverse.)

The difference between these two things is important because it can't
possibly keep you from doing something stupid (such as some of the
things you cite later in your message).  All it can do is to allow you
to do something safe in a particular well-known way.

That is, my original proposal (which is why the issue is named as it
is) was to have an IN-SYNTAX macro like IN-PACKAGE), and the group
decided the macro was something a user could write if they wanted, but
that they would bargain down to just having these functions bind the
relevant variables.

> but I consider it a botch in the language that we of X3J13 didn't
> provide some way to set these variables outside the call to load if
> that is indeed what the file really intends to do.

Well, this would beg the question: how far outside.  That's like
saying you would like a way to set a special (or even lexical)
variable outside of its scope.

Classically, the way to do either what you propose or the other two
things I suggested, is to make a different side-effect to something
you can change, providing data upward, and then have the caller either
install that data or run some function you've provided. e.g.,

 (defvar *foo* 3)
 (funcall (let ((*foo* 4)) (lambda () (setq *foo* 5))))
 => 5
 *foo*
 => 5

Likewise in LOAD, the usual thing I do in all of my programs that want
to pass environment stuff back is to have the loaded file define some
function, let's call it SETUP-STATE, and then in the loading file do
(SETUP-STATE) after the load if it wants to use that state.

> Too many times one wants something like a configuration file to do
> this.

I don't discount having wanted to do this, however, at the same time,
I've generally suggested to people that files should NOT actually set
state (other than definitions of functions and variables needed to
make an algorithm work), in order to allow the file to be reloaded
with corrected definitions.

The two-pronged effect of doing a LOAD and then calling a function
often leads to better style in a number of situations, not just
working around this alleged bug.  For example, put putting what
amounts to a "script" in a function, it means the function can be
called more than once without the file I/O overhead, and it allows a
proper functional way of providing arguments.  A file is a usually
high-overhead way of doing the same.

But if you want that, it's easy enough to define:

 (defun eval-file-contents (file)
   (with-open-file (stream file)
     (loop with eof = (list 'eof)
           for form = (read stream nil eof)
           while (not (eq form eof))
           do (eval form))))

> Here is an interesting hack programming challenge, if anyone wants to
> attempt a solution:
> 
> Despite the lambda binding of *package* and *readtable* by load,
> load does not prevent to load-time code from modifying the package
> or readtable object that was the value of *package* or *readtable*
> when load was called.  The challenge is to write a function that can
> be called at load time that liberally uses copy-readtable,
> rename-package, and the other readtable and package modifying
> functions that modify the current readtable or package to be
> equivalent to some other desired readtable or package, including in
> the package case giving the package the name of the new package.

I see where you're going with this, but I think you're implicitly
advocating a terrible programming style by doing this.  I think the
things I've done above are a better way to do that.

Indeed, while it's possible to do even simpler things in loading
files, like have them set the current readtable, I advocate not doing
that.  Rather, I think the file should provide a way to either set the
current readtable appropriately (and allow the loader of the file to
call it) or else to have the file define a readtable that someone can
just use, or both (by having the latter readtable be set by the former
function).

> I believe both of these functions are computable, but only with
> difficulty.  They are _not_ semantically transparent, of course,
> because functions and variables may have already captured various
> package and readtable objects, and the captured objects will not be
> eql to the newly modified objects that are the value of *package*
> and *readtable*.

Ah good.  So you know you're advocating bad style, too, ... and I'm
just saying it explicitly so that people don't let that go quietly
past.

It's good to understand the power at one's fingertips.  But the having
of power is not always a call to use it.  Just look at the US
presidential administration under Bush and you can see what happens
when people don't understand that.

In fact, people often yield great power to certain others precisely
because they trust that it will not, in general, be used.
Programming is like that, too.  Learning programming style is about
which power is ok to use casually and which is not.