As a point of curiosity, I did a quick search in the source code used by Wolfram for various palettes, dialog boxes, and toolbars. About one in eight Dynamic
constructs were accompanied by a With
.
The example you provide is certainly a good one. It illustrates the general principle nicely, but what it doesn't do is to illustrate how widely the general principle applies. To review and restate your example in terms of Dynamic
:
Slider[Dynamic[x], {1, 10, 1}]
Table[Dynamic[i x], {i, 1, 10}] (* wrong *)
Table[With[{i=i}, Dynamic[i x]], {i, 1, 10}] (* right *)
The "wrong" example produces, for x = 1, a list of {i,i,i,i,i,i,i,i,i,i}
because Dynamic
is HoldFirst
, and therefore the value that Table
assigned to i
is never allowed to evaluate inside Dynamic
.
So, in this case, Dynamic
really only shares a property with any other HoldFirst
(or HoldRest
or HoldAll
) function. But what may not be obvious is that Dynamic
poses a bigger problem not shared by many of those functions, although it is shared by Button
. Most functions are evaluated in a well-understood sequence of Shift-Enter evaluations. There may be tricky localizations on the part of Table
, Block
, or related functions, but access to unscoped variables will work quite well.
Less obvious than tight scoping constructs like Table
is a much more abstract and bigger scoping construct that doesn't live inside the Mathematica language. That scoping construct is the kernel session. Once a kernel session ends, the variables disappear and become inaccessible. Stated that way, it seems pretty obvious. But the interesting part is that Dynamic
(and Button
) are capable of not only escaping tight scoping constructs, but they're even capable, by design, of escaping the Mathematica session. E.g., save their results in a notebook, quit Mathematica, restart, and open the notebook.
If you're holding state, then DynamicModule
is the way to go. But, as is pointed out in the comments following your question, you often don't need variables for state...you're just using them as a form of macro expansion. It was for the convenience of authoring, and not at all necessary for the execution of the code, that some variables are used. And these variables are typically best deployed into a Dynamic
construct using With
.
And so the reason I see With
frequently in the Mathematica source code is because it is really important that these interfaces fully encapsulate all initializations and definitions. Sometimes you may write an interface where you don't really care whether it has to be "prepped" by a series of Shift+Enter evaluations. But if you want the interface to really stand alone, you're almost certainly going to be relying on With
.
With
being used as a macro boilerplate generator? Its use there is to shorten things, not making things possible which weren't before. $\endgroup$