From: Randall Randall
Subject: Style and/or advisability of this symbol-macrolet usage
Date: 
Message-ID: <43619482.0407270807.63e61cd1@posting.google.com>
Hi, all.

One of the things that I hate typing, over and over, is (gethash variable hashname).

So, after asking about various ways of getting around this on #lisp, and detouring
through reader-macro-land and using a LET for a while, I finally ended up here:

(defmacro with-hash-values ((hash vars &optional hash-var) 
                                             &body body)
  "Hash must be equalp, unless all upper case is used."
  (let ((hvar     (or hash-var (gensym)))
        (s&m-list ()))
    `(let ((,hvar ,hash))
       (symbol-macrolet ,(dolist (var vars s&m-list) 
                           (push 
                             `(,var (gethash (string (quote ,var)) ,hvar)) 
                              s&m-list))
                        ,@body))))

...so that I could use hash keys as variables normally with SETF and
so on.

So, this seems so obviously useful to me that I thought that either
there's something wrong with doing it this way, or other people would
want to see it (a friend alerted me to one of these possibilities; I'll let
you guess which possibility).  :)

Is there something wrong with this sort of thing?  Am I missing something
that will poke holes in my laptop and shred my cat?

Thanks for any input!  If anyone wants this code, you can certainly use it;
I'm placing it in the public domain.  As should be obvious from the fact
that I'm asking whether it's a good idea, there is no warranty of any kind
associated with it, of course.


--
Randall Randall <·······@randallsquared.com>
Property law should use #'EQ , not #'EQUAL .

From: Erann Gat
Subject: Re: Style and/or advisability of this symbol-macrolet usage
Date: 
Message-ID: <gNOSPAMat-2BB8E7.09394927072004@nntp1.jpl.nasa.gov>
In article <····························@posting.google.com>,
 ·······@randallsquared.com (Randall Randall) wrote:

> Hi, all.
> 
> One of the things that I hate typing, over and over, is (gethash variable 
> hashname).
> 
> So, after asking about various ways of getting around this on #lisp, and 
> detouring
> through reader-macro-land and using a LET for a while, I finally ended up 
> here:
> 
> (defmacro with-hash-values ((hash vars &optional hash-var) 
>                                              &body body)
>   "Hash must be equalp, unless all upper case is used."
>   (let ((hvar     (or hash-var (gensym)))
>         (s&m-list ()))
>     `(let ((,hvar ,hash))
>        (symbol-macrolet ,(dolist (var vars s&m-list) 
>                            (push 
>                              `(,var (gethash (string (quote ,var)) ,hvar)) 
>                               s&m-list))
>                         ,@body))))
> 
> ...so that I could use hash keys as variables normally with SETF and
> so on.
> 
> So, this seems so obviously useful to me that I thought that either
> there's something wrong with doing it this way, or other people would
> want to see it (a friend alerted me to one of these possibilities; I'll let
> you guess which possibility).  :)
> 
> Is there something wrong with this sort of thing?  Am I missing something
> that will poke holes in my laptop and shred my cat?

Nope.  This is perfectly fine.  (In fact, this sort of thing is exactly 
what symbol-macrolet is for.)  A couple of minor improvements are 
possible:

You can eliminate a line of code by providing a default value for the 
optional parameter:

(defmacro with-hash-values ((hash vars &optional (hash-var (gensym)) ...

You can eliminate the s&m-list temporary by using MAPCAR (or LOOP ... 
COLLECT).

E.
From: Randall Randall
Subject: Re: Style and/or advisability of this symbol-macrolet usage
Date: 
Message-ID: <43619482.0407272149.4712ad84@posting.google.com>
Erann Gat <·········@flownet.com> wrote in message news:<·······························@nntp1.jpl.nasa.gov>...
> In article <····························@posting.google.com>,
>  ·······@randallsquared.com (Randall Randall) wrote:
> > Is there something wrong with this sort of thing?  Am I missing something
> > that will poke holes in my laptop and shred my cat?
> 
> Nope.  This is perfectly fine.  (In fact, this sort of thing is exactly 
> what symbol-macrolet is for.)  A couple of minor improvements are 
> possible:
> 
> You can eliminate a line of code by providing a default value for the 
> optional parameter:
> 
> (defmacro with-hash-values ((hash vars &optional (hash-var (gensym)) ...

Yes, but doing so puts the burden of passing in a gensym on macros that
use this and take parameters to pass to it.  That is, I have a WITH-FORM
macro that uses WITH-HASH-VALUES to supply apparent locals to it's 
body from HTML form inputs.  Since it hides the use of WITH-HASH-VALUES,
and since I want to be able to choose from arguments to WITH-FORM
whether I get a handle on the actual hash, doing the gensym in the 
lambda list forces WITH-FORM to also do a gensym for the same purpose, 
since otherwise it would pass in NIL, which is invalid.  Doing that 
extra LET in WITH-HASH-VALUES allows me to keep that required gensym 
call encapsulated inside the macro that needs it. 

> You can eliminate the s&m-list temporary by using MAPCAR (or LOOP ... 
> COLLECT).

I did take your advice regarding using something more direct than 
DOLIST/PUSH, though. :)  For completeness, my (probably final) version 
is:

(defmacro with-hash-values ((hash vars &optional hash-var) 
                            &body body)
  "Hash must be equalp, unless all upper case is used."
  (let ((hvar (or hash-var (gensym))))
    `(let ((,hvar ,hash))
       (symbol-macrolet 
        ,(mapcar (lambda (var) 
                   `(,var (gethash (string (quote ,var)) ,hvar))) 
                 vars)
        ,@body))))

Thanks for your comments, Erann!

--
Randall Randall <·······@randallsquared.com>
Property law should use #'EQ , not #'EQUAL .
From: Thomas A. Russ
Subject: Re: Style and/or advisability of this symbol-macrolet usage
Date: 
Message-ID: <ymizn5kc9xx.fsf@sevak.isi.edu>
Ingvar <······@hexapodia.net> writes:

> 
> ·······@randallsquared.com (Randall Randall) writes:
> 
> > > You can eliminate a line of code by providing a default value for the 
> > > optional parameter:
> > > 
> > > (defmacro with-hash-values ((hash vars &optional (hash-var (gensym)) ...
> > 
> > Yes, but doing so puts the burden of passing in a gensym on macros that
> > use this and take parameters to pass to it.  That is, I have a WITH-FORM
> > macro that uses WITH-HASH-VALUES to supply apparent locals to it's 
> > body from HTML form inputs.  Since it hides the use of WITH-HASH-VALUES,
> > and since I want to be able to choose from arguments to WITH-FORM
> > whether I get a handle on the actual hash, doing the gensym in the 
> > lambda list forces WITH-FORM to also do a gensym for the same purpose, 
> > since otherwise it would pass in NIL, which is invalid.  Doing that 
> > extra LET in WITH-HASH-VALUES allows me to keep that required gensym 
> > call encapsulated inside the macro that needs it. 
> 
> Though that's exactly what providing a default argument for HASH-VAR
> does (that is, it's a gensym, unless an explicit argument is
> provided). It's just that the default default value is NIL and you
> check against that.

That's what I thought at first, but then I think I figured out what
Randall meant.  The issue is if you write another macro whose expansion
uses the WITH-HASH-VALUES macro.  You either have to use the same
(gensym) initializer for its optional hash-var variable or else the
macro expansion has to test for a value an expand differently.

By doing the work inside the expansion rather than in the lambda list,
the user of the macro doesn't have to know about the default value.

For example:

(defmacro with-form ((arg1 &optional var) &body body)
  `(with-hash-values (*hash* ,arg1 ,var)
     ...))

Otherwise one would need to duplicate this with something like:

(defmacro with-form ((arg1 &optional var) &body body)
  (if var
      `(with-hash-values (*hash* ,arg1 ,var)
        ...)
      `(with-hash-values (*hash* ,arg1)
        ...)))



-- 
Thomas A. Russ,  USC/Information Sciences Institute
From: Randall Randall
Subject: Re: Style and/or advisability of this symbol-macrolet usage
Date: 
Message-ID: <43619482.0407281626.7dbc289b@posting.google.com>
···@sevak.isi.edu (Thomas A. Russ) wrote in message news:<···············@sevak.isi.edu>...
> Ingvar <······@hexapodia.net> writes:
> > Though that's exactly what providing a default argument for HASH-VAR
> > does (that is, it's a gensym, unless an explicit argument is
> > provided). It's just that the default default value is NIL and you
> > check against that.
> 
> That's what I thought at first, but then I think I figured out what
> Randall meant.  The issue is if you write another macro whose expansion
> uses the WITH-HASH-VALUES macro.  You either have to use the same
> (gensym) initializer for its optional hash-var variable or else the
> macro expansion has to test for a value an expand differently.

Yes, that was what I meant, however....

> By doing the work inside the expansion rather than in the lambda list,
> the user of the macro doesn't have to know about the default value.
> 
> For example:
> 
> (defmacro with-form ((arg1 &optional var) &body body)
>   `(with-hash-values (*hash* ,arg1 ,var)
>      ...))
> 
> Otherwise one would need to duplicate this with something like:
> 
> (defmacro with-form ((arg1 &optional var) &body body)
>   (if var
>       `(with-hash-values (*hash* ,arg1 ,var)
>         ...)
>       `(with-hash-values (*hash* ,arg1)
>         ...)))

... in email, Ingvar pointed out by implication that the first form
here still assumes knowledge about the WITH-HASH-VALUES
macro, just the opposite knowledge.  Further, doing things the 
first way conflates the value and the flag about whether we got
the value, destroying the value of constructions like
(defmacro with-hash-values 
   (arg1 &optional (var DEFAULT var-supplied-p))
...)

I fell into this trap because I was writing Python algorithms
in Lisp, and Python has no way to tell if a value that matches
the default was received or supplied by the default (people 
work around this in various ways).

So, I think something like the second way must be right, 
though if I understand correctly, a WITH-FORM based on 
your example would be more compactly written:

(defmacro with-form ((arg1 &optional (var () var-supplied-p)) &body body)
  `(with-hash-values (*hash* ,arg1 ,@(when var-supplied-p `(,var)))
        ...))

--
Randall Randall <·······@randallsquared.com>
Property law should use #'EQ , not #'EQUAL .