8
$\begingroup$

Using V 8.04 on windows.

I think this issue might be related to the fact that Mathematica reads main body the Manipulate before evaluating or running the Initialization option.

Or, might be related to, and I quote "Symbols (and their contexts) are created when parsing, not evaluation" as per this question at SO

Too complicated for me to figure it.

I have a Manipulate with a Module in its Initialization section. I find that I need to evaluate the cell which Manipulate is in 2 times to get the correct behavior (ie. have all symbols fully evaluated)

There might be also a way to go around this using Leonid idea shown here but I can't use this method, if it applicable here, as it uses a BeginPackage which is not allowed for my setup.

Since the code used will be in a CDF/demo, these are the symbols I had errors using before and I can't use in a demo: SetOptions, ToExpression, Symbol , $Context, SetAttributes, Clear, Unprotect, DownValues, UpValues, OwnValues and packages not allowed to be used or created. Also, Manipulate[] has to be the most outside construct.

Now, here is the problem description. I Quit[] kernel, and clear everything

Remove["Global`*"]

Then in new cell I evaluate this Manipulate

Manipulate[
 x;
 init[obj];
 get[obj],

 Button["run", x++],
 {x, None},
 {{obj, makeObj[]}, None},
 TrackedSymbols :> {x},

 Initialization :>
  {       
   makeObj[] := Module[{obj, u},
     init[obj] ^:= u = {1, 2, 3};
     get[obj] ^:= {u, Date[]};
     Return [obj]
     ]       
   }     
 ]

which gives this output

enter image description here

You can see that the value of symbol u is not evaluated. Now I go back the Manipulate cell, and evaluate it again (Hit ENTER in the cell) and now I see

enter image description here

So, it takes 2 evaluations to have this work as expected.

Is there a way to make this setup work with one evaluation?


Update (1)

This is my finding comparing the 3 methods below to try to see which one will work for me best.

First, the 3 methods solve the above problem for me. Next, I looked to see which method works best when I make a copy of the code, to see if the new copy will share anything with the old copy of Manipulate, as this is important issue in making demos.

To do that, I modified the above Manipulate example a little to make it return the object itself (obj$nnnn) so I can see if it is named differently or not in the new copy, and also added a counter to see if changing the counter in one copy will update the counter in the other.

Below are the 3 Manipulate example codes, one for each method, and below that I show a result of what I found from the test:

Method 1 (Kugler method)

Manipulate[x;
 get[obj],
 Button["add to counter", x++],
 Button["zero the counter", init[obj]],
 {x, None},
 {{obj, makeObj[]}, None},
 TrackedSymbols :> {x},
 Initialization :>
  {
   makeObj[] := makeObj[] = Module[{obj, u = 0},
      init[obj] ^:= u = 0;
      get[obj] ^:= {obj, u++, Date[]};
      obj
      ]
   }
 ]

Method 2: (Faysal method):

Manipulate[x;
 get[obj],
 Button["add to counter", x++],
 Button["zero the counter", init[obj]],
 {x, None},
 {{obj,
   makeObj[] := Module[{obj, u = 0},
     init[obj] ^:= u = 0;
     get[obj] ^:= {obj, u++, Date[]};
     obj
     ];
   makeObj[]
   }, None},
 TrackedSymbols :> {x}
 ]

Method 3: (Albert method)

Manipulate[x;
 get[obj],
 Button["add to counter", x++],
 Button["zero the counter", init[obj]],
 {x, None},
 {{obj, None}, None},

 TrackedSymbols :> {x},

 Initialization :>
  {
   makeObj[] := Module[{obj, u = 0},
     init[obj] ^:= u = 0;
     get[obj] ^:= {obj, u++, Date[]};
     obj
     ];
   obj = makeObj[];
   init[obj];
   }
 ]

Testing method and results:

Before running each test, I did

Remove["Global`*"]

Then evaluated the Manipulate cell, then clicked on the add counter to make it go to 10, then made a copy of the Manipulate output to a new cell (copy/paste). Then looked to see what shows up in the new copy, and if the new symbol will have zero as value, as that is the initial value for it, or not, and if the symbol name will be different as well.

The results is: Method(1) and method(3) generate a new symbol. But With method (1), this happens only when I click the button on the first Manipulate to increment it one more time. Initially both output had the same obj$nnn value.

Method (3), did not have this issue. The new copy immediately showed zero as value of the symbol, and also new symbol name obj$mmm.

I was surprised, because I thought Method(2) since Module is inside Manipulate it will have a new symbol when I make a copy. Learned something new today.

This screen shot shows the results.

Therefore, Method (3) will work best for me in a demo, as it is a requirement that new copy of Manipulate do not interact with old one.

enter image description here

Thanks to everyone for their help, I learned from all of these answers.

$\endgroup$
7
  • $\begingroup$ Actually I don't underand exactly what's going on here, and I doubt that it is doing what probably was intended, although I of course don't know what the intend was and whether this situation was considered at all. On the other hand I think your code has other problems: is it intended that the frontend variable obj will be replaced with an new "instance" of the module variable obj$xxx at every button press? Use get[obj] ^:= {obj, Hold[u], u, Date[]}; to see what I mean. $\endgroup$ Commented Jan 25, 2012 at 21:36
  • 1
    $\begingroup$ Changing SetDelayed to Set in the initialization part fixes this issue. It seems that with SetDelayed the first run is just defining makeObject[] without executing it. $\endgroup$
    – kglr
    Commented Jan 25, 2012 at 22:40
  • $\begingroup$ @kguler this solution doesn't work though if you want makeObject to have arguments. $\endgroup$
    – faysou
    Commented Jan 25, 2012 at 23:16
  • $\begingroup$ @Faysal, you are right. $\endgroup$
    – kglr
    Commented Jan 25, 2012 at 23:34
  • $\begingroup$ @Nasser, thank you. I will post it as an answer. $\endgroup$
    – kglr
    Commented Jan 25, 2012 at 23:35

3 Answers 3

6
$\begingroup$

Still not sure whether this is what you intended, but if you keep initialization stuff within the Initialization things seem to behave well:

Manipulate[x;
 get[obj],
 Button["run", x++],
 {x, None},
 {{obj, None}, None},
 TrackedSymbols :> {x},
 Initialization :> (
   makeObj[] := Module[{obj, u},
     init[obj] ^:= u = {1, 2, 3};
     get[obj] ^:= {obj, u, Date[]};
     obj
     ];
   obj = makeObj[];
   init[obj];
   )
 ]

I have seen that Faysal Aberkane has given an explanation what goes wrong in the first place. I just had prepared a piece of code which shows you in which order the various parts are evaluated, so I thought I'll share it, although it doesn't add anything new:

Manipulate[
 WriteString[$Output, "body\n"];
 x,
 Button["run", x++],
 {x, None},
 {{obj, Print["var. init"]}, None},
 TrackedSymbols :> {x},
 Initialization :> {
   Print["Initialization option"]
   }
 ]

Here is another try which only uses frontend owned variables:

Manipulate[x;
 "init"[obj];
 "get"[obj],
 Button["run", x++],
 {x, None},
 {u, None},
 {obj, None},
 TrackedSymbols :> {x},
 Initialization :> (
   obj =.;
   "init"[obj] ^:= u = {1, 2, 3};
   "get"[obj] ^:= {obj, Hold[u], u, Date[]};
   )
 ]
$\endgroup$
2
  • $\begingroup$ I don't think that obj is more global then with any other approach which "abuses" Module to create unique symbols: there is an obj$xxx and an u$xxx generated during the Module evaluation, which are of course global in some sense. If you want to avoid that, you can stuff all the code and data into variables owned by the frontend, as I have shown in the example I have added. $\endgroup$ Commented Jan 25, 2012 at 23:41
  • $\begingroup$ +1 for bringing clarity on the evaluation order and your final solution. $\endgroup$
    – faysou
    Commented Jan 26, 2012 at 13:27
5
$\begingroup$

The problem is that the first time Manipulate is executed makeObj[] is not yet defined when obj is initialized. But if you put the definition of makeObj[] just before calling it, it works !

See this related post that lead to this question How can you give a Module a context and have its local variables and Modules belong to that context?. It contains many different ideas about representing simple objects in MM.

Manipulate[
 x;
 init[obj];
 get[obj]
 ,

 Button["run", x++]
 ,
 {x, None}
 ,
 {{obj, 
        makeObj[] := 
           Module[{Obj, u},
             init[Obj] ^:= u = {1, 2, 3};
             get[Obj] ^:= {u, Date[]};
             Obj
           ]; 
        makeObj[]}, None}
 ,
 TrackedSymbols :> {x}  
]
$\endgroup$
3
  • $\begingroup$ Yes thats true, but given the constraints some indentation work isn't so expensive. Will you publish this huge demo ? You're gathering so much intel for it that it must interesting ! $\endgroup$
    – faysou
    Commented Jan 25, 2012 at 23:22
  • $\begingroup$ @Faysal: +1 for the correct explanation of what actually goes wrong. I think with your solution you could avoid defining makeObj at all, initializing obj to just the Obj that the Module returns. $\endgroup$ Commented Jan 25, 2012 at 23:47
  • $\begingroup$ @Nasser: why do you think makeObj isn't global? ?makeObj tells me something else... $\endgroup$ Commented Jan 25, 2012 at 23:47
3
$\begingroup$

It seems that with SetDelayed the first run is just defining makeObject[] without evaluating it. So, changing SetDelayed to Set in the initialization part fixes the issue.

Edit: The following achieves the same thing. Perhaps, it may be also be a way to handle situations where makeObj has arguments.

Manipulate[x; init[obj];get[obj], 
Button["run", x++], {x, None}, {{obj, makeObj[]}, None}, 
TrackedSymbols :> {x}, 
Initialization :> {makeObj[] :=  makeObj[] = 
 Module[{obj, u}, init[obj] ^:= u = {1, 2, 3}; get[obj] ^:= {u, Date[]};obj]}]
$\endgroup$

Your Answer

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

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