One step closer to closures, or, how I captured variables by clever use of context switches.

Andre Garzia andre at andregarzia.com
Fri Aug 29 04:55:26 EDT 2008


Folks,

This is the kind of post you get when I keep code until 5:15 AM. Today
I've set a goal to produce a library that would allow me to create
persistent variables, by persistent variables I mean that when the
program finishes it somehow saves the variable contents and when it
loads, it inserts the content back. I know about custom properties,
SQL Databases, stack files, that's how we usually solve this kind of
issue, but I'd decided to try something different and work with actual
variables.

To solve this problem, I've decomposed the task into two big
components, the saver and the loader. The loader is the piece of code
that picks the value and inserts back into the variable, this is
trivial, you just read the value from somewhere and insert it back,
the tricky thing is the saver.

Picking values from global variables is easy, you just declare a
global and grab the value. Script local variables are easy to access
when you're in the same script. Temporary variables, well, those are
tricky to grab when you're not in the same handler as them. So the big
task is, how to we access variables that were defined and used in a
different context? For those new to transcript let me put a simple
example below:

on firstHandler
  local temp
  put "I am a happy variable" into temp
end firstHandler

No code outside "firstHandler" can query the value of the temp
variable, it is defined in the context of that handler. Now, imagine
that you have the following piece:

on firstHandler
  local temp
  put "I am a happy variable" into temp
  newPersistentVariable "temp"
  put "I've just changed the value of temp." into temp
  secondHandler
end firstHandler

on secondHandler
  put captureVariable("temp")
end secondHandler

And when secondHandler runs, it outputs "I've just changed the value
of temp.". If you look carefully at the code above, you'll see that
this is not an easy task. We have a strange handler called
newPersistentVariable which we don't know what it is and on the
secondHandler we have a function that is supposed to return the value
from a variable on the other handler.

Now, even without the details about those two functions, you can see
that we're redefining the content of the temp variable after calling
newPersistentVariable, so in a magic way, captureVariable function is
returning the last value of temp .

How does it works? It works by capturing the context and switching to
it when needed. When newPersistentVariable is called it receives a
param which is the variable name that you want to keep track (not it's
value). newPersistentVariable then checks the executioncontexts and
save it for later reference. When we call captureVariable()  we pass
it a variable name, it then checks to see in which context that
variable was defined, it switch there and with a little hack, picks
the value from there.

The code for both functions is:

local lPersistentVariablesA
global gVar

function captureVariable pVariable
    put lPersistentVariablesA[pVariable] into tContextsRaw
    put 0 into tNum
    repeat for each line tLine in tContextsRaw
        add 1 to tNum
        put return & tNum & comma & tLine after tBuf
    end repeat
    delete char 1 of tBuf
    set the cREVScriptDebugMode of stack "revPreferences" to true
    set the debugcontext to (item 1 of line -2 of tBuf)
    put format("global gVar; put %s into gVar", pVariable) into tCmd
    debugdo tCmd
    set the debugcontext to empty
    set the cREVScriptDebugMode of stack "revPreferences" to false
    return gVar
end captureVariable

on newPersistentVariable pVariable
    put the executioncontexts into lPersistentVariablesA[pVariable]
end newPersistentVariable

Now, why this is useful? Well, as you can see from the code, we have
an array called lPersistentVariablesA, we could simply loop the keys
of that array and capture and save all variables on shutdownRequest.
We could modify the newPersistentVariable to check for the saved
content and insert it back.

Whats wrong with my current code? (A-Ha!) Right now, if you nest to
many handlers, the context switch fails somehow... it's hard to debug
this. I think that as Rev exits the handlers it drops the contexts
(can we call that backtracking?). So if we go from firstHandler into
secondHandler into thirdHandler, then in the thirdHandler we can
capture variables from them all, but if we go from firstHandler into
secondHandler out of secondHandler and into thirdHandler, then
anything from secondHandler is lost.

Anyway, it still a nice hack and it still useful. I've developed this
to use with CGI programming, and in CGIs most things happen in the
startup script or very near it, so I can still use these routines to
pick variables from contexts that I know are still alive.

How to solve this? Well, I don't know, I just wanted to play and try
to build myself something that would allow me to freeze the execution
contexts in time so that I could re-use them later (still trying to
create state out of the stateless nature of cgis, order from
chaos...).


Andre

-- 
http://www.andregarzia.com All We Do Is Code.



More information about the use-livecode mailing list