From: Lee Spector
Subject: Help me avoid Eval
Date: 
Message-ID: <36987@mimsy.umd.edu>
I'm having trouble figuring out how to avoid eval in this seemingly
simple situation: I need to create a function on the fly in which
the forms in the lambda form will be spliced in from elsewhere.
Here's an example that makes it work, albeit using eval:

(setq xx '((print 'a)(print 'b)))
(funcall (eval `#'(lambda () ,@xx)))
=> 
A 
B 
B

I want to eliminate the use of eval not only because it's a stylistic
no-no, but because in fact I need access to the lexical environment
in which I'm actually doing this (the fleshed-out lambda form will refer
to lexical variables).

I've been twiddling with combinaitons of macros and variants of 
backquote syntax to the point of total confusion - so any help
would be appreciated!
  -Lee (·······@cs.umd.edu)

From: Barry Margolin
Subject: Re: Help me avoid Eval
Date: 
Message-ID: <1991Jul19.195643.19294@Think.COM>
In article <·····@mimsy.umd.edu> ·······@mimsy.umd.edu (Lee Spector) writes:
>I'm having trouble figuring out how to avoid eval in this seemingly
>simple situation: I need to create a function on the fly in which
>the forms in the lambda form will be spliced in from elsewhere.

By "one the fly", do you mean at runtime or compile-time, i.e. is
"elsewhere" the arguments to a macro?

>Here's an example that makes it work, albeit using eval:
>
>(setq xx '((print 'a)(print 'b)))
>(funcall (eval `#'(lambda () ,@xx)))
>=> 
>A 
>B 
>B
>
>I want to eliminate the use of eval not only because it's a stylistic
>no-no, but because in fact I need access to the lexical environment
>in which I'm actually doing this (the fleshed-out lambda form will refer
>to lexical variables).

If you mean at runtime, you can't (portably) create functions on the fly
that access the lexical environment of the creating routine.  It's called
the "lexical environment" for a reason -- it's only accessible to code that
is lexically contained within the binding form (the one exception is that
the rule is applied after macro expansion).

If you mean at macroexpansion time, then it shouldn't be very hard.  You
just have to make sure that the expansion has the lambda expression inside
the binding form that creates the lexical environment you're interested in.

I was going to include an example of this, but I'm having a really hard
time thinking of something that isn't extremely contrived and couldn't be
done better some other way.  Perhaps if you explained what you're trying to
accomplish at a higher level we could show a good way to do it.

-- 
Barry Margolin, Thinking Machines Corp.

······@think.com
{uunet,harvard}!think!barmar
From: Lee Spector
Subject: Re: Help me avoid Eval
Date: 
Message-ID: <37004@mimsy.umd.edu>
In article <······················@Think.COM> ······@think.com writes:
>
>If you mean at macroexpansion time, then it shouldn't be very hard.  You
>just have to make sure that the expansion has the lambda expression inside
>the binding form that creates the lexical environment you're interested in.
>
>I was going to include an example of this, but I'm having a really hard
>time thinking of something that isn't extremely contrived and couldn't be
>done better some other way.  Perhaps if you explained what you're trying to
>accomplish at a higher level we could show a good way to do it.

I'll give an example to clarify.  It's contrived,  but not too far from 
what I'm actually doing.  I think there probably IS a simple macro-based
solution, but I'm having trouble finding it...  maybe I'm just suffering
from staring at too many macros and backquotes - a malady to which I
am quite susceptible!  (Note: this example is in CLOS, just because
that's the context in which I ran into this.)

Suppose we want to allow people to create instances of a class DOG, which
has two slots, name and barkfns.  Barkfns will be filled with a list of
funcall-able functions, but we want the user to specify these functions
as lists (fn-name forms).  In other words, we want DOGs to be created as:
(setq ginger (make-instance 'dog 
                            :name 'ginger
                            :barkfns '((first-fn (print 'woof))
                                       (second-fn (print 'bow-wow)))))

This can be accomplished with the following:

? (defclass dog ()
    ((name :accessor name :initarg :name :initform 'fido)
     (barkfns :accessor barkfns :initarg :barkfns :initform nil)))
#<STANDARD-CLASS DOG>
 
? (defmethod initialize-instance :after ((d dog) &rest init-args)
    (declare (ignore init-args))
    (setf (barkfns d)
          (mapcar 
           #'(lambda (name-fn-pair)
               (eval `#'(lambda ()
                          (format t "~%Using barkfn:~A" 
                                  ',(car name-fn-pair))
                          ,@(cdr name-fn-pair))))
           (barkfns d))))
 
#<STANDARD-METHOD INITIALIZE-INSTANCE :AFTER (DOG)>

Now we can define a function BARK, create ginger as we wished, and make
her bark:

? (defmethod bark ((d dog))
    (mapc #'funcall (barkfns d)))
#<STANDARD-METHOD BARK (DOG)>
 
? (setq ginger (make-instance 'dog 
                              :name 'ginger
                              :barkfns '((first-fn (print 'woof))
                                         (second-fn (print 'bow-wow)))))
#<DOG #x5F46B1>
 
? (bark ginger)

Using barkfn:FIRST-FN
WOOF 
Using barkfn:SECOND-FN
BOW-WOW 
(#<Anonymous Function #x60BC76> #<Anonymous Function #x60C03E>)

The problem is that I also want to allow the user to include, say
the symbol D in the provided forms (I'd use a better symbol...) so
that one could create barkfns that refer to the dog being created:

(setq ginger (make-instance 'dog 
                              :name 'ginger
                              :barkfns '((first-fn (format t "My name is ~A"
                                                           (name d)))
                                         (second-fn (print 'bow-wow)))))

Of course, this doesn't work.  Special variables is one way out, but I'd
very much like to avoid it.  Hence my interest in getting those lambda
forms to expand properly.  If the barkfns form provided above was
textually spliced into the call to initialize-instance, and if there was no
intervening "eval" then the reference to D would work.  Perhaps, though
I'm confused about what macros can do.... 

Any suggestions would be welcome... Thanks!  -Lee  (·······@cs.umd.edu)
From: Barry Margolin
Subject: Re: Help me avoid Eval
Date: 
Message-ID: <1991Jul20.041620.4391@Think.COM>
In article <·····@mimsy.umd.edu> ·······@mimsy.umd.edu (Lee Spector) writes:
>Suppose we want to allow people to create instances of a class DOG, which
>has two slots, name and barkfns.  Barkfns will be filled with a list of
>funcall-able functions, but we want the user to specify these functions
>as lists (fn-name forms).  In other words, we want DOGs to be created as:
>(setq ginger (make-instance 'dog 
>                            :name 'ginger
>                            :barkfns '((first-fn (print 'woof))
>                                       (second-fn (print 'bow-wow)))))
>
>This can be accomplished with the following:
>
>? (defclass dog ()
>    ((name :accessor name :initarg :name :initform 'fido)
>     (barkfns :accessor barkfns :initarg :barkfns :initform nil)))
>#<STANDARD-CLASS DOG>
> 
>? (defmethod initialize-instance :after ((d dog) &rest init-args)
>    (declare (ignore init-args))
>    (setf (barkfns d)
>          (mapcar 
>           #'(lambda (name-fn-pair)
>               (eval `#'(lambda ()
>                          (format t "~%Using barkfn:~A" 
>                                  ',(car name-fn-pair))
>                          ,@(cdr name-fn-pair))))
>           (barkfns d))))
> 
>#<STANDARD-METHOD INITIALIZE-INSTANCE :AFTER (DOG)>
>
>Now we can define a function BARK, create ginger as we wished, and make
>her bark:
>
>? (defmethod bark ((d dog))
>    (mapc #'funcall (barkfns d)))

>The problem is that I also want to allow the user to include, say
>the symbol D in the provided forms (I'd use a better symbol...) so
>that one could create barkfns that refer to the dog being created:
>
>(setq ginger (make-instance 'dog 
>                              :name 'ginger
>                              :barkfns '((first-fn (format t "My name is ~A"
>                                                           (name d)))
>                                         (second-fn (print 'bow-wow)))))
>
>Of course, this doesn't work.  Special variables is one way out, but I'd
>very much like to avoid it.  Hence my interest in getting those lambda
>forms to expand properly.  If the barkfns form provided above was
>textually spliced into the call to initialize-instance, and if there was no
>intervening "eval" then the reference to D would work.  Perhaps, though
>I'm confused about what macros can do.... 

OK, I understand.

You don't need to evaluate the forms inside the method in order to provide
a variable D for them to access.  All you have to do is make the lambda
expression take an argument D, and access that.

? (defmethod initialize-instance :after ((d dog) &rest init-args)
    (declare (ignore init-args))
    (setf (barkfns d)
          (mapcar 
	    #'(lambda (name-fn-pair)
		`(lambda (d)
		   (format t "~%Using barkfn:~A" 
			   ',(car name-fn-pair))
		   ,@(cdr name-fn-pair)))
	    (barkfns d))))

Then you need to change BARK slightly:

? (defmethod bark ((d dog))
    (dolist (fn (barkfns d))
      (funcall fn d)))

By the way, this code will probably require a minor modification in ANSI
CL, due to the changes X3J13 is proposing for the function type.  FUNCALL
and APPLY will only be required to accept function names and function
objects, but not lambda expressions.  The modification is that `(lambda...)
will become (coerce `(lambda...) 'function).  The way to write this that
will be portable in both flavors of CL is (eval `#'(lambda...)); we defined
this coercion expressly so that programmers wouldn't be forced to use EVAL
for this, but the coercion is define in terms of what EVAL does (in
particular, since it happens at runtime, it's evaluated in the null lexical
environment).

-- 
Barry Margolin, Thinking Machines Corp.

······@think.com
{uunet,harvard}!think!barmar
From: Harley Davis
Subject: Re: Help me avoid Eval
Date: 
Message-ID: <DAVIS.91Jul22153430@passy.ilog.fr>
The included message is at the end.

Barmar's solution still uses EVAL, as he obliquely points out.  I
think the only way to solve the problem, guarding something like the
interface you want, is to define a dog definition macro to be used as
follows:

  (setq ginger (make-dog :name 'ginger
                         :barkfns '((fn1 (print 'woof))
                                    (fn2 (print 'bow-wow)))))

So the macro would be defined like this (should be more complicated
for good error checking):

(defmacro make-dog (init-list)
  (let ((fns (getf :barkfns init-list)))
    `(make-instance 'dog
       :name ,(getf ...)
       :barkfns (list ,(mapcar #'generate-barkfn fns)))))

(defun generate-barkfn (fn-spec)
  `#'(lambda (d)
       (print "Using " ',(car fn-spec))
       ,@(cdr fn-spec)))

The functions are still called as before with FUNCALL.  No method on
INITIALIZE-INSTANCE is needed.

Now no EVAL is ever used, and you can use D in the forms.  If you want
to use an arbitrary variable for the new instance, you must change the
syntax of the :BARKFNS function specs.  I think that would be cleaner.

-- Harley

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

In article <·····················@Think.COM> ······@think.com (Barry Margolin) writes:

   >Suppose we want to allow people to create instances of a class DOG, which
   >has two slots, name and barkfns.  Barkfns will be filled with a list of
   >funcall-able functions, but we want the user to specify these functions
   >as lists (fn-name forms).  In other words, we want DOGs to be created as:
   >(setq ginger (make-instance 'dog 
   >                            :name 'ginger
   >                            :barkfns '((first-fn (print 'woof))
   >                                       (second-fn (print 'bow-wow)))))
   >
   >This can be accomplished with the following:
   >
   >? (defclass dog ()
   >    ((name :accessor name :initarg :name :initform 'fido)
   >     (barkfns :accessor barkfns :initarg :barkfns :initform nil)))
   >#<STANDARD-CLASS DOG>
   > 
   >? (defmethod initialize-instance :after ((d dog) &rest init-args)
   >    (declare (ignore init-args))
   >    (setf (barkfns d)
   >          (mapcar 
   >           #'(lambda (name-fn-pair)
   >               (eval `#'(lambda ()
   >                          (format t "~%Using barkfn:~A" 
   >                                  ',(car name-fn-pair))
   >                          ,@(cdr name-fn-pair))))
   >           (barkfns d))))
   > 
   >#<STANDARD-METHOD INITIALIZE-INSTANCE :AFTER (DOG)>
   >
   >Now we can define a function BARK, create ginger as we wished, and make
   >her bark:
   >
   >? (defmethod bark ((d dog))
   >    (mapc #'funcall (barkfns d)))

   >The problem is that I also want to allow the user to include, say
   >the symbol D in the provided forms (I'd use a better symbol...) so
   >that one could create barkfns that refer to the dog being created:
   >
   >(setq ginger (make-instance 'dog 
   >                              :name 'ginger
   >                              :barkfns '((first-fn (format t "My name is ~A"
   >                                                           (name d)))
   >                                         (second-fn (print 'bow-wow)))))
   >
   >Of course, this doesn't work.  Special variables is one way out, but I'd
   >very much like to avoid it.  Hence my interest in getting those lambda
   >forms to expand properly.  If the barkfns form provided above was
   >textually spliced into the call to initialize-instance, and if there was no
   >intervening "eval" then the reference to D would work.  Perhaps, though
   >I'm confused about what macros can do.... 

   OK, I understand.

   You don't need to evaluate the forms inside the method in order to provide
   a variable D for them to access.  All you have to do is make the lambda
   expression take an argument D, and access that.

   ? (defmethod initialize-instance :after ((d dog) &rest init-args)
       (declare (ignore init-args))
       (setf (barkfns d)
	     (mapcar 
	       #'(lambda (name-fn-pair)
		   `(lambda (d)
		      (format t "~%Using barkfn:~A" 
			      ',(car name-fn-pair))
		      ,@(cdr name-fn-pair)))
	       (barkfns d))))

   Then you need to change BARK slightly:

   ? (defmethod bark ((d dog))
       (dolist (fn (barkfns d))
	 (funcall fn d)))

   By the way, this code will probably require a minor modification in ANSI
   CL, due to the changes X3J13 is proposing for the function type.  FUNCALL
   and APPLY will only be required to accept function names and function
   objects, but not lambda expressions.  The modification is that `(lambda...)
   will become (coerce `(lambda...) 'function).  The way to write this that
   will be portable in both flavors of CL is (eval `#'(lambda...)); we defined
   this coercion expressly so that programmers wouldn't be forced to use EVAL
   for this, but the coercion is define in terms of what EVAL does (in
   particular, since it happens at runtime, it's evaluated in the null lexical
   environment).

   -- 
   Barry Margolin, Thinking Machines Corp.

   ······@think.com
   {uunet,harvard}!think!barmar
--
------------------------------------------------------------------------------
nom: Harley Davis			ILOG S.A.
net: ·····@ilog.fr			2 Avenue Gallie'ni, BP 85
tel: (33 1) 46 63 66 66			94253 Gentilly Cedex, France
From: Harley Davis
Subject: Re: Help me avoid Eval
Date: 
Message-ID: <DAVIS.91Jul22162336@passy.ilog.fr>
In article <···················@passy.ilog.fr> ·····@passy.ilog.fr (Harley Davis) writes:

   (defmacro make-dog (init-list)
     (let ((fns (getf :barkfns init-list)))
       `(make-instance 'dog
	  :name ,(getf ...)
	  :barkfns (list ,(mapcar #'generate-barkfn fns)))))

A small error.  This shoud read:

   (defmacro make-dog (init-list)
     (let ((fns (getf :barkfns init-list)))
       `(make-instance 'dog
	  :name ,(getf ...)
	  :barkfns (list ,@(mapcar #'generate-barkfn fns)))))
                          ^

Also, this is not the _only_ solution, as I implied in my message.

-- Harley
--
------------------------------------------------------------------------------
nom: Harley Davis			ILOG S.A.
net: ·····@ilog.fr			2 Avenue Gallie'ni, BP 85
tel: (33 1) 46 63 66 66			94253 Gentilly Cedex, France