From: Brian Seitz
Subject: appropriate use of eval?
Date: 
Message-ID: <wxf656oc26c.fsf@dragon.stsci.edu>
I'm curious if the following is an appropriate use of eval.  My goal
is to provide a syntax for redefining logical pathname translations
that's a clean as possible for end-users.  It must be able to
incorporate the values of environment variables, so it needs an (env)
and a (concat).  The syntax I've come up with is the following:

(define-spike-dirs
    ("blah1" (concat (env "DATA_DIR") "bar/blah1/"))
    ("blah2" (concat (env "DATA_DIR") "foo/blah2/")))

So it takes an arbitrary number of two-element lists, where the first
part of each list is the symbolic name we are redefining and the
second part is the machine-dependent pathname string.

The code is this:

(defun env (x)
  (sys:getenv x))

(defun concat (&rest args)
  (apply #'concatenate 'string args))

(defmacro define-spike-dirs (&rest list)
  (let ((collect))
    (dolist (x list)
      (let ((symbolic (eval (first x)))
	    (real (eval (second x))))
	(push `(buffy:modify-translation "spike" ,symbolic ,real)
	      collect)))
    `(progn ,@collect)))

The function (buffy:modify-translations) munges the arguments a bit,
adding pathname separators and wilds where necessary.  The "spike"
argument is the logical host.

Okay, so I'm using eval to individually eval the two elements of the
lists.  This is exactly what I want and it works.  (Of course, in this
example, I don't need to (eval) the first arg, but it doesn't hurt.)
Is there a non-eval-using way to do this without changing the syntax
of (define-spike-dirs)?  I know I could require backquoting or using
the function (list) in the syntax, but that's not the point.

I'm also curious if the last line of the macro is the easiest way to
specify what I want.  I could make the (dolist) part of the macro
expansion and avoid that apparent awkwardness, but my version of that
made the macro definition a lot longer (though now the macro expansion
could possibly be longer).

I'm not looking to get into a discussion about logical pathnames, by
the way.  I know this is not a conventional way of loading them, but
it's the best for my situation.

Thanks,

Brian

From: Matthew Danish
Subject: Re: appropriate use of eval?
Date: 
Message-ID: <20040908152420.GA408@mapcar.org>
On Wed, Sep 08, 2004 at 09:36:59AM -0400, Brian Seitz wrote:
> (define-spike-dirs
>     ("blah1" (concat (env "DATA_DIR") "bar/blah1/"))
>     ("blah2" (concat (env "DATA_DIR") "foo/blah2/")))
> 
> Okay, so I'm using eval to individually eval the two elements of the
> lists.  This is exactly what I want and it works.  (Of course, in this
> example, I don't need to (eval) the first arg, but it doesn't hurt.)
> Is there a non-eval-using way to do this without changing the syntax
> of (define-spike-dirs)?  I know I could require backquoting or using
> the function (list) in the syntax, but that's not the point.

You don't want to use macros to evaluate code; you use macros to
re-arrange code so that normal Lisp evaluation can understand it.

(defmacro define-spike-dirs (&rest list)
  `(progn 
     ,@(loop for (symbolic real) in list collecting
	     `(buffy:modify-translation "spike" ,symbolic ,real))))

This macro merely transforms the code, it allows evaluation to occur at
the proper time.  Now you can use macroexpand-1 to debug uses of this
macro.  

-- 
;;;; Matthew Danish -- user: mrd domain: cmu.edu
;;;; OpenPGP public key: C24B6010 on keyring.debian.org
From: Wade Humeniuk
Subject: Re: appropriate use of eval?
Date: 
Message-ID: <8gF%c.79518$jZ5.8328@clgrps13>
Brian Seitz wrote:


> (defmacro define-spike-dirs (&rest list)
>   (let ((collect))
>     (dolist (x list)
>       (let ((symbolic (eval (first x)))
> 	    (real (eval (second x))))
> 	(push `(buffy:modify-translation "spike" ,symbolic ,real)
> 	      collect)))
>     `(progn ,@collect)))

Why not just do?

(defmacro define-spike-dirs (&rest list)
   `(progn
      ,@(mapcar (lambda (dir)
                  `(buffy:modify-translation "spike" ,(first dir) ,(second dir)))
                list)))

There is no need to eval the args, they are inserted into the expansion
and then run, all with the proper calls to env and concat.

Wade
From: Pascal Bourguignon
Subject: Re: appropriate use of eval?
Date: 
Message-ID: <871xhcdb94.fsf@thalassa.informatimago.com>
Brian Seitz <······@dragon.stsci.edu> writes:

> I'm curious if the following is an appropriate use of eval.  My goal
> is to provide a syntax for redefining logical pathname translations
> that's a clean as possible for end-users.  It must be able to
> incorporate the values of environment variables, so it needs an (env)
> and a (concat).  The syntax I've come up with is the following:
> 
> (define-spike-dirs
>     ("blah1" (concat (env "DATA_DIR") "bar/blah1/"))
>     ("blah2" (concat (env "DATA_DIR") "foo/blah2/")))
> 
> So it takes an arbitrary number of two-element lists, where the first
> part of each list is the symbolic name we are redefining and the
> second part is the machine-dependent pathname string.
> 
> The code is this:
> 
> (defun env (x)
>   (sys:getenv x))
> 
> (defun concat (&rest args)
>   (apply #'concatenate 'string args))
> 
> (defmacro define-spike-dirs (&rest list)
>   (let ((collect))
>     (dolist (x list)
>       (let ((symbolic (eval (first x)))
> 	    (real (eval (second x))))
> 	(push `(buffy:modify-translation "spike" ,symbolic ,real)
> 	      collect)))
>     `(progn ,@collect)))

> Okay, so I'm using eval to individually eval the two elements of the
> lists.  This is exactly what I want and it works.  (Of course, in this
> example, I don't need to (eval) the first arg, but it doesn't hurt.)
> Is there a non-eval-using way to do this without changing the syntax
> of (define-spike-dirs)?  I know I could require backquoting or using
> the function (list) in the syntax, but that's not the point.

The point is that you're evaluating the arguments at "macro-expansion
time" (ie, generally, at compilation time).  Is it really the value of
(env "DATA_DIR") at compilation time what you want?  I guess not,
usually we fetch environment variables at run time, to let the user
customize the behavior of the program.  So you probably want to
evaluate them at run time. See Wade's message.

(Everything in a defmacro but the resulting value is evaluated at
compilation time.)


> I'm also curious if the last line of the macro is the easiest way to
> specify what I want.  I could make the (dolist) part of the macro
> expansion and avoid that apparent awkwardness, but my version of that
> made the macro definition a lot longer (though now the macro expansion
> could possibly be longer).

See above, in this case you'll want to loop over the arguments at run time.


> I'm not looking to get into a discussion about logical pathnames, by
> the way.  I know this is not a conventional way of loading them, but
> it's the best for my situation.

(Yes. There is the "standard", but implementation defined
LOAD-LOGICAL-PATHNAME-TRANSLATIONS).

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

Our enemies are innovative and resourceful, and so are we. They never
stop thinking about new ways to harm our country and our people, and
neither do we.
From: Coby Beck
Subject: Re: appropriate use of eval?
Date: 
Message-ID: <kLF%c.76662$S55.864@clgrps12>
"Brian Seitz" <······@dragon.stsci.edu> wrote in message
····················@dragon.stsci.edu...
> I'm curious if the following is an appropriate use of eval.  My goal
> is to provide a syntax for redefining logical pathname translations
> that's a clean as possible for end-users.  It must be able to
> incorporate the values of environment variables, so it needs an (env)
> and a (concat).  The syntax I've come up with is the following:
>
> (define-spike-dirs
>     ("blah1" (concat (env "DATA_DIR") "bar/blah1/"))
>     ("blah2" (concat (env "DATA_DIR") "foo/blah2/")))
>
> So it takes an arbitrary number of two-element lists, where the first
> part of each list is the symbolic name we are redefining and the
> second part is the machine-dependent pathname string.
>
> The code is this:
>
> (defun env (x)
>   (sys:getenv x))
>
> (defun concat (&rest args)
>   (apply #'concatenate 'string args))
>
> (defmacro define-spike-dirs (&rest list)
>   (let ((collect))
>     (dolist (x list)
>       (let ((symbolic (eval (first x)))
>             (real (eval (second x))))
>          (push `(buffy:modify-translation "spike" ,symbolic ,real)
>                 collect)))
>     `(progn ,@collect)))

It is not clear to me that the eval is required here at all...if I may
combine that with a style suggestion at the same time:

(defmacro define-spike-dirs (&rest list)
   `(progn
       ,@(mapcar #'(lambda (item)
                    `(buffy:modify-translation
                      "spike" ,(first item) ,(second item)))
                  ,list)))

Also, loop would be simpler than dolist and a local var:

,@(loop for item in list
        collect `(buffy:modify-translation
                  "spike" ,(first item) ,(second item)))

I think the only difference between this and adding an EVAL would be forcing
your ENV call to happen at macroexpansion time rather than load time which
*seems* to be at best unnecessary and at worst wrong.

The only situations I can think of where you want code to run at
macroexpansion time all involve side effects.

> Okay, so I'm using eval to individually eval the two elements of the
> lists.  This is exactly what I want and it works.  (Of course, in this

I suspect it "works" because (equal "foo" (eval "foo"))

> I'm also curious if the last line of the macro is the easiest way to
> specify what I want.  I could make the (dolist) part of the macro
> expansion and avoid that apparent awkwardness, but my version of that
> made the macro definition a lot longer (though now the macro expansion
> could possibly be longer).

See the above examples for (in my experience) more idiomatic expressions of
the same idea.

-- 
Coby Beck
(remove #\Space "coby 101 @ big pond . com")
From: Brian Seitz
Subject: Re: appropriate use of eval?
Date: 
Message-ID: <wxfy8jkpubb.fsf@dragon.stsci.edu>
"Coby Beck" <·····@mercury.bc.ca> writes:

> 
> ,@(loop for item in list
>         collect `(buffy:modify-translation
>                   "spike" ,(first item) ,(second item)))
> 
> I think the only difference between this and adding an EVAL would be forcing
> your ENV call to happen at macroexpansion time rather than load time which
> *seems* to be at best unnecessary and at worst wrong.
> 
> The only situations I can think of where you want code to run at
> macroexpansion time all involve side effects.
> 

Duh, I am a moron.  Several of my previous versions of this macro did
not do that at macroexpansion time.  I just got caught up in getting
the syntax the way I wanted and lost my way.

Thanks everyone.
From: Coby Beck
Subject: Re: appropriate use of eval?
Date: 
Message-ID: <1TO%c.80111$jZ5.59657@clgrps13>
"Brian Seitz" <······@dragon.stsci.edu> wrote in message
····················@dragon.stsci.edu...
> "Coby Beck" <·····@mercury.bc.ca> writes:
> > The only situations I can think of where you want code to run at
> > macroexpansion time all involve side effects.
> >
>
> Duh, I am a moron.  Several of my previous versions of this macro did
> not do that at macroexpansion time.  I just got caught up in getting
> the syntax the way I wanted and lost my way.

I think we've all been there!  ;-)

-- 
Coby Beck
(remove #\Space "coby 101 @ big pond . com")