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]

Re: fluid-let


Russell McManus <mcmanus@IDT.NET> writes:

> My attempt seems to work; give it a whirl and let me know if you
> agree.

Your macro is fine for the usual case of single-entry/single-exit.
But with the full power of continuations, it can actually happen that
the flow of control enters your BODY (gives a nice chill down the
back), leaves it temporarily, and then reenters it.

This would happen when you implement coroutines with continuations,
say.  Each of the coroutines should be able to have their own
fluid-let active and they should not interfere.

Now, with your implementation, the fluid variables are correctly
restored to the values that they have outside of the fluid-let, but
when the flow of control returns, they are again set to the *initial*
values specified in the flui-let form and not to the values they had
inside the fluid-let when the flow of control left.  In addition, the
external values are not saved when we reenter the fluid-let.  Thus,
when we leave it a second time, the fluid variables are *again*
restored to the values they had when entering the fluid-let for the
first time.

In essence you simply want to keep a backup copy of the values of the
fluid variables and swap these backup values with the variables both
on entering and leaving the fluid-let.  That is, when the flow of
control is inside the fluid-let, the backup copy would hold the values
that the fluid variables had outside the fluid-let; and when the flow
of control is outside the fluid-let, they hold the values for inside
until the flow returns.

Like, for a single variable:

    (define-macro (swap x y)
      (let ((t (gensym)))
	`(let ((,t ,x))
	   (set! ,x ,y)
	   (set! ,y ,t))))

    (define-macro (fluid-let bindings-ls . body)
      (let ((id  (caar bindings-ls))
	    (val (cadar bindings-ls))
            (backup (gensym)))
	`(let ((,backup ,val)
	       (body (lambda () ,@body)))
	   (let ((swapper (lambda ()
			    (swap ,id ,backup))))
	     (dynamic-wind swapper
			   body
			   swapper)))))

When extending this to more than one variable, one should be careful
to make fluid-let actually like `let', and not like `letrec', like
your code does (by using `define').

> The question I have is: how would one do this with
> (make-fluid) instead of (gensym) so that this would work with threads?
> Is it really basically just a couple of substitutions?

Rewriting this to use Guile's fluids would give you `with-fluids'.
You still can't access the fluid objects like normal variables
(because they are not normal variables).  In fact, when we have
generalized variables on could simply write

    (fluid-let ((fluid the-current-module-fluid) my-module)
      <do stuff with my-module being current>)

Having to use `(fluid-ref x)' instead of just `x' all over the place
is inconvenient, but solving this is purely a syntax issue in my view.
When we do solve it, by implementing symbol-macros for example, we
should not restrict the solution to fluids.  The same thing (implicit
setters/getters) is needed for variable traces.

[Doesn't syntax-case already have symbol-macros?]