8
$\begingroup$

As mentioned by Albert Retey in a comment, we can unintentionally interfere with our dynamics as follows

x = 2;
Dynamic@Framed@
  Panel[x, "The value of Global`x is dynamically displayed below"]
Block[{x}, x = 1; Pause[1]; x]
(*The value of Global`x is dynamically displayed below*)
1
(*regular output*)
1

We can also interfere the other way around, which is perhaps even more troublesome, as follows

Dynamic[y = 1; 
  Framed@Panel[y, "This cell changes Global`y and displays it"], 
  UpdateInterval -> 0.1]
Block[{y}, y = 5; Pause[1]; y]
(*This cell changes Global`y and displays it*)
1
(*regular output, could be expected to be 5*)
1

A special case of the problems is highlighted here, also by Albert Retey.

I had thought the advice about not using Block, e.g. here, referred to other problems that I figured I knew how to handle. It seems this particular behavior did not receive much attention.

Surrounding Block with PreemptProtect seems to mitigate these problems.

Question: Does consistently surrounding Block with PreemptProtect provide complete protection against this kind of interference?

$\endgroup$
2
  • 1
    $\begingroup$ Yes, I think so. For both examples, the explanation is that the update procedure for the Dynamic is started while the kernel is executing the Pause command, so it interrupts its task when the Pause is over and then returns the current value, i.e. the local value, to the frontend. Wrapping Block in PreemptProtect makes the kernel returning the current value of x when the evaluation of the Block is completed, so now it will return the value outside the Block. $\endgroup$ May 9, 2015 at 18:17
  • $\begingroup$ @FredSimons yes, I agree with you, this is probably safe. But I figured I had better ask, before I discover I have been arrogant in ignoring advice again. Another method to protect yourself against this is to use contexts. It is good symbol management to put local variables in a in a separate context anyway. $\endgroup$ May 9, 2015 at 18:39

1 Answer 1

10
$\begingroup$

The two examples you mention are easily understood from the way Dynamic updating works. Let us concentrate on Dynamic[x]. It is sent by the frontend to the kernel and the kernel returns it unevaluated to the frontend, together with the boxes in which the frontend will display. Then the front starts an update procedure over the preemptive link, asking the kernel for the value of x. So the kernel interrupts what it was doing, evaluates it and returns the value to the frontend.

The two examples strongly rest on the use of Block instead of Module; the variable inside Block is the same as inside Dynamic, so it is not surprising that interference may occur.

Example 1:

x = 2; Dynamic[x]
Block[{x = 1}, Pause[1]; x]

Here the update procedure starts when the kernel is evaluating the Block command. So the kernel completes the Pause and then returns the value 1 to the frontend. When the Block was wrapped in PreemptProtect, the kernel would first have competed the evaluation of the Block command and would then return the value of x, now again 2.

Example 2:

Dynamic[x = 1, UpdateInterval -> 0.1]
Block[{x = 5}, Pause[1]; x]

The frontend sends the expression x=1 for updating to the kernel when the kernel is evaluating the Pause statement of the Block command. So when the Pause is over, the kernel sets x to 1 and sends that value to the frontend. Then it resumes the evaluation of Block, with the value 1 instead of 5.

When the Block was wrapped in PreemptProtect, this would not have happened.

I would like to add another similar example, with the scoping construct Table instead of Block.

x = 10; Dynamic[x]
Table[Pause[1]; x, {x, 1, 5}]

In the Table command, each second the value of x changes, so each second the kernel informs the frontend that it must start an update procedure and that is shown in the Dynamic display. But when the evaluation of the Table command is finished, no assignment is done to x, so the frontend is not informed that it has to start another update procedure, so the Dynamic will display 5 instead of 10. Of course, when we wrap Table in PreemptProtect, the updating takes place when x again has the original value 10, so it then looks fine. Nevertheless, even now there can be some interference. First evaluate this command:

x = 10; Dynamic[{x, RandomInteger[{1, 100}]}]

And then:

Table[Pause[1]; x, {x, 1, 5}] // PreemptProtect

During the execution of the above table, the kernel changes the value of x so it sends an update mesage to the frontend. The resulting update procedure by the frontend is now done when the construction of the Table is finished, so x will still be displayed as 10. But the random integer will be recomputed as well, so almost surely the display of Dynamic will change.

Edit: SUMMARY

The answer to the question is negative. Even when we wrap Block in PreemptProtect, there might be side effects. This is not restricted to Block; it applies to any scoping construct with dynamic scoping.

Suppose that we have a displayed Dynamic[ expression(x) ], and that we have a scoping construct with a local variable x that uses dynamic scoping. Then the name x in Dynamic is exactly the same as the name x in the scoping construct. Whether or not the scoping construct is wrapped in PreemptProtect, changing the value of x in the scoping construct forces (by means of a kernel message) the frontend to start a, maybe unwanted, update procedure for Dynamic[ expression(x) ], and this updating procedure may give a changed display of Dynamic[ expression(x) ], as in the above example. The only effect of PreemptProtect is the time when the kernel executes the update call. Without PreemptProtect very likely during the evaluation of the scoping construct, otherwise when the evaluation of the scoping construct is finished.

On the other hand, when we wrap the scoping construct in PreemptProtect, the evaluation of the scoping construct cannot be influenced by preemptive calls, and therefore it will always return the expected result.

$\endgroup$
7
  • 1
    $\begingroup$ Thank you for your elaborate answer. I think the example with Table is really good, because it shows that we still may have to be careful even when we wrap all Blocks in PreemptProtect. Again we can interfere both ways, so that the outcome of Table can be unexpected. I expect the same is true for Do. Perhaps you could edit your answer slightly to start with something like "No, the are other scoping constructs, for example Table that can also be interfered with in this way. You would have to wrap these in PreemptProtect as well." $\endgroup$ May 10, 2015 at 10:15
  • 1
    $\begingroup$ I think this would be a good summary and a direct answer to the question. I will probably accept this answer either way. $\endgroup$ May 10, 2015 at 10:15
  • 1
    $\begingroup$ I agree, again, that that is probably safe. I suppose the question is a bit vague. I also consider the interference with Table to be contained in "this kind of interference", but that was not very clear. I think your comment is very on topic so no need to delete it. $\endgroup$ May 10, 2015 at 11:47
  • $\begingroup$ Also by "marking a message as temporary" I simply mean putting the text "(temporary message)" at the end of the message. The website does not have any support for temporary messages or anything like that. It is just a reminder for myself and for others that I mean to delete the message :). $\endgroup$ May 10, 2015 at 11:49
  • 1
    $\begingroup$ In my opinion, these pathological, but highly instructive examples have more to do with the limitations of dynamic scoping than with interactive functions such as Dynamic. If I am not mislead by my memory, in Mathematica 2 the scoping construct Module was introduced to overcome unwanted behaviour of the scoping construct Block of Mathematica 1. For example, the following function f[x_] := Block[{i = 2}, x^i] squares its argument. But when we happen to use the name of the local variable, we get an unwanted answer: f[i] returns 4. With Module, we indeed find i^2. $\endgroup$ May 10, 2015 at 12:01

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.