This is the mail archive of the guile@cygnus.com mailing list for the guile project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]

fluid-let that actually works with fluids.



A while back, I was trying to learn defmacros, so I tried to write
a version of 'fluid-let'.  It turned out that I was missing some
important concepts, but finally I produced something that sort of
worked.  The person who helped me on the list challenged me to then
go produce a version of fluid-let that works for guile's fluid variables,
so that it would be useful in a threaded environment.

Finally, I've had some use for such a beast, and since I didn't find
one anywhere in the guile source, I decided to whip up a version.  It
was pretty easy for me to do, but that just probably means that I've
got some obvious bugs in there.  Can those who know macros and fluid
semantics take a look?

Something like this (assuming it works right) should be included in
the guile distribution.

-russ


#!/opt/guile/bin/guile -s
!#

(define-module (gs fluid-let))
;;; end-header

;;; Copyright (C) 1999 Russ McManus
;;; 
;;; This program is free software; you can redistribute it and/or modify
;;; it under the terms of the GNU General Public License as published by
;;; the Free Software Foundation; either version 2 of the License, or
;;; (at your option) any later version.
;;; 
;;; This program is distributed in the hope that it will be useful,
;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;;; GNU General Public License for more details.
;;; 
;;; You should have received a copy of the GNU General Public License
;;; along with this program; if not, write to the Free Software
;;; Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
;;; 
;;; Documentation:
;;;
;;; We require that there is a fluid-variable called
;;; *fluid-let-bindings*, which will be an association list that
;;; associates the name of bound fluid variables with their previous
;;; values.  At the beginning of a 'fluid-let', we push some pairs
;;; onto this alist, and at the end, we pop them off.  Voila!
;;; 
;;; To avoid searching, we assume that items are put onto the bind
;;; stack in order, and that 'fluid-let' will pop them off in order.  We
;;; continue to keep both the names of the vars along with the values,
;;; even though this is not strictly necessary, as a help to debugging
;;; and possibly future reflection.
;;; 
;;; One can only bind fluid variables with this version fluid-let; it
;;; won't work for regular variables.  If you don't care about
;;; threads, you can get a version of fluid-let that works for normal
;;; variables out of slib.

; (define a (let ((val (make-fluid)))
; 	    (fluid-set! val 1)
; 	    val))
; (define b (let ((val (make-fluid)))
; 	    (fluid-set! val 2)
; 	    val))

; (list (fluid-ref a) (fluid-ref b))

; (fluid-let ((a 2)
; 	    (b 3))
;   (list (fluid-ref a) (fluid-ref b)))

; (list (fluid-ref a) (fluid-ref b))

(define *fluid-let-bindings*
  (let ((val (make-fluid)))
    (fluid-set! val '())
    val))

(defmacro fluid-let (clauses . body)
  (let ((%make-binding (gensym))
	(%binding-value (gensym))
	(%add-binding (gensym))
	(%pop-binding (gensym)))
    `(let ((,%make-binding (lambda (key value) (cons key value)))
	   (,%binding-value (lambda (bind) (cdr bind)))
	   (,%add-binding
	    (lambda (bind)
	      (fluid-set! *fluid-let-bindings* (cons bind (fluid-ref *fluid-let-bindings*)))))
	   (,%pop-binding
	    (lambda ()
	      (let ((bind (car (fluid-ref *fluid-let-bindings*))))
		(fluid-set! *fluid-let-bindings* (cdr (fluid-ref *fluid-let-bindings*)))
		bind))))
       (dynamic-wind
	   (lambda ()
	     ,@(map (lambda (clause)
		      `(begin
			 (,%add-binding (,%make-binding ',(car clause) (fluid-ref ,(car clause))))
			 (fluid-set! ,(car clause) ,(cadr clause))))
		    clauses))
	   (lambda ()
	     ,@body)
	   (lambda ()
	     ,@(map (lambda (clause)
		      `(fluid-set! ,(car clause) (,%binding-value (,%pop-binding))))
		    (reverse clauses)))))))

(export *fluid-let-bindings*)
(export fluid-let)


--
They don't make nostalgia like they used to.