Friday, February 15, 2008

Faux dynamic binding for Arc

The objective in what follows is to simulate one of my favorite features of CL, which is dynamic binding of special variables. If you ever programmed QuickDraw for the Mac and tediously had to code up GetPort, SetPort, and then a SetPort back when finished drawing, just imagine having the ability simply to code:

(let grafport* (grafPort my-window)

...and get the whole save, re-bind, and restore taken care of for you. Does not come up a lot, but when it does this rocks (and Cells uses it for its core behavior of tracking dependencies). You see this sort of thing especially in cases exactly like the drawing GrafPort, something so fundamental that the lowest level functions will need it so it is either a global of some kind or every function in the API needs it as a parameter.

In the case of Cells, without dynamic binding almost every function in the application would need it. Not a pretty thought.

Caveat emptor: The following has been lightly tested with only one variable pseudo-dynamically bound. Bug reports welcome, but if the report is "Listen, dummy, Arc already has that."...well, I'll just be a normallispweeny for a week:

(mac withs* (parms . body)
(let uparms (map1 [cons (uniq) _] (pair parms))
`(do ,@(map1 (fn ((save curr val))
`(= ,save ,curr ,curr ,val)) uparms)
(do ,@body)
,@(map1 (fn ((save curr val))
`(= ,curr ,save)) uparms)))))

(= stak* (list 'top))

(def pstack (k)
(prs k stak*)(prn))

(pstack 'start)
(with* (stak* (cons 'me stak*))
(prt 'with-me stak*)
(pstack 'confirm-with-me))
(pstack 'bye-bye))

Output should be:

start (top)
with-me (me top)
confirm-with-me (me top)
bye-bye (top)

Enjoy. (And with* left as an exercise. :)


Anonymous said...

(= stack* (list 'top))

Hy, it seems you loose the ' in
....(list 'top)

Thank's for all

Kenny Tilton said...

Right you are. Corrected. Thx.

Anonymous said...

And maybe -> (prt 'with-me stack*)
is -> (prn 'with-me stack*)

Kenny Tilton said...

And maybe -> (prt 'with-me stack*)
is -> (prn 'with-me stack*)

But without a separator:

(prn 1 2 3)
-> 123

(prt 1 2 3)
-> 1 2 3

offby1 said...

arc already has "do1", which I bet does what your "prog1" does.

Anonymous said...

An advantage of using MzScheme's parameter facility is that it would work with threads, a disadvantage is that to get the current value of a parameter you need to call it: (stack*)

Kenny Tilton said...

Thx to the folks pointing out DO1, I meant and mean to ask for anything else along these lines -- I am not making an exhaustive study of arc1, strange as that may sound. Not sure what catdancer was saying, but it sounds like I stumbled onto a reserved name if you will in stack*. Changed.

Anonymous said...

Not sure what catdancer was saying

Sorry, I'll try to unpack a little.

In the web server that comes with Arc, each request that comes in is run in its own thread. So this is an example where most of the time you want code running in different threads to work independently of each other... unless there is something specific that you want to share between requests (and then you'd usually have some kind of thread safe data structure).

So, for example, suppose I wanted to use stak* in a web application, so I use with* to give it my desired value while my code is running. Then some other request comes in and runs at the same time, and if it also sets stak*, then I'll suddenly (and to me, randomly!) see the value that the other thread set it to.

If this is an issue for you (if you want to be able to use dynamic variables in a multi-threaded application such as a web server), a very easy thing to do would be to use MzScheme's parameter facility:

Which automatically takes care for you all the difficult details of making the value local to each thread and restoring the previous value of your dynamic value if your code throws an error instead of returning normally.

However a disadvantage of MzScheme's parameter facility is that to get the value of a parameter, you have to call it to get its value, instead of just using it like a variable. So to get the current value of your dynamic variable stak*, your code would have to say (stak*) to get the value, instead of simply saying stak*.

Anonymous said...

Hmm, let me see if I can convince blogger to turn my URL for the MzScheme manual into a link...

MzScheme parameter documentation

Anonymous said...

That's better. :-)

Kenny Tilton said...

use MzScheme's parameter facility

Oh, gotcha. Thanks, that is great info. I hope Arc grows a dynamic binding mechanism off that. Maybe I'll mention in on the Arc forum. Thx again.