There are several things to understand here, but all of them center around a common theme. Exactly what is evaluating, and exactly when? It looks like you're just trying to guess what's going on, but guessing is bad... you really should understand the evaluation model.
A: Problems with the code in the question
The method in the question would not have worked under normal evaluation without Dynamic
or DynamicModule
because it used Set
(=
) rather than SetDelayed
(:=
). i.e.,
a=1;
b=f[a];
a=2;
b
(* f[1] *)
So, your example fails immediately because what it assigned to shr0
was not ShearingTransform[deg Degree, {1, 0}, {0, 1}]
, but ShearingTransform[30 Degree, {1, 0}, {0, 1}]
. Changing deg
will never again help you.
You can't just put Dynamic
anywhere and expect it to delay evaluation. For example, the following:
a=2; 2 + Dynamic[a]
will not give you four, and putting a Slider
on the value of a
would not allow you to see the result of 2+a
. I've written on this subject ad nauseum before...a bit in this forum and to a much greater extent here.
Dynamic
works in exactly two places... where it directly translates into output you wish to see visibly, or where it is the value of a control. Every time you use Dynamic
, you need to think about that... am I seeing the result? Am I setting the value from a control? If not, you are misusing Dynamic
and will probably not get the effect you intended.
B: Misconceptions in the comments and other answers
An issue which is causing confusion among the commenters is that DynamicModule
, sadly, does not support SetDelayed
on its member variables. SetDelayed
is silently translated to Set
.
ToBoxes[DynamicModule[{a = 5, b}, b := a; Dynamic[b]], StandardForm]
(* DynamicModuleBox[{a$$ = 5, b$$ = 5},
DynamicBox[ToBoxes[b$$, StandardForm]], DynamicModuleValues :> {}] *)
Note the output shows that what was assigned to b
was 5
, not a
. And the assignment is immediate, not delayed.
Leonid's answer very subtly "works around" the issue by flipping evaluation orders around, but ultimately fails because it can't keep the evaluation order flipped forever. To boil down the essential point of Leonid's example, we have:
DynamicModule[{a, b}, b := f[a]; {Slider[Dynamic[a]], Dynamic[b]},
Initialization :> (a = 0)]
As long as Dynamic[b]
always sends f[a]
to the kernel, things will work. But if, at any point, the Dynamic
instead sends f[0]
(or any other value of a
), the jig is up, and Dynamic[b]
will stay frozen forever. Both the kernel and the FE have copies of a
and b
lying around, and for reasons which are outside of the scope of this question, this happens to cause the FE to think b=f[a]
while the kernel thinks b=f[0]
. But if they ever have to sync up values, the jig is definitely up. And the FE always syncs up values when determining what to save to disk or when making a clipboard copy of a DynamicModule
. So closing/reopening the file or pasting a copy of the resulting cell will show that the association is broken in both my toy example and Leonid's full example.
C: The right approach
So what would I suggest? First, you have to expand the scope of the Dynamic
to fully cover the output. Leonid's example does that correctly, as will mine. And Leonid's example would work perfectly if you simply removed shr0
and just used the full rhs value directly in the Dynamic
, but I'll assume that you have good reason to want to modularize this. The trick, then, is to use DynamicWrapper
to do the updating we might have preferred to do via SetDelayed
. First, let's see how this would apply to the simple example above:
DynamicModule[{a, b}, {Slider[Dynamic[a]], DynamicWrapper[Dynamic[b], b = f[a]]}]
DynamicWrapper
, like Dynamic
, evaluates its contents when they appear onscreen. By wrapping Dynamic[b]
with DynamicWrapper
, I guarantee that the evaluation b=f[a]
will always happen at the right time for Dynamic[b]
to display the proper result.
So, let's see the final result in action:
DynamicModule[{shr, deg = 30},
Column[{Slider[Dynamic[deg], {0, 90, 1}],
DynamicWrapper[
Dynamic[GeometricTransformation[{Blue, Rectangle[{0, 0}, {3, 1}]},
shr] // Graphics],
shr = ShearingTransform[deg Degree, {1, 0}, {0, 1}]]}]]
This code, as all good DynamicModule
code should, will survive restarts of the kernel and the FE, copy/paste, and closing/reopening of the file.
DynamicModule[{a, b}, b := f[a]; {Slider[Dynamic[a], {0, 1}], Dynamic[a], Dynamic[b, TrackedSymbols -> {a}]} ]
(f
is an inert head here) $\endgroup$DynamicModule[{a, b}, b := f[a]; {Slider[Dynamic[a], {0, 1}], Dynamic[a], Dynamic[b, TrackedSymbols -> {a}]}, Initialization :> (a = 0.4444) ]
. But now try this for both versions: select the output and press Ctrl-Shift-I to convert to InputForm and see what's in theDynamicModule
. Now convert back to StandardForm (Ctrl-Shift-N), and see again what happens. Leonid's version got "broken" while the first version picked upa
value forb
butb
still won't update. $\endgroup$DynamicModule
variables are owned by the Front End and not the kernel. Also note that when usingModule
(making the variables be owned by the kernel), the example seems to work. $\endgroup$DynamicModule[{a, b}, b := f[a]; {Slider[Dynamic[a, (a = #; Update[Unevaluated[b]]) &], {0, 1}], Dynamic[a], Dynamic[b]}]
$\endgroup$b
changes the behaviour: you could use(a = #; b) &
as the second arg inDynamic
---it's enough to "fix" it. $\endgroup$