[comp.lang.lisp] retarded evaluation question

rit@killdeer.stanford.edu (Jean-Francois Rit) (03/17/90)

What is an elegant solution to implement the following function:

(defxxx with-bindings (b-list s-exprs)
 "evaluates the s-exprs in an implicit progn. If the argument s-exprs contains
 free variables, they are bound according to b-list. 
 Example: (setq sub '((?x (1 2)) (?y (3 5)))
                clause '(+ (car ?x) (cadr ?y))) 
          --> (+ (car ?x) (cadr ?y))
          (with-bindings sub clause)
          --> 6")

Note that a macro expanding to a let won't work, since sub, for instance, is a
symbol, not a cons.

JF Rit

barmar@think.com (Barry Margolin) (03/17/90)

In article <1990Mar16.170540.20445@Neon.Stanford.EDU> rit@killdeer.stanford.edu (Jean-Francois Rit) writes:
>What is an elegant solution to implement the following function:
>
>(defxxx with-bindings (b-list s-exprs)
> "evaluates the s-exprs in an implicit progn. If the argument s-exprs contains
> free variables, they are bound according to b-list. 

If you're programming in Common Lisp you want to use PROGV, which allows
you to bind dynamic variables specified at runtime.  Here's an
implementation that has precisely the semantics you described.

(defun with-bindings (b-list s-expr) ; from your example, it appears that
				     ; s-expr should be singular
  (progv (mapcar #'first b-list) (mapcar #'second b-list)
    (eval s-expr)))

Actually, I'd suggest implementing this as a macro to avoid the EVAL (which
would allow proper lexical scoping):

(defmacro with-bindings ((b-list) &body body)
  "Execute BODY with the special bindings specified in B-LIST in effect.
   B-LIST is evaluated, and should return an association list of the form
   ((var1 val1) (var2 val2) ...).  Returns the value of the last form in BODY."
  (let ((b-var (gensym)))
    `(let ((,b-var ,b-list))		; why doesn't CL have ONCE-ONLY?
       (progv (mapcar #'first ,b-var) (mapcar #'second ,b-var)
	 .,body))))

(setq sub '((?x (1 2)) (?y (3 5)))
      clause '(+ (car ?x) (cadr ?y)))

(with-bindings (sub)
  (eval clause))
=> 6

In your particular example the EVAL is necessary because the clause is also
computed at runtime (I presume you're implementing an interpreter of some
kind, by translating into Lisp), but I think it's better not to hide it
away in the implementation of WITH-BINDINGS, as it would restrict the ways
in which WITH-BINDINGS could be used.  I think it's better to have the EVAL
out in the open.
--
Barry Margolin, Thinking Machines Corp.

barmar@think.com
{uunet,harvard}!think!barmar