MathGroup Archive 2008

[Date Index] [Thread Index] [Author Index]

Search the Archive

Re: Localizing Large Numbers of Variables in a DynamicModule:

  • To: mathgroup at smc.vnet.net
  • Subject: [mg85911] Re: Localizing Large Numbers of Variables in a DynamicModule:
  • From: Ron Monson <ron.monson at yahoo.com>
  • Date: Wed, 27 Feb 2008 04:31:21 -0500 (EST)

<<
 With[{args =  {x1,x2,x3,x4,x5}},
 DynamicModule[args,
foo@@args;
goo@@args;
 ...
 ]
 ]
<

Yes this allows the arguments to be referred to within the body of the "With" but the question really came in two parts, the first relating to the ability to externally group these variables  and the second, to make these variables available to all functions during the evaluation of the DynamicModule. It is the second part that seems to be the challenge.

As the following examples show, in some specialized cases these local variables can be made without explicit passing but, in general, DynamicModule's scoping is not sufficiently "Block-like" for this to occur. In particular, the scoping seems to be a "Module-Block hybrid" that depends on whether or not a Dynamic subsequently wraps around a declared loca variable somewhere in the DynamicModule's initial evaluation.

The rest of this post that illustrates the comments above and has some observations about DynamicModule's scoping and updating behaviour particularly in relation to segregated code. From this the basic conclusions I've drawn are as follows:

* DynamicModule local variables can sometimes  (but unreliably) be Block-like in relation to their local variables in the initial execution, Module-like in terms of generating new variables and DynamicModule-like in their subsequent behaviour in the Front-end.
* Mixing Block & DynamicModule can lead to unexpected behaviour especially with any variable shadowing.
* Despite the resulting code-bloat and inconvenience, passing all dynamic variables to nested functions appears safer mainly due to more predictable scoping and predictable updating.
* Even with passing all DynamicModule variables there are some subtleties that distinguish it from "With", "Module" and "Block"
 .* Much flexibility would be gained if we had "Meta Scoping" - the ability to functionally add scoped variables to DynamicModule code blocks. This would not only allow eliminate the aforementioned code-bloat but assist with engineering and controlling Dynamic updating,  maintain Syntax Coloring and finally also allow more flexible and interactive "Dynamic Code" generation.

Just to recap the initial question and its motivation.

The DynamicModule construct is very useful for creating Interfaces whose dynamic variables are made spacially local. Most of the examples of DynamicalModule I've observed however, seem to include the body of the code (lexically) within the DynamicalModule definition (i.e. directly between the "[" and "]"). This simplifies initialization, scoping and intended Dynamic Updating. In designing more complex interfaces however, this lexical placement quickly becomes unwieldy and one way of managing (and parallelizing) this complexity is to instead place function calls within the body of the DynamicModule. These functions could, for example, be thought of as corresponding to different components in an overarching interface. This process however, significantly affects the initialization, scoping and intended updating of these DynamicModule variables and one of the points of the following is to see what these effects are and how they can be managed. Hence in some of these "toy
 examples" the demonstrated idioms may appear unnecessary or unmotivated but it needs to remembered that the point here is to consider the usefulness of the idioms for large numbers of variables and functions and where any global variables can be considered to be in the "Private`" namespace of an associated package.

Firstly here is the idiom I'm after

VARS = {x1, x2, x3, x4};
f[] := "Interface Component A that accesses some or all of VARS";
g[] := "An Interface Component B that accesses some or all of VARS";
DynamicModule[
 VARS,
 OverArchingInterface[
  f[],
  g[]
  ]
 ]

(* NOT VALID *) 

So essentially I want Block's  "temporary globalization" to apply to DynamicModule's local variables so that other evaluated function (f and g ) have access to these variables even if they are not explicitly passed. 

The "With idiom" of insertion without evaluation as initially suggested in the reply certainly allows one to define all the arguments in the one place but, in general (as will be shown) this does not produce Block-like scoping. One might wonder why one would want to this Block-like behaviour since all the arguments, can be passed at once (and in some situations is good programming practice -especially when not done in a package namespace while the extra overhead of passing a variable space is neglible). For example, 

ARGS = {x1, x2};

f1[args_] := 
  With[{x1 = args[[1]], x2 = args[[2]]}, {Slider2D[Dynamic[{x1, x2}]], {Dynamic@x1, Dynamic@x2}}];

With[{args = ARGS},
  DynamicModule[args, f1[args]]]

The point though here is when VARS contains a large number of variables that are required by many functions often deeply placed. In these cases the variables need to eventually extracted from within the nested function and hence in a sense the unwieldeness has just been deferred.
 
At first, the Block-like behaviour appears to already be present.

ARGS = {x1, x2};

f2[] := {Slider2D[Dynamic[{x1, x2}]], {Dynamic@x1, Dynamic@x2}};

With[{args = ARGS},
  DynamicModule[args, f2[]]]

That is, both x1 and x2 were not passed explicitly but remain completely local to the DynamicModule. That is, if you copy and paste this output the 2D slider are not linked via global variables x1, x2. This however does not work in general. Since this is a scoping issue of DynamicModule it is easier to observe without the "With" clutter (which also adds complications in any initializations). Take the following:

Clear@x1;

g1[] := x1;
f3[] := Dynamic@x1;

DynamicModule[{x1}, {g1[], f3[]}]


---> {x1$$, FE`x1$$14}

In the evaluation of DynamicModule's body the locally declared variable is cast as local if it is wrapped in a Dynamic. Otherwise it is is cast as a global variable. Initiallizing with some values can make this clearer.

Clear@x1;

g1[] := x1;
x1 = 1;
f4[] := Dynamic@x1;

DynamicModule[{x1 = 0}, {g1[], f4[]}]

--> {1,0}

That is, within g1 the local variable is cast as global whereas within f4 the Dynamic wrapper ensures that x1 is cast to the local DynamicModule variable. Hence it order to ensure that a variable is cast as the local DynamicModule variable it must be wrapped in a Dynamic wrapper and evaluated with the initial DynamicModule evaluation. Take the following

f5[] := {Slider[Dynamic@x], {Dynamic@x}};

DynamicModule[{x}, {f5[], Dynamic[f5[]]}]

The output shows two sliders and with their respective linked variable. The first behaves as desired with x being cast to the locally declared DynamicalModule variable. The second slider however, while initially showing the same functionality,  is actually using a global value of x without any casting taking place. This can be confirmed by copying and pasting the output to another part of the notebook and observing that the second sliders are coupled. This is because casting doesn't take place since in the Dynamic[f5[]] because the f5[] expression is not immediately evaluated due to Dynamic's HoldAll Attribute. In this case, it can be avoided by pushing the First Dynamic "deeper" in order to ensure that the local variables are seen in the initial valuation thus enabling casting.

f6[] := Dynamic[{Slider[Dynamic@x], {Dynamic@x}}];
DynamicModule[{x}, f6[]]

A more fundamental problem however, is that since the scope of a local variable depending on its initial evaluation this is not something a programmer will either want or sometimes be able to trace. In particular, it can depend on the logical control which can lead to unexpected results. For example in the following the intention is for z to be cast as a local DynamicModule variable.

g2[] := {SetterBar[Dynamic@z, {"z1", "z2", "z3"}], Dynamic@z};

f7[] := Dynamic[If[x, "nothing", g2[]]];

DynamicModule[{x = True, z}, {Checkbox[Dynamic@x], f7[]}]

At first, the output may appear to work as intended however, note that the Dynamic@z term in g2 is not seen in the initial evaluation of the DynamicModule. This means that the variable z remains global not being cast to its local DynamicModule namesake. This can be observed by placing another copy of the output into another cell, unchecking both checkboxes and observing that the two SetterBars are linked via this global variable.

 This seems to pretty much be the death knell for any hopes for Block-like behaviour from DynamicModule. 

One can try to co-opt but Block's behaviour directly but as the following examples show (apart from some unexpected Block variables interaction) this doesn't provide a solution either: 

g2[] := {SetterBar[Dynamic@z, {"z1", "z2", "z3"}], Dynamic@z};

f7[] := Dynamic[If[x, "nothing", g2[]]];

Block[{x, z}, 
 DynamicModule[{x = True, z}, {Checkbox[Dynamic@x], f7[]}]]

nor does

g2[] := {SetterBar[Dynamic@z, {"z1", "z2", "z3"}], Dynamic@z};

f7[] := Dynamic[If[x, "nothing", g2[]]];

DynamicModule[{x = True, z}, 
 Block[{x = False, z}, {Checkbox[Dynamic@x], f7[]}]]

Curiously in the last example, the inner Block initializations do not over-ride the outer DynamicModule.

There are other possibilites that may simulate this behaviour - use of Preread to pre parse this list of arguments and the use of Interpretation but all seem to get stuck on Mathematica lexical scoping. Therefore it seems that trying to create a Block-like variable space is not really viable under the current DynamicModule scoping since such scoping depends on the control flow in the evaluation of the body of the DynamicModule. Hence, unfortunately it appears if that one is forced to adopt the potentially unweildy process of passing all relevant arguments to subfunctions in order to avoid this automatic casting.

Ultimately then the issue here seems to be Mathematica's exclusive use of lexical scoping rather than what I'll coin "meta scoping". Adding "meta scoping" to DynamicModule (with Refresh integration) - the ability to functionally assign variable scoping to a function (say via its Attribute property) - would, I think, do several things - it would allow greater flexibity in organizing and managing argument spaces, the maintainenance of syntax coloring, the production of more readable code and the ability to interactively fashion dynamic interfaces.

Even with performing normal argument passing however, there are several points worth noting in relation to DynamicModule local variable behaviour. Some of these are related to finding workarounds to the earlier examples and hence are added here for completeness

The position of a wrapping Dynamic can DynamicModule's scope and lead to unintended behaviour. Imagine in the following f represents - say an updateable sub-inteface

f8[x_] := {Slider[Dynamic@x], {Dynamic@x}};
DynamicModule[{x},  Dynamic[f8[x]]]

Moving the right-hand slider produces an error about not being able to assign to a raw object - in this case the current local value of the DynamicModule x. This is due to to, during a dynamical update of  f8, x's lastest value of 0 being inserted into Dynamic[x]. Further movement of this slider results in an assignment being attempted to this number instead of x. As before, this can be resolved by placing placing the Dynamic more deeply.

f8[x_] := Dynamic[ {Slider[Dynamic@x], {Dynamic@x}}];
DynamicModule[{x},  f8[x]]

Another point in modularizing code for dynamic interfaces is that initialising DynamicModule local variables can lead to premature evaluations. For example, take the following

DynamicModule[{x = True}, {Checkbox[Dynamic@x], Dynamic@x}]

to be broken down idiomatically as follows"

f9[x_] := {Checkbox[Dynamic@x], Dynamic@x};

DynamicModule[{x = True}, f9[x]]

Clicking the CheckBox creates an error as the box attempts to change the protected "True" Symbol. One straightforward way of dealing with this is to create an Initialization wrapper that performs any initializations after the interface has been created (N.B. this collects all the initializations in one spot  - ControlObjects can perform their own initializations if distributing these in the code is not a problem)


f9[x_] := {Checkbox[Dynamic@x], Dynamic@x};

Initializations[x_] := {x = True};

DynamicModule[{x}, With[{pre = f9[x]}, Initializations[x]; pre]]

Even without initializations within the DynamicModule variable declarations, a premature evaluation can result. This can be managed, with the normal HoldAll Attribute setting.For example, recall the earlier example, that demonstrated how logical dependencies in the DynamicModule's body can affect the scope of its local variables. To avoid this with variable passing one might at a first pass try the following

g2a[z_] := {SetterBar[Dynamic@z, {"z1", "z2", "z3"}], Dynamic@z};

f7a[x_, z_] := Dynamic[If[x, "nothing", g2a[z]]];

DynamicModule[{x, z}, {Checkbox[Dynamic@x], 
  With[{pre = f7a[x, z]}, x = True; pre]}]

Now de-select the checkbox to reveal the "z-setter" and then try and set z to z1. You'll get that familiar complaint about the setting of raw objects. This is because this value of z is being inserted into the setter bar. This can be delayed by changing g2a's HoldAll Attribute

g2a[z_] := {SetterBar[Dynamic@z, {"z1", "z2", "z3"}], Dynamic@z};
SetAttributes[g2a, HoldAll];

f7a[x_, z_] := Dynamic[If[x, "nothing", g2a[z]]];

DynamicModule[{x, z}, {Checkbox[Dynamic@x], 
  With[{pre = f7a[x, z]}, x = True; pre]}]

And the output works with all variables being suitably localized (which again can be checked by the decoupling of any pasted copies)

Ron




























Szabolcs Horv�t <szhorvat at gmail.com> wrote: Ron Monson wrote:
>   
> I'm wondering if anyone can see a shorter way of  localising a large number of variables within a DynamicModule? For example,  instead of 
> DynamicModule[{x1,x2,x3,x4,x5},
> oo[x1,x2,x3,x4,x5];
> oo[x1,x2,x3,x4,x5];
> something with an idiom along the lines of 
> args={x1,x2,x3,x4,x5};
> DynamicModule[args,
> oo[args];
> oo[args]
> 
> The motivation for this is that in constructing a complex  interface the number of varfiables and functions may get quite large and further,  these variables may also need to be accessed in another function that is nested  more deeply . For example, function foo may call other functions which in  turn call other functions ... until finally the bottom function may need to  access say the variable - x1. Having to  repeatedly pass the entire or selected parts of  the variable space can get unwieldy and impact on the final code's  readability. Thanks.


I really don't understand what you are trying to achieve here ... but 
perhaps you will find some inspiration in this:

With[{args = {x1,x2,x3,x4,x5}},
   DynamicModule[args,
     oo@@args;
     oo@@args;
     ...
   ]
]

With[] will just subsitute {x1,x2,x3,x4,x5} for every instance of args 
before it evaluates its body.

Szabolcs


  • Prev by Date: Re: PointSize (and shape) frustration
  • Next by Date: v6: Input Cell Word Wrapping: how to set global default?
  • Previous by thread: Re: Localizing Large Numbers of Variables in a DynamicModule:
  • Next by thread: Probable bug: EvaluationMonitor with NIntegrate, "ExtrapolatingOscillatory",