Example and background ( note the usage of Hold, ReleaseHold ):
The following code represents a static factory method to create a scenegraph object ( from an XML file ). The (output-)field is an instance of CScenegraph ( an OO-System class ).
new[imp_]:= Module[{
ret,
type = "TG",
record ={{0,0,0},"Root TG"}
},
ret = MathNew[
"CScenegraph",
2,
MathNew["CTransformationgroup",1,{type,record},0,0,0,0,Null]];
ret@setTree[ret];
ret@getRoot[]@setColref[ret];
csp = loadClass["CSphere"];
spheres = Cases[imp, XMLElement["sphere", _, __], Infinity];
codesp = Cases[spheres, XMLElement["sphere",
{"point" -> point_, "radius" -> rad_, "hue" -> hue_}, {}] -> Hold[csp@new[ToExpression[point], ToExpression[rad], ToExpression[hue]]]];
ret@addAschild[ret@getRoot[],ReleaseHold[codesp]];
ret
];
My question is about the following:
spheres = Cases[imp, XMLElement[\sphere\, _, __], Infinity];
codesp = Cases[spheres, XMLElement[\sphere\,
{\point\ -> point_, \radius\ -> rad_, \"hue\" -> hue_}, {}] -> Hold[csp@new[ToExpression[point], ToExpression[rad], ToExpression[hue]]]];
ret@addAschild[ret@getRoot[],ReleaseHold[codesp]];
where
addAschild
adds ( a list of ) geometries to a ( root ) transformationgroup and has the signature
addAsChild[parent MathObject, child MathObject], or
addAsChild[parent MathObject, Children List{MathObject, ...}]
and the XML element representing a sphere looks as follows:
<sphere point='{0., 1., 3.}'
radius='1'
hue='0.55' />
If I do NOT USE Hold[] , ReleaseHold[] I end up with objectdata like
{"GE", {"SP", {CScenegraph`point, CScenegraph`rad}}, {CScenegraph`hue}}
while I would have expected
{"GE", {"SP", {{4., 3., -4.}, 3.}}, {0.45}}
(The above code with Hold[], ReleaseHold[] yields the correct data.)
Questions
1. Why is Hold necessary in this case? ( In fact, is it? Is there a way to code this without Hold[], ReleaseHold[]? ) ( I got it right by trial and error! Don’t really understand why. )
2. As a learning point: What is the prototypical example / case for the usage of Hold / ReleaseHold?
EDIT:
Summary of Leonid’s answer. Change this code
codesp = Cases[spheres, XMLElement["sphere",
{"point" -> point_, "radius" -> rad_, "hue" -> hue_}, {}] -> Hold[csp@new[ToExpression[point], ToExpression[rad], ToExpression[hue]]]];
ret@addAschild[ret@getRoot[],ReleaseHold[codesp]];
to:
codesp = Cases[spheres, XMLElement["sphere",
{"point" -> point_, "radius" -> rad_, "hue" -> hue_}, {}] :> csp@new[ToExpression[point], ToExpression[rad], ToExpression[hue]]];
ret@addAschild[ret@getRoot[],codesp];
The short answer for the first question is that you probably should have used
RuleDelayedrather thanRule, and then you don’t needHold–ReleaseHold.It is hard to be sure what is going on since your code sample is not self-contained. One thing to be sure is that OO-System performs non-trivial manipulations with contexts, since it uses contexts as an encapsulation mechanism (which makes sense). Normally,
RuleandRuleDelayedinject the matched expressions in the r.h.s., so it is not clear how this could happen. Here is one possible scenario (you may execute this in a notebook):Now,
What happened is that, since we used
RuleinXMLElement[...]->rhs, ther.h.s.evaluates before the substitution takes place – in this case the functionfevaluates. Now,The result is different here since the idiom
XMLElement[...] :> rhswas used in implementation offn1, involvingRuleDelayedthis time. Therefore,f[{a,b}]was not evaluated untilaandbwere substituted by the matching numbers from the l.h.s. And sincefdoes not have a rule for the argument of the form of list of 2 numbers, it is returned.The reason why your method with
Hold–ReleaseHoldworked is that this prevented the r.h.s. (functionfin my example, and the call tonewin your original one) from evaluation until the values for pattern variables have been substituted into it. As a side note, you may find it useful to add better error-checking to your constructor (if OO-System allows that), so that problems like this would be better diagnosed at run-time.So, the bottom line: use
RuleDelayed, notRule.To answer the second question, the combination
ReleaseHold–Holdis generally useful when you want to manipulate the held code before you allow it to evaluate. For example:One can probably come up with more sensible examples. This is especially useful for things like code-generation – one less trivial example can be found here. The specific case at hand, as I already mentioned, does not really fall into the category of cases where
Hold–ReleaseHoldare beneficial – they are here just a workaround, which is not really necessary when you use delayed rules.