[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Clarifications please



    Date: Thu, 28 Apr 88 18:41:40 EDT
    From: James J. Hunt <jjh at ll-vlsi.arpa>

    Suppose that I have some variable *GLOBAL* that I need to
    dynamically bind for some group of procedures: p1, p2, ... pn.
    Furthermore I know that none of these will be called before I
    execute some function that binds *GLOBAL*'s value.  BIND insists
    that *GLOBAL* has a value BEFORE BIND of *GLOBAL* is used.  However
    some other module may care about *GLOBAL*'s default value, so I
    would rather not write something like (lset *GLOBAL* nil), but
    instead (if (bound?  *GLOBAL*) (then (lset *GLOBAL* nil))).  Of
    course, I would rather that BIND did not depend on *GLOBAL* being
    bound.

I should apologize about BIND's misleading name -- it doesn't bind the
variable (like DEFINE, LAMBDA, and LSET do), it does a temporary assignment.
The variable must be bound (with DEFINE or LAMBDA or LSET) before you
can do a BIND, since otherwise there would be no location to assign.
(Locations can't be created on demand because there's no way to tell in
what environment the variable should become bound.)
However I think it would be a good idea to have a way to bind a variable
without assigning it.  MIT Scheme does this with the syntax
	(define foo)
and in T this would want to be written as
	(lset foo)
instead (because SET!, and therefore BIND, is in principle illegal on
variables bound with DEFINE -- an unfortunate incompatibility with R^3
Scheme).

I know that the SETQ-IF-UNBOUND or DEFVAR style you describe is
widespread in Maclisp and its derivatives.  (The special form you want
in T is not BOUND? but rather ASSIGNED?.)  But I have always found this
to be a rather questionable practice -- I always prefer for one
particular module to be responsible for binding a variable; i.e.
multiple LSET's on a variable should be in error just as multiple
DEFINE's are.  Otherwise conflicts and confusion are very likely.

    Likewise, (if (test) (then ...) (else ...))
    seems to me much more readable than
    (cond ((test) ...) (else ...)).

De gustibus non est disputandum.  I needn't add to the existing
critiques.  If you like your IF, define a macro.

    	1. It seem rather ugly to have to use two tables to keep track of
    	   TABLE-HAS-ENTRY? state and the table values, as is need if you
    	   don't plan to modify the source, and

No, you don't need two tables, you just need some distinguished object to
represent a false entry.  And you needn't modify the source if you
simply shadow T's definition of TABLE-ENTRY.

    	2. (walk-table (lambda (key value)
    	                 (ignore value)
    	                 (set (table-entry <table> key) '#f))
    	     <table>)
    	   does not work.  You must say (clean-table <table>).  Whereas
    	   this walk does work so long as you are not setting the table
    	   value to '#f.  This seems to be a gross anomaly!  This is
    	   especially painfull if the new value is computed from the old
    	   one.

I consulted with the person who wrote the table package and it seems
that it's an error to modify a table while it is being walked.  That it
sometimes works is gratuitous.  Unfortunately it would be prohibitively
expensive to detect this error situation; tables must be fast.  And to
change this would require that WALK-TABLE in effect copy the table
before it starts walking; that would make WALK-TABLE too expensive.

This restriction should definitely be documented.

I recommend that you create a brand new table when you want to do
something like this, and let the old one be gc'ed.