I ran across a scoping puzzle while experimenting with ScheduledTasks, and I'd be grateful for an explanation from the sages here. I found a workaround by intuitive horse sense, but was unable to explain to myself adequately what was wrong with my original attempt.
First, a straightforward, tail-recursive, synchronous Module
Module[{state = 0, doNextIteration},
doNextIteration = Function[
state = state + 1;
If[state < 4,
(Print[state]; doNextIteration[])]];
doNextIteration[]]
(* 1 2 3 *)
Next, my first attempt to do the same with asynchronous tasks, which I need to tail-chain as one-shot ScheduledTasks. If CreateScheduledTask has a second List argument, then it creates a one-shot task delayed by the time in seconds in the list, just what I need for my bigger application. In this simplified sample, the ScheduledTask tail-chains a new one-shot ScheduledTask by a tail-recursive call -- just like the successful synchronous code -- once every tenth of a second until the condition on state produces False (ignore cleanup of the task objects for simplicity, here).
Module[{state = 0, runNextTask},
runNextTask = Function[
StartScheduledTask@
CreateScheduledTask[
state = state + 1;
If[state < 4,
(Print[state]; runNextTask[])],
{0.10}]];
runNextTask[]]
OK, the problem is this only Prints once, not the desired three times. I had a sickening hunch that the problem has something to do with runNextTask being local to the Module and somehow not being able to refer to itself recursively -- even though there is only one Module -- in the bizzaro-land of asynchrony, so I "fixed" the code as follows:
Module[{state = 0, runNextTask = Unique[]},
runNextTask[] :=
If[state < 3,
StartScheduledTask@
CreateScheduledTask[
Module[{},
state = state + 1;
Print[state];
runNextTask[]],
{0.10}]];
runNextTask[]]
I made runNextTask refer to a Unique global symbol, and made my function into a rewrite rule attached to runNextTask[]. This works great, but I don't understand well why it does. I hate being as dumb as a horse, even if I can jump the fences.
Clues, advice, explanations: all appreciated.
Module[{state = 0, runNextTask}, runNextTask = Function[StartScheduledTask@CreateScheduledTask[state = state + 1; If[state < 4, (Print[state]; runNextTask[])], {0.10}]]; runNextTask[]; Hold@runNextTask]– Albert Retey Apr 02 '12 at 09:32UniquethanModuleto create the symbol: Not only because no additional "tricks" are needed to make things work. It also seems easier to guess what the code actually is supposed to do for a reader. As far as I can see the OP is using the version I'd recommend anyway... – Albert Retey Apr 02 '12 at 14:12