12
$\begingroup$

Mathematica provides access to $ModuleNumber which can be enormously useful.

Unfortunately I can't find the equivalent for DynamicModule.

I know such a parameter must exist for two reasons:

First, consider evaluating DynamicModule[{A}, Dynamic[A]] four times:

DynamicModule[{A}, Dynamic[A]]
=> FE`A$$4
DynamicModule[{A}, Dynamic[A]]
=> FE`A$$5
DynamicModule[{A}, Dynamic[A]]
=> FE`A$$6
DynamicModule[{A}, Dynamic[A]]
=> FE`A$$7

This incrementation suggests the existence of such a variable.

Secondly, looking at the internals of DynamicModule one finds references to a parameter serialno_, specifically in FE`ExecuteInDynamicModule

Using UpValues and print statements on like Refresh[FE`ExecuteInDynamicModule[a___],___]:>Print[{a}] one finds that the second argument to FE`ExecuteInDynamicModule (serialno_) increments each time, but pulling back one step further to FrontEnd`SynchronousDynamicEvaluate[___,Refresh[FE`ExecuteInDynamicModule[a___],___],___] provides no information, as FrontEnd`SynchronousDynamicEvaluate does not directly provide a serial number and its DownValues give no hints for how to proceed.

Now, I also know that this parameter, whatever it is, is fundamentally a front end parameter, due to both looking at the evaluation chain and code of DynamicModule and because this function:

frontEndModule[varSpec_, body_]] :> 
Block[{Manipulate`Dump`$eDynamicModule = True}, 
DynamicModule[varSpec, body]

Behaves identically to the DynamicModule behavior seen above, except in both following the code and in watching the evaluation chain, none of the heavy module-machinery of DynamicModule is invoked.

Unfortunately after many hours of searching and testing I still do not know where to find this parameter, although I feel like I must be close with the serialno_ argument to FE'ExecuteInDynamicModule.

I'm assuming the symbol will be in some front end context, because I've searched nearly exhaustively through the symbols of the various Manipulate and Dynamic contexts via a symbol-browser and an OwnValues based test to see which symbols (using string pattern "*") are numeric did not reveal the answer, although I can confirm that it produced $ModuleNumber, as a sanity check.

Does anyone know where to find this?

$\endgroup$
6
  • 2
    $\begingroup$ Can I ask why do you need this? It seems that th $xx part will change anyway when you save/open the notebook with created DM. $\endgroup$
    – Kuba
    Mar 2, 2016 at 11:40
  • $\begingroup$ About the context marker, take a look here: meta.mathematica.stackexchange.com/a/14/12 $\endgroup$
    – Szabolcs
    Mar 2, 2016 at 12:21
  • $\begingroup$ I doubt this is available as a kernel variable like $ModuleNumber, FE`* or FrontEnd`* symbols. If it were, there should be some evidence in LinkSnooper while watching dynamic module instantiation, but I don't see any. Maybe you can query the FE to tell you the current number. $\endgroup$
    – Michael E2
    Mar 2, 2016 at 12:53
  • $\begingroup$ @Kuba It provides a clean and simple way to predictively assess what a DynamicModule will call a variable, which can be used in all the same ways as for Module @ Michel E2 how would you suggest going about that? I've looked through pretty much every symbol in FE` at this point. $\endgroup$
    – b3m2a1
    Mar 2, 2016 at 18:57
  • $\begingroup$ Something that may prove useful: here link @halirutan discusses using Internal`GetTrackedSymbols[] and finds the ID numbers that some functions clearly use to get the actual dynamic ID (i.e., the 44 in FE`A$44 ). Unfortunately I can't figure out how to go from those numbers to that serial number, still. $\endgroup$
    – b3m2a1
    Mar 3, 2016 at 4:18

3 Answers 3

8
$\begingroup$

Intro

The answer is: Yes, if you know what you want and it makes sense.

Let's say we want to get current parent's module number from withing Module, this is the way to go:

Module[{x}, {x, $ModuleNumber -1 }]

For DynamicModule it does NOT make sense in context of initial evaluation but:

  • it makes sense in context of any evaluation triggered from within generated DynamicModuleBox, at the end it is the FrontEnd who owns it:

    DynamicModule[{x}
    , {
        $DynamicModuleNumber                        (* does NOT make sense *)
          , Dynamic @  $DynamicModuleNumber         (* DOES make sense *)
      , Button["run", Print @ $DynamicModuleNumber] (* DOES make sense on click*)
      }
    ]
    
  • one needs to be aware and EXPECT that if you close/open notebook/cdf containing this DynamicModuleBox, it will change.

  • you need to EXPECT that if you copy and paste the parent box/cell, it will change

  • it may change if you edit an output cell the DynamicModuleBox lives in

  • for modularized/nested apps with multiple DynamicModuleBox instances one needs to know what is going on to stay in control. That is, you can always get the module number in Initialization and pass by value to nested elements in the body if they are built from DynamicModules too.

Those limitations enclose a good bunch of use cases anyway so one may wonder why the api to that number is not public/more user friendly.

Example

DynamicModule[{x, y}
, {
    Dynamic @ DynamicModuleNumber[]
  , Dynamic@x
  , Dynamic@y
  , Button["set x", x = DynamicModuleNumber[]]
  , Button["set y", y = DynamicModuleNumber[], Method -> "Queued"]
  }
] (* twice *)

enter image description here

Code

So, let's cook up something with respect to what makes sense. Your answer already does something like this but it is too cumbersome for me.

DynamicModuleNumber::noparent="DynamicMoudelNumber not executed in DynamicModule";
DynamicModuleNumber::nokids="DynamicMoudelNumber can not access any variable of parent DynamicModule";

DynamicModuleNumber[]:= DynamicModuleNumber[FrontEnd`Private`names]
DynamicModuleNumber[HoldPattern[FrontEnd`Private`names]]:=(
  Message[DynamicModuleNumber::noparent];$Failed
);
DynamicModuleNumber[{}]:=(Message[DynamicModuleNumber::nokids];$Failed);
DynamicModuleNumber[{Hold[sym_Symbol],___}]:=First @ StringCases[
  SymbolName[Unevaluated[sym]]
, __~~"$$"~~dmn:DigitCharacter..~~EndOfString:>ToExpression[dmn]
]

Explanation

When FE sends a packet to evaluation, which comes from an object (Dynamic/Button) embedded in DynamicModuleBox, it wraps the packet with ExecuteInDynamicModule.

E.g. clicking this button

DynamicModule[{},      Button["print", foo[]]    ]

will result in FE --> K

System`EvaluatePacket[ 
  FrontEnd`SynchronousDynamicEvaluate[ 
    FE`ExecuteInDynamicModule[
      {}
    , 89
    , TimeConstrained[
        Function[Global`foo[]][BoxData["\"print\""], Automatic, 1, {}]
      , 6.0
      ]
    ]
  , 0
  , 9.0
  , 383.25714111328125
  ]
]

89 there is the parent's dynamic module number. Fortunately FE`ExecuteInDynamicModule can be PrintDefinitions-ed so we can learn that it does something like:

ExecuteInDynamicModule[init_, serialno_, body_] := Block[
    {names, ...},
    ...
    names = Symbol @ StringJoin[
      "FE`DynamicModuleVariableList$", ToString @ serialno
    ];
    ...
    ... body ...
    ...
];

It means that dynamically scoped (Block) names (FrontEnd`Private`names) will contain information about serialno, which turn out to be a list of variables {Hold[x$$234], ...}.

Great, assuming there is at least one variable in parent DynamicModuleBox we can parse this symbol, which is what the last down value of DynamicModuleNumber does.

$\endgroup$
2
  • $\begingroup$ This is actually exactly what I’ve been doing/where I got it. But as you note, my solution is a bit more cumbersome. $\endgroup$
    – b3m2a1
    May 29, 2018 at 16:03
  • 1
    $\begingroup$ @b3m2a1 right, hope you don't mind that slightly excessively explanatory style but my goal was a self contained explanation for future visitors rather than a short piece of code for few :). $\endgroup$
    – Kuba
    May 29, 2018 at 21:13
4
$\begingroup$

Short answer: there is no equivalent of $ModuleNumber for DynamicModule.

Authoritative comments by John Fultz (here and below) about the scoping of DynamicModule and how and why it is differing from the comparably simpler scoping of Module:

[...] "this would require a concept of $ModuleNumber to be handled natively by the FE in the typesetting. Which could not be relied upon in the global namespace, so we'd have to be constantly rewriting the typesetting based upon the global state of the FE. Right now, the only concept of $ModuleNumber is one attached to the kernel instantiation, which does not require typesetting and can be different in different sessions. I'm not saying that would have been impossible to do this, but implementation would have been quite tricksy."

[...] "the case outlined here is equivalent to something like Module[{x}, Function[{y}, Module[{x}, {x, y}]][x]]. Except in typesetting. Module evaluates away on us after doing its replacement, but DynamicModuleBox is a persistent creature, and we must always be able to determine which DynamicModuleBox we want each variable to target in order to solve the problem. You have to do that by renaming the variables in the typesetting. If you're not thinking of this as a typesetting problem, you're not properly understanding the problem."

$\endgroup$
0
2
$\begingroup$

So, yes, there's no equivalent, but just as a quick work-around we can leverage our knowledge of how the FE works to extract it. Consider this:

DynamicModule[{
  dmlist,
  serialno
  },
 Dynamic@serialno,
 Initialization :>
  (
   serialno =
    Max@
     ToExpression@
       StringTrim[
        Names["FE`DynamicModuleVariableList$*"],
        "FE`DynamicModuleVariableList$"
        ];
   dmlist =
    ToExpression["FE`DynamicModuleVariableList$" <> ToString[serialno]]
   )
 ]

That serialno is now a handle we can use to access the variables the DynamicModule is serializing for us. The dmlist is just a fun example of what it's useful for.

I can't remember why I wanted it, but now it's there.

Just for fun here's how it let's us figure out what a DynamicModule var is called from the boxes:

dynamicModuleSerialNoExtract[box_DynamicModuleBox] :=

  FirstCase[box,
   HoldPattern[_Symbol?(
        Function[Null,
         SymbolName[Unevaluated@#] === "serialno$$",
         HoldAllComplete
         ]) = sn_Integer] :> sn,
   $Failed,
   \[Infinity]
   ];
dynamicModuleSerialNoExtract[e_] :=
  FirstCase[e,
   d_DynamicModuleBox :>
    dynamicModuleSerialNoExtract[d],
   $Failed,
   \[Infinity]
   ];

dynamicModuleVarExtract[varName_String, e_] :=

  Replace[dynamicModuleSerialNoExtract[e],
   i_Integer :> ToExpression@("FE`" <> varName <> "$" <> ToString[i])
   ];
dynamicModuleVarExtract[var_Symbol, e_] :=
  dynamicModuleVarExtract[
   Evaluate@ToString[Unevaluated[var]],
   e
   ];
dynamicModuleVarExtract[a_?(MatchQ[_String | _Symbol]), e_] :=

  dynamicModuleVarExtract[Evaluate@a, e];
dynamicModuleVarExtract~SetAttributes~HoldFirst

It scrapes out this serialno and then sticks it onto the var in question.

Then here's a quick wrapper function for this DynamicModule form:

serializedDynamicModule[{vars___},
   e : Except[_?OptionQ] : Dynamic[serialno],
   ops___?OptionQ] :=
  DynamicModule[{
    serialno,
    vars
    },
   e,
   Initialization :>
    (
     serialno =
       Max@
        ToExpression@
         StringTrim[
          Names["FE`DynamicModuleVariableList$*"],
          "FE`DynamicModuleVariableList$"
          ];
     ),
   ops
   ];
serializedDynamicModule~SetAttributes~HoldAllComplete

And finally here's the thing in action:

NotebookWrite[InputNotebook[],
 Cell[BoxData@ToBoxes@serializedDynamicModule[{}],"Output"]
 ];
dynamicModuleVarExtract[DynamicModuleVariableList,NotebookRead@NextCell[]]

893

{Hold[FE`serialno$$893]}

Note that we really shouldn't be able to extract this, so this is something of a win, even if it's largely without application.

$\endgroup$

Your Answer

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

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