*** Here's the original post...
I'm setting up a secondary kernel in sandbox mode with LinkLaunch, and need to pass various expressions to it for evaluation. These expressions are only known at runtime, however (grabbed from notebook cells that the end-user writes). I need to wrap them in Hold to prevent evaluation in the main kernel before they get passed off to the secondary via LinkWrite. The problem I'm having is that somehow, ReleaseHold is ignored when passed through LinkWrite. Here's an example:
In[17]:= newKernel = LinkLaunch[First[$CommandLine] <> " -wstp -noicon"]
Out[17]= LinkObject["/Applications/Mathematica.app/Contents/MacOS/\ WolframKernel -wstp -noicon", 893, 7]
In[18]:= LinkWrite[newKernel, Unevaluated@EvaluatePacket[Developer`StartProtectedMode[]]]
In[19]:= LinkRead[newKernel]
Out[19]= InputNamePacket["In[1]:= "]
In[20]:= LinkRead[newKernel]
Out[20]= ReturnPacket[Null]
In[21]:= LinkWrite[newKernel, Unevaluated@EvaluatePacket[foo = 2]]
In[22]:= LinkRead[newKernel]
Out[22]= ReturnPacket[2]
In[23]:= LinkWrite[newKernel, Unevaluated@EvaluatePacket[foo^2]]
In[24]:= LinkRead[newKernel]
Out[24]= ReturnPacket[4]
In[25]:= expression = Hold[bar = 3]
Out[25]= Hold[bar = 3]
In[26]:= LinkWrite[newKernel, Unevaluated@EvaluatePacket@ReleaseHold[expression]]
In[27]:= LinkRead[newKernel]
Out[27]= ReturnPacket[Hold[bar = 3]]
In[28]:= LinkWrite[newKernel, Unevaluated@EvaluatePacket[Names[Context[] <> "*"]]]
In[29]:= LinkRead[newKernel]
Out[29]= ReturnPacket[{"expression", "foo"}]
In[30]:= LinkWrite[newKernel, Unevaluated@EvaluatePacket[bar^2]]
In[31]:= LinkRead[newKernel]
Out[31]= ReturnPacket[bar^2]
In[32]:= LinkClose[newKernel]
In the line In[26], just as in In[23], the Unevaluated prevents the expression from being evaluated in the local kernel before it gets passed through the link by LinkWrite. I would have expected that when the expression gets to the remote kernel and is evaluated, the ReleaseHold will remove the Hold and the core definition bar=3 will be evaluated, defining the symbol bar.
That, apparently, is not what happens. As far as I can tell, the ReleaseHold is completely ignored. Perhaps someone wiser than I in the dark arts of Mathematica delayed evaluation can explain?
And, more importantly, perhaps someone can suggest a workaround so that when I have an expression in held form, I can get it evaluated by the remote kernel?
*** UPDATE: Here's my partial answer and modified question.
Oh, my... I think I just stumbled over the explanation myself. expression is not defined in the remote kernel, so it's just an undefined symbol. As far as the remote kernel is concerned, there's no Hold to be released!
The reason it looks like I'm getting an unreleased Hold back is that LinkRead returns the unevaluated expression, which is then evaluated here in the local kernel with the definition given above.
Which still leaves me with the problem of how to pass an expression through to the remote kernel for evaluation. All the LinkWrite examples I've found in the documentation specify an expression explicitly in the LinkWrite call. How can one use LinkWrite when the expression to be evaluated must be handled programmatically, i.e., is referred to by a variable?
expressionasxchanges anything. Within theEvaluatePacketstatement, they're both equivalent toHold[bar=3], are they not? – ibeatty Sep 10 '15 at 15:15Withis purpose-built for this exact situation. It does more than "simply renameexpression" -- it evaluates it and then inserts its value into held expressions. This is quite different from, say,ModuleorBlock. For details, see What are the use cases for different scoping constructs?. – WReach Sep 10 '15 at 15:19baris being given a value of3in the local content, not in the sandbox. Trybar^2locally, and then doClear[bar]locally and tryLinkWrite[newKernel, Unevaluated@EvaluatePacket[bar^2]]again. I just getReturnPacket[bar^2]. 8-( – ibeatty Sep 10 '15 at 15:35Unevaluated. TheWithhas to be wrapped around the wholeLinkWriteexpression to avoid prematurely strippingUnevaluated. I have adjusted to response accordingly. – WReach Sep 10 '15 at 17:08Unevaluatedthat it needs to wrap the entirety of an argument toLinkWritein order to work properly and suppressLinkWrite's evaluation of the argument? – ibeatty Sep 10 '15 at 21:02Unevaluatedappeared directly within theWith, it was removed when the innerWithexpression was evaluated, but before theLinkWritewas evaluated. Thus, theReleaseHoldwas no longer being protected from local evaluation. By makingWiththe outer expression,Unevaluatedremains in place while theLinkWriteis evaluated. – WReach Sep 10 '15 at 22:39