# The dangers of SaveDefinitions --- should this really happen?

I have been bitten hard by SaveDefinitions -> True. I'll describe in detail what happened below.

My questions are: Is this a bug? What is the most convenient workaround?

Consider a definition issued like this:

Block[{x, y}, f[x_, y_] = x + y;]


Why didn't I use := instead? Because the expression that stands in place of x+y in my actual problem is computed (symbolically) within the Block so (numerical) evaluations of f are going to be sufficiently fast.

We can check the definition:

?f


f[x_,y_]=x+y

Now let's give x a value ...

x = 1


... and test that f still works as expected:

f[0, 0]


0 (* as expected *)

Let's use f in Manipulate with SaveDefinitions -> True ...

Manipulate[f[a, b], {a, -1, 1}, {b, -1, 1}, SaveDefinitions -> True]


... and check that it works again:

f[0, 0]


1 (* oops!! *)

?f

f[x_, y_] = 1 + y


The definition of f has been rewritten and changed to something else as a side effect of SaveDefinitions.

What is the morale? Probably that SaveDefinitions and Set are not safe to use together.

Note that what happened here is different form the situation when the definition of f is overwritten just because a notebook containing a manipulate with SaveDefinitions has been opened.

My current workaround is to use the following hack to "neutralize" the HoldAll attribute of SetDelayed:

Block[{x, y},
(f[x_, y_] := #) &[x + y];]


Alternative suggestions are welcome.

• I have the feeling it won't get much better than using Evaluate or your alternative
– Rojo
Sep 9, 2013 at 19:51
• Didn't I tell you before that SaveDefinitions is dangerous? Sep 9, 2013 at 21:11
• Although this is posed as problem with Manipulate, hasn't this behavior been a problem with Save for a much longer time? I suspect that the dependency tracing mechanism used in Save is just showing up again. Sep 9, 2013 at 23:21
• I never use SaveDefinitions. Just never. It's terribly convenient, but for my purposes, it is just not sufficiently predictable. And it can sometimes be incredibly inefficient (e.g., when it stores ridiculous amounts of definitions which were hidden behind a Needs or Get). Sep 12, 2013 at 6:02
• @Szabolcs All SaveDefinitions does is to auto-construct an Initialization option for you. I'm a control freak. Let me construct my own Initialization option. Sep 13, 2013 at 19:52

Using InputForm on your Manipulate reveals (like it did in my SaveDefinitions Considered Dangerous post) that it contains the following:

Manipulate[f[a, b], {a, -1, 1}, {b, -1, 1}, Initialization :> {f[x_, y_] = x + y, x = 1}]


So, it actually stores two definitions, one for f and one for x. This actually makes sense, as SaveDefinitions's task is to store any definitions that the Manipulate depends on. When it is checking for this, it finds f, stores its definition and probably then checks whether fitself has any dependencies that need to be taken into account.It checks x and finds a definition in the Global namespace, so this is stored as well.

• @kuba ... or just use \[FormalX] and \[FormalY] symbols in your definitions. Can't go wrong with that. Sep 9, 2013 at 21:50
• Oh, I wasn't aware of this. Thanks :)
– Kuba
Sep 9, 2013 at 21:52
• More people should use the InputForm technique. Bravo to you. Sep 12, 2013 at 6:04
• @JohnFultz Thanks! Sep 12, 2013 at 10:43

This seems to be a very easy way to bite yourself in the foot (non-flexible programmers this is not for you). I also have the habit of doing those blocked set-based definitions. It's probably time to change the habit now.

It seems to me that SaveDefinition extracts the definitions of the symbols required by the Manipulate, as you entered them. If you used :=, then it will be stored as :=, if = then =. So, if you made a definition with = that required certain localization, that localization won't be captured by SaveDefinitions.

I am not sure how to avoid that, other than using := in the first place and ensuring what you pass to := is already the final form of the rhs you desire.

So, your own suggestion looks good. The natural alternative is using f[x_, y_]:=Evaluate[...].

Another attempt of an alternative could be to modify your Manipulate after creation, changing the Set-based definitions to SetDelayed (assuming this doesn't bring other unintended sideeffects).

An implementation could be (please @Mr.Wizard, prettify this if you see a nice way. I haven't got the time or brains these days)

postProcessTheManipulate[m_Manipulate] :=
Replace[m // ToBoxes // ToExpression, (Initialization :> l_List) :>
Block[{},
(Replace[Hold@l,
HoldPattern@
Set[args : PatternSequence[Except[_Symbol], ___]] :>
SetDelayed[args], {2}] /.
Hold[ll_] :> (Initialization :> ll)) /; True], {1}]


To be used

Manipulate[f[a, b], {a, -1, 1}, {b, -1, 1},
SaveDefinitions -> True] // postProcessTheManipulate


Edit by Mr.Wizard -- As requested here is a terse version, assuming it is safe to replace all Set expressions at level one on the RHS of Initialization:

pptm[m_Manipulate] :=
Replace[m // ToBoxes // ToExpression,
init : (Initialization :> _) :>
RuleCondition @ Replace[init, Verbatim[Set][x__] :> SetDelayed[x], {2}],
{1}
]

• Bite yourself in the foot? Around here we say shoot yourself in the foot, which somehow makes a lot more sense. :^) Sep 9, 2013 at 20:21
• Could you explain the reason for args : PatternSequence[Except[_Symbol], ___] -- at the moment I don't see the need for the complex pattern. Sep 9, 2013 at 20:26
• By providing the Manipulate with Method -> {"ExtraVariables" :> {f}} f will get localized and the original un-delayed definition wont be contaminated. ( {f,x} in Mr.Wizards version )
– ssch
Sep 9, 2013 at 20:40
• @Rolf I used the Spelunking package on Manipulate to see how it created the definitions, ran into it in ManipulateDumpMakeManipulateBoxes. Would be even nicer if SaveDefinitions localized things automatically to avoid the problem Sjoerd linked
– ssch
Sep 9, 2013 at 21:43
• @ssch That prevents the interaction with f but the Manipulate still leaks the setting for x. After a restart the notebook with that Manipulate panel will have x` defined as 1. So you have to add all variables (i.e., x and y) to the list. Sep 10, 2013 at 8:37