[comp.lang.lisp] scope/extent interaction with flet and load

sboisen@bbn.com (Sean Boisen) (10/26/90)

Scenario: assume a file foo.lisp which contains the following single form.

	(print (+ 1 2))

What i expected to see when executing the following

> (flet ((+ (a b) (cons a b))) (load "foo"))

was something like
(1 . 2)

Instead (at least under Lucid and Allegro on a Sun) i see 

3

In other words, my flet "redefinition" of + wasn't in effect when the
file got loaded, even though it seems to me the loading is inside the
scope of the flet, according to the rules in CLtL. Can somebody
explain to me why this behavior is correct (assuming it is)? 


(This is of course a silly example, for illustrative purposes: what i
*really* want to work like this is a macrolet, because i have a big
file containing macro forms, and i want to load this file with a
slightly altered macro definition, without trashing the existing macro
definition. That seems to me like just what macrolet was designed for,
but i'm frustrated that it doesn't work: i get the existing macro
definition, not the new one i've created with macrolet. Then i
discovered that flet has the same property, which makes it look like
that's the way things are supposed to work. Extra credit if you can
tell me how to accomplish my original intent!)
--


Sean

engelson-sean@cs.yale.edu (Wisp Lizard) (10/27/90)

In article <60375@bbn.BBN.COM>, sboisen@bbn.com (Sean Boisen) writes:
|> 
|> Scenario: assume a file foo.lisp which contains the following single form.
|> 
|> 	(print (+ 1 2))
|> 
|> What i expected to see when executing the following
|> 
|> > (flet ((+ (a b) (cons a b))) (load "foo"))
|> 
|> was something like
|> (1 . 2)
|> 
|> Instead (at least under Lucid and Allegro on a Sun) i see 
|> 
|> 3
|> 
|> In other words, my flet "redefinition" of + wasn't in effect when the
|> file got loaded, even though it seems to me the loading is inside the
|> scope of the flet, according to the rules in CLtL. Can somebody
|> explain to me why this behavior is correct (assuming it is)? 

This behavior is correct because of Common Lisp's lexical scoping; the
contents of the file are outside the lexical scope of the flet.  To do
what you want with functions, you need to change the global function
definition, as follows:

(let ((old+ #'+)) 
  (setf (symbol-function '+) #'cons)
  (unwind-protect
      (load "fu")  ; note the correct spelling :-)
    (setf (symbol-function '+) old+))

Note the use of unwind-protect in case an error occurs while loading.  I
don't know how to do this with macros, however.

|> Sean

Coincidence?

----------------------------------------------------------------------
Sean Philip Engelson, Poet Errant	Make your learning a fixture;
Yale Department of Computer Science	Say little and do much;
Box 2158 Yale Station			And receive everyone with 
New Haven, CT 06520			   a friendly attitude.
----------------------------------------------------------------------
For a successful technology, reality must take precedence over public
relations, for Nature cannot be fooled.
			--Richard Feynman

moore%cdr.utah.edu@cs.utah.edu (Tim Moore) (10/27/90)

In article <1990Oct26.133156@cs.yale.edu> engelson-sean@cs.yale.edu (Wisp Lizard) writes:
>In article <60375@bbn.BBN.COM>, sboisen@bbn.com (Sean Boisen) writes:
>|> 
>|>[rebinding macros and functions doesn't work as expected when loading]
>
>This behavior is correct because of Common Lisp's lexical scoping; the
>contents of the file are outside the lexical scope of the flet.  To do
>what you want with functions, you need to change the global function
>definition, as follows:
>
>(let ((old+ #'+)) 
>  (setf (symbol-function '+) #'cons)
>  (unwind-protect
>      (load "fu")  ; note the correct spelling :-)
>    (setf (symbol-function '+) old+))
>
>Note the use of unwind-protect in case an error occurs while loading.  I
>don't know how to do this with macros, however.

I'm sure the function names are for illustrative purposes, but
remember that redefining CL functions like #'+ is a really bad idea.

You can do the same trick with macros:

(let ((old-macro-def (macro-function 'foo)))
  (setf (macro-function 'foo) (macro-function 'bar))
  (unwind-protect
       (load "foo")
    (setf (macro-function 'foo) old-macro-def)))

With the new macro functions in CLtL2 it's not hard to write a macro
that dynamically binds macro definitions. Of course, this only will
only work for forms that are being explicitly evaluated (either by
eval or load).

>|> Sean
Tim Moore                    moore@cs.utah.edu {bellcore,hplabs}!utah-cs!moore
"Ah, youth. Ah, statute of limitations."
		-John Waters

kmorgan@fergie.ge.com (Keith Morgan) (10/27/90)

In article <1990Oct26.133156@cs.yale.edu> engelson-sean@cs.yale.edu (Wisp Lizard) writes:

> To do 
>what you want with functions, you need to change the global function 
>definition, as follows: 
> >(let ((old+ #'+)) 
>   (setf (symbol-function '+) #'cons)
>   (unwind-protect 
>     (load "fu") ; note the correct spelling :-) 
>    (setf (symbol-function '+) old+)) 
>


Unfortunately this solution will not work in a multiprocess
environment since the global function cell is being set, not bound,
and all processes will end up using the new definition . What is
really needed is a way to declare an flet binding special but I don't
think there is a way to do that.
Keith Morgan  
Domain: kmorgan@atl.ge.com 
Path: !mcnc!ge-rtp!atl.ge.com!kmorgan

eliot@phoenix.Princeton.EDU (Eliot Handelman) (10/28/90)

In article <332@puma.ge.com> kmorgan@fergie.UUCP (Keith Morgan) writes:
;In article <1990Oct26.133156@cs.yale.edu> engelson-sean@cs.yale.edu (Wisp Lizard) writes:
;
;> To do 
;>what you want with functions, you need to change the global function 
;>definition, as follows: 
;> >(let ((old+ #'+)) 
;>   (setf (symbol-function '+) #'cons)
;>   (unwind-protect 
;>     (load "fu") ; note the correct spelling :-) 
;>    (setf (symbol-function '+) old+)) 
;>
;
;
;Unfortunately this solution will not work in a multiprocess
;environment since the global function cell is being set, not bound,
;and all processes will end up using the new definition . What is
;really needed is a way to declare an flet binding special but I don't
;think there is a way to do that.

Here is one way.

;; load but don't eval.

(defun fake-load (file)
  (let ((forms '()))
    (with-open-file (o file :direction :input)
      (loop
       (let ((form (read o nil nil)))
	 (if form
	     (push form forms)
	     (return (nreverse forms))))))))

;; Make sure forms are evaluated in lexical environment of flets.

(defmacro load-in-environment (file &rest flets)
  (let ((forms (fake-load file)))
    `(flet ,flets
      (progn ,@forms))))

;; use it like this:

(load-in-environment "foo" ;; contains form (print (+ 1 2))
   (+ (a b) (cons a b))
   (baz () nil)))

;; return (1 . 2)

Propbably one could rewrite this so that it would work compiled,
if one really cares. No doubt this will be much slower than LOAD,
for reasons that were previously discussed here, and which I forget.

--eliot

cutting@parc.xerox.com (Doug Cutting) (10/30/90)

In article <332@puma.ge.com> kmorgan@fergie.ge.com (Keith Morgan) writes:
   In article <1990Oct26.133156@cs.yale.edu> engelson-sean@cs.yale.edu (Wisp Lizard) writes:
   >what you want with functions, you need to change the global function 
   >definition, as follows: 
   > >(let ((old+ #'+)) 
   >   (setf (symbol-function '+) #'cons)
   >   (unwind-protect 
   >     (load "fu") ; note the correct spelling :-) 
   >    (setf (symbol-function '+) old+)) 

   Unfortunately this solution will not work in a multiprocess
   environment since the global function cell is being set, not bound,
   and all processes will end up using the new definition . What is
   really needed is a way to declare an flet binding special but I don't
   think there is a way to do that.

How about:

 (defvar *plus* #'+)
 (defun + (&rest numbers) (apply *plus* numbers))
 (let ((*plus* #'list))
   (load "fu"))

	Doug

sra@ecs.soton.ac.uk (Stephen Adams) (10/30/90)

In article <1990Oct26.122108.27095@hellgate.utah.edu> moore%cdr.utah.edu@cs.utah.edu (Tim Moore) writes:

 > In article <1990Oct26.133156@cs.yale.edu> engelson-sean@cs.yale.edu (Wisp Lizard) writes:
 > >In article <60375@bbn.BBN.COM>, sboisen@bbn.com (Sean Boisen) writes:
 > >
 > >|>[rebinding macros and functions doesn't work as expected when loading]

A solution to this is to use the `evaluate form' read macro.
Instead of

	(flet ((+ (a b) (cons a b))) (load "foo"))

Try this:

	(flet ((+ (a b) (cons a b)))
	   #.(with-open-file (f "foo.lisp") (read f)))

The #. macro evaluates the form at the	time that the *flet*
form is read in and splices in a copy of the file.
Of course, this only works with source files containing one
form (it could be a progn).
--
Stephen Adams                        S.R.Adams@ecs.soton.ac.uk
Computer Science                     S.R.Adams@sot-ecs.uucp
Southampton University
Southampton SO9 5NH, UK

gumby@Cygnus.COM (David Vinayak Wallace) (11/04/90)

   Date: 26 Oct 90 17:31:56 GMT
   From: engelson-sean@cs.yale.edu (Wisp Lizard)

   (let ((old+ #'+)) 
     (setf (symbol-function '+) #'cons)
     (unwind-protect
	 (load "fu")  ; note the correct spelling :-)
       (setf (symbol-function '+) old+))

   Note the use of unwind-protect in case an error occurs while loading.

Yuk.  No need for unwind-protect (and this version should work for
multiprocessing lisps if their letf is implemented correctly):

(letf (((macro-function 'foo) #'cons)) (load "foo"))

I believe letf was added to the revised common lisp.

moore%cdr.utah.edu@cs.utah.edu (Tim Moore) (11/05/90)

In article <GUMBY.90Nov3185409@Cygnus.COM> gumby@Cygnus.COM (David Vinayak Wallace) writes:
>
>   Date: 26 Oct 90 17:31:56 GMT
>   From: engelson-sean@cs.yale.edu (Wisp Lizard)
>
>   (let ((old+ #'+)) 
>     (setf (symbol-function '+) #'cons)
>     (unwind-protect
>	 (load "fu")  ; note the correct spelling :-)
>       (setf (symbol-function '+) old+))
>
>   Note the use of unwind-protect in case an error occurs while loading.
>
>Yuk.  No need for unwind-protect (and this version should work for
>multiprocessing lisps if their letf is implemented correctly):
>
>(letf (((macro-function 'foo) #'cons)) (load "foo"))
>
>I believe letf was added to the revised common lisp.

I'm not sure what Gumby means by "revised common lisp", but letf is
not described in CLtL2 or in any ANSI draft I've seen. It may be
"standard" in some implementations (Genera?).

Tim Moore                    moore@cs.utah.edu {bellcore,hplabs}!utah-cs!moore
"Ah, youth. Ah, statute of limitations."
		-John Waters

harrisr@turing.cs.rpi.edu (Richard Harris) (11/06/90)

Here is a definition for letf.

(export '(LETF LETF*))

;The function LETF (defined below) is really a cross between the (Symbolics) functions
;LETF and LET-GLOBALLY, and should really be called LETF-GLOBALLY.

;"letf plaves-and-values body...              Special form
;      Just like let, except that it can bind any storage cells rather than just variables."
;"let-globally ((var value)...) body...       Special form
;      Similar in form to let.  The difference is that let-globally does not bind the
;      variables; instead, it saves the old values and sets the variables, and sets up
;      an unwind-protect to set them back."
;      The difference between let and let-globally is important (only)
;      in a multiple-process Lisp system.

(defmacro letf (bindings &body forms)
  (let ((tvars nil)
	(tvals nil)
	(store-vars nil)
	(store-forms nil)
	(access-forms nil)
	(value-forms nil)
	(save-vars nil))
    (dolist (binding bindings)
      (let ((setf-form (if (atom binding) binding (car binding)))
	    (value-form (if (atom binding) nil (cadr binding))))
	(multiple-value-bind (vars vals stores store-form access-form)
	    (get-setf-method setf-form)
	  (setq tvars (nconc tvars vars))
	  (setq tvals (nconc tvals vals))
	  (setq store-vars (nconc store-vars stores))
	  (setq store-forms (nconc store-forms (list store-form)))
	  (setq access-forms (nconc access-forms (list access-form)))
	  (setq value-forms (nconc value-forms (list value-form)))
	  (setq save-vars (nconc save-vars (list (gensym)))))))
    `(let ,(mapcar #'list tvars tvals)
       (let ,(mapcar #'list save-vars access-forms)
	 (unwind-protect
	      (progn
		(let ,(mapcar #'list store-vars value-forms)
		  ,@store-forms)
		,@forms)
	   (let ,(mapcar #'list store-vars save-vars)
	     ,@store-forms))))))

(defmacro letf* (bindings &body forms)
  (if (null (cdr bindings))
      `(letf ,bindings
	  ,@forms)
      `(letf (,(car bindings))
	  (letf* ,(cdr bindings)
	     ,@forms))))