From: Christopher W. Bowron
Subject: Binding question
Date: 
Message-ID: <63vk05$9dh$1@msunews.cl.msu.edu>
I'm new to LISP (I've been using it for a few weeks) and I'm trying to
set up a couple bindings for accessing a list... I'd like to do
something like this...

(bind a (nth 5 mylist))
... random code ...
(setf a newvalue)

and have it change the item in the original list... any easy way to do
this?

Thanks in advance..

--
----------
  Christopher W. Bowron
     ········@cps.msu.edu
	http://www.cps.msu.edu/~bowronch
	   ----------

From: Rainer Joswig
Subject: Re: Binding question
Date: 
Message-ID: <joswig-ya023180000711972233490001@news.lavielle.com>
In article <············@msunews.cl.msu.edu>,
········@aubineth.user.msu.edu (Christopher W. Bowron) wrote:

> I'm new to LISP (I've been using it for a few weeks) and I'm trying to
> set up a couple bindings for accessing a list... I'd like to do
> something like this...
> 
> (bind a (nth 5 mylist))
> ... random code ...
> (setf a newvalue)

(let ((list '(foo bar baz)))
  (setf (nth 2 list) 'win)
  list)

-- 
http://www.lavielle.com/~joswig/
From: Kent M Pitman
Subject: Re: Binding question
Date: 
Message-ID: <sfw67q4avpt.fsf@world.std.com>
WARNING: Somewhat long stream-of-consciousness essay follows.
 Read at your own risk; don't say I didn't warn you.
 Oh, and if you're new to Lisp, this material might be beyond
 your level.  Don't let that scare you.  Ask questions if you like.

······@lavielle.com (Rainer Joswig) writes:

> In article <············@msunews.cl.msu.edu>,
> ········@aubineth.user.msu.edu (Christopher W. Bowron) wrote:
> 
> > I'm new to LISP (I've been using it for a few weeks) and I'm trying to
> > set up a couple bindings for accessing a list... I'd like to do
> > something like this...
> > 
> > (bind a (nth 5 mylist))
> > ... random code ...
> > (setf a newvalue)
> 
> (let ((list '(foo bar baz)))
>   (setf (nth 2 list) 'win)
>   list)

[Naughty, naughty.  Side-effect to constant structure.  I'm 
 sure you meant (list 'foo 'bar 'baz). :-]

The thing about this that troubles me is that (nth ...) is so
inviting to use.  One good thing about CAR/CDR/CAAR/CADR/etc.
is that after a while, you have to start using more operators
and you start to wonder "hey! isn't this getting slow?"  when
you write (nth 5 ...) you hardly think about the fact that what
you are accessing is 5 down the list. 

You see, strictly, the answer to Christopher's question is:

 (let ((mylist (list 1 2 3 4 5)))
   (symbol-macrolet ((a (nth 0 mylist))
                     (b (nth 1 mylist))
                     (c (nth 2 mylist))
                     (d (nth 3 mylist))
                     (e (nth 4 mylist)))
     (setq a 'one)
     (setq b (cons c d))
     (incf e))
   mylist)
 => (ONE (3 . 4) 3 4 6)

That is, there really is an operator in Lisp, called 
SYMBOL-MACROLET, which is the low-level way in which CLOS
instance variables are implemented which allows you to alias
a variable to an expression and whenever that variable is used
(even in SETQ, not just in SETF) within the scope of the 
current lexical binding, the expression will be used instead.
You can see this in:

 (let ((mylist (list 1 2 3 4 5)))
   (symbol-macrolet ((a (nth 0 mylist))
                     (b (nth 1 mylist))
                     (c (nth 2 mylist))
                     (d (nth 3 mylist))
                     (e (nth 4 mylist)))
     (macrolet ((expansion-of (x &environment env)
                  `',(macroexpand-1 x env)))
       (expansion-of e))))
 => (NTH 4 MYLIST)

Anyway, one way to think about this is to say that just because
something LOOKS like a variable doesn't mean it's constant time to
access.  I happen to think that's a bad way to look at it though.  I
prefer instead to think that if it looks like a variable and doesn't
have the property that variables have of being constant time access,
then maybe it ought not BE made to look so convenient to use.

But NTH looks like constant time even when exposed (because people
mistakenly think lists are vectors).  So really I am observing that
the use of SYMBOL-MACROLET isn't the "bug" here but the use of NTH is
in many cases a bad choice of a way to access a set of objects.
Maybe better in some cases to get more used to using arrays, for which
AREF is much faster.

Then again, the indexes stay fairly small, NTH may not be THAT bad
and there may be nothing wrong with adding the symbol-macrolet layer.
Hmmm. Judgment call, I suppose.  I just cringe as I think of 
(SETF (NTH 50 ..) ..) for example and hope it's not being done in a loop.

Another approach is to trade in SYMBOL-MACROLET's "call by reference"
feel for a "call by value/return" approach like:

 (let ((mylist (list 1 2 3 4 5)))
   (destructuring-bind (a b c d e) mylist
     (setq a 'one)
     (setq b (cons c d))
     (incf e)
     (let ((temp mylist))
       (setf (car temp) a) (pop temp)
       (setf (car temp) b) (pop temp)
       (setf (car temp) c) (pop temp)
       (setf (car temp) d) (pop temp)
       (setf (car temp) e) (pop temp)))
   mylist)
 => (ONE (3 . 4) 3 4 6)

One could presumably package that into a macro like:

(defmacro with-element-names (vars exp &body forms)
 (let ((lvar (gensym "LIST"))
       (tvar (gensym "TEMP")))
   `(let ((,lvar ,exp))
      (destructuring-bind ,vars ,lvar
        (multiple-value-prog1 (progn ,@forms)
          (let ((,tvar ,lvar))
            ,@(mapcan #'(lambda (var)
                          (list `(setf (car ,tvar) ,var)
			        `(pop ,tvar)))
                      vars)))))))

so that you could just write:

 (let ((mylist (list 1 2 3 4 5)))
   (with-element-names (a b c d e) mylist
     (setq a 'one)
     (setq b (cons c d))
     (incf e))
   mylist)
 => (ONE (3 . 4) 3 4 6)

But note here the  main difference is that while executing the macro,
the list is not changed--all the updating to the list is done at the
end.  If an ABORT happens, the update is not done.  (That MIGHT be a
desirable behavior, actually; but if that were deemed a bug, you could
use UNWIND-PROTECT instead of MULTIPLE-VALUE-PROG1 in
WITH-ELEMENT-NAMES.) 

 (let ((mylist (list 1 2 3 4 5)))
   (catch 'cancel
     (with-element-names (a b c d e) mylist
       (setq a 'one)
       (setq b (cons c d))
       (incf e)
       (throw 'cancel nil)))
   mylist)
 => (1 2 3 4 5)

Another implication of the non-change is 

 (let ((mylist (list 1 2 3 4 5)))
   (catch 'oops
     (with-element-names (a b c d e) mylist
       (setq a 'one)
       (setq b a)
       (setq c (car mylist))))
   mylist)
 => (ONE ONE 1 4 5)

vs (with SYMBOL-MACROLET):

 (let ((mylist (list 1 2 3 4 5)))
   (catch 'oops
     (symbol-macrolet ((a (nth 0 mylist))
                       (b (nth 1 mylist))
                       (c (nth 2 mylist))
                       (d (nth 3 mylist))
                       (e (nth 4 mylist))) mylist
       (setq a 'one)
       (setq b a)
       (setq c (car mylist))))
     mylist)
 => (ONE ONE ONE 4 5)

Back to the original problem, though, I think the most
natural thing you'd find in most lisp programs is just to use 
DESTRUCTURING-BIND to READ the variables and to manually update
things only if a change really happened, as in:

 (let ((mylist (list 1 2 3 4 5)))
   (destructuring-bind (a b c d e) mylist
     (setf (second mylist) (setq b (+ c d e)))
     (cons b mylist)))
 => (12 1 12 3 4 5)

Oops.  Just thought of another trick that's actually a better answer
than any of the above.  But I'll leave the above anyway in hopes 
some readers will find the exercises instructive to have worked through.

(defmacro with-element-names-2 (vars exp &body forms)
  (let* ((tempvars (mapcar #'(lambda (x) (declare (ignore x)) (gensym)) vars))
	 (inits (cons ,exp (mapcar #'(lambda (v) `(cdr ,v)) tempvars))))
    `(let* ,(mapcar #'list tempvars inits)
       (symbol-macrolet ,(mapcar #'(lambda (var tempvar)
				     `(,var (car ,tempvar)))
				 vars tempvars)
	 ,@forms))))

This has the nice property that:

 (let ((mylist (list 1 2 3 4 5)))
   (with-element-names-2 (a b c d e) mylist
     (incf a)
     (decf c)
     (copy-list mylist))) ;copy-list to take snapshot before exit of w-e-n-2
                          ;proving the update was really happening as we went
 => (2 2 2 4 5)

AND that the elements get constant time access (after an initial
setup cost, of course) because here's a sample expansion:

 (macroexpand-1 '(with-element-names-2 (a b c) mylist ...blah...))

 => (LET* ((#:G2824 MYLIST)
	   (#:G2825 (CDR #:G2824))
	   (#:G2826 (CDR #:G2825)))
      (SYMBOL-MACROLET ((A (CAR #:G2824))
			(B (CAR #:G2825))
			(C (CAR #:G2826)))
	...BLAH...)) 

Sorry for the length and rambly nature of this.  But there are too few
examples in the literature, probably, so I figured a few worked
examples here in news couldn't hurt "just for fun".  Hope I didn't
bore anyone...
From: Juliusz Chroboczek
Subject: Re: Binding question
Date: 
Message-ID: <dh37mai3iet.fsf@papa.dcs.ed.ac.uk>
In article <···············@world.std.com>, 
  ······@world.std.com (Kent M Pitman) writes:

[soliloquy about binding stuff and mutating other stuff deleted]

KMP> Sorry for the length and rambly nature of this.
KMP> Hope I didn't bore anyone...

No, to the contrary.  I'm seriously thinking about collecting the most
interesting of your contributions to this group into a web page.  `KMP
ranting and raving on Lisp', `Oldthinkers unbellyfeel Lisp', `Sleaze
allegations in X3J13 -- KMP speaks out!' or such.  Do I have your
/imprimatur/?

I'd like to ask a question about `destructuring-bind', which you used
heavily in your examples.  According to the (hyper-)spec, if the
arguments mismatch, a condition of type `error' is signalled.  This
makes it unusable for parsing data typed in by the user, or read in
from a file (unless you can establish a handler for `error' and still
sleep on nights).  I'm thinking of code such as:

(let ((form (read)))
  (cond
    ...
   ((eql (car form) 'lambda)
    (destructuring-bind (l varlist &rest body) form
      ...))))

which I wanted to produce meaningful error messages in the case of a
syntax error.  I've ended up doing the destructuring by hand.  (Guess
I've been programming in ML too much.)

What's the rationale behind that?  Is it an oversight of the
committee, who were defining `d-b' for macrology only?  Or is there
some profound and subtle argument against using `d-b' at runtime?

(Aside: what streams should I read/write from/to in an interactive
REPL?)

Sincerely,

                                        J. Chroboczek
From: Barry Margolin
Subject: Re: Binding question
Date: 
Message-ID: <643hgi$j1s@pasilla.bbnplanet.com>
In article <···············@papa.dcs.ed.ac.uk>,
Juliusz Chroboczek  <···@dcs.ed.ac.uk> wrote:
>KMP> Sorry for the length and rambly nature of this.
>KMP> Hope I didn't bore anyone...
>
>No, to the contrary.  I'm seriously thinking about collecting the most
>interesting of your contributions to this group into a web page.  `KMP
>ranting and raving on Lisp', `Oldthinkers unbellyfeel Lisp', `Sleaze
>allegations in X3J13 -- KMP speaks out!' or such.  Do I have your
>/imprimatur/?

I guess Kent's been itching to get things off his chest, ever since Lisp
Pointers went away and he lost his soapbox. :)

[Regarding destructuring-bind's limited error reporting.]

>What's the rationale behind that?  Is it an oversight of the
>committee, who were defining `d-b' for macrology only?  Or is there
>some profound and subtle argument against using `d-b' at runtime?

The expectation was that destructuring-bind would generally be used to take
apart known data structures.  Thus, a mismatch indicates a programming
error, not a user error that the program would be expected to handle.  If
you're parsing user-supplied data, you need to examine it carefully so you
can give meaningful errors; even if d-b signalled a more specific error, it
probably wouldn't be useful.

Also, if you look around, you'll see that CL doesn't really specify many
specific errors.  We simply didn't have the resources to go through the
language defining conditions for everything.  So most things are documented
as simply signalling ERROR.  There were a handful of areas where we knew
from prior experience about specific error types, such as the arithmetic
functions.

>(Aside: what streams should I read/write from/to in an interactive
>REPL?)

Probably *TERMINAL-IO*, or read from *STANDARD-INPUT* and write to
*STANDARD-OUTPUT*.

-- 
Barry Margolin, ······@bbnplanet.com
GTE Internetworking, Powered by BBN, Cambridge, MA
Support the anti-spam movement; see <http://www.cauce.org/>
Please don't send technical questions directly to me, post them to newsgroups.
From: Alistair E. Campbell
Subject: Re: Binding question
Date: 
Message-ID: <6421k0$fji$1@prometheus.acsu.buffalo.edu>
In article <············@msunews.cl.msu.edu>,
Christopher W. Bowron <········@aubineth.user.msu.edu> wrote:
�I'm new to LISP (I've been using it for a few weeks) and I'm trying to
�set up a couple bindings for accessing a list... I'd like to do
�something like this...
�
�(bind a (nth 5 mylist))
�... random code ...
�(setf a newvalue)
�
�and have it change the item in the original list... any easy way to do
�this?

Presuming you want to modify mylist, it looks like you're using a different
sense of of the word "binding" than is traditionally meant in Common Lisp
and other languages.  Binding a symbol to some object means that there is
some sort of link from the symbol to the object.  But, setf changes the
link, rather than modifying the object it points to, as you seem to want to
do.  You want "synonomy" rather than binding, e.g. "whenever I say 'A', I
mean (nth 5 mylist)" (and I like the convenience of that)

You could use a macro to accomplish this.  Here's my first stab at it:

(defmacro bind ((b c) &body forms)
  ;; replace all occurrences of b with c in the body.
  (let ((forms2 (deep-replace b c forms)))
    `(progn
       ,@forms2)))

Example:

USER(11): (macroexpand '(bind (a (nth 5 mylist)) (setf a 100)))
(PROGN (SETF (NTH 5 MYLIST) 100))
USER(12): (setf mylist '(1 2 3 4 5 6 7 8 9))
(1 2 3 4 5 6 7 8 9)
USER(13): (bind (a (nth 5 mylist)) (setf a 100))
100
USER(14): mylist
(1 2 3 4 5 100 7 8 9)

I leave deep-replace as an exercise.  :-)

-- 
Alistair E. Campbell, Lecturer                              ···@cs.buffalo.edu
Department of Computer Science                             (716) 645-3180 x129
University at Buffalo                           http://www.cs.buffalo.edu/~aec