# How to evaluate an expression via LinkWrite when the expression is only known at runtime?

*** 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[DeveloperStartProtectedMode[]]]

Out[19]= InputNamePacket["In[1]:= "]

Out[20]= ReturnPacket[Null]

Out[22]= ReturnPacket[2]

Out[24]= ReturnPacket[4]

In[25]:= expression = Hold[bar = 3]

Out[25]= Hold[bar = 3]

Out[27]= ReturnPacket[Hold[bar = 3]]

Out[29]= ReturnPacket[{"expression", "foo"}]

Out[31]= ReturnPacket[bar^2]



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?

A simple way to inject the value of expression into the unevaluated LinkWrite argument is to use With:

Clear[bar]

expression = Hold[bar = 3];

bar

(* bar *)

With[{x = expression},
]

(* ReturnPacket[3] *)

(* ReturnPacket[9] *)

bar

(* bar *)


Original Problem

As noted in the update to the question, the original problem was due to the variable expression only being defined within the local kernel. When the remote kernel was asked to evaluate ReleaseHold[expression], it returned the symbol expression. The local kernel then dutifully evaluated the now unheld symbol expression to produce Hold[bar = 3].

• Why does this work? I don't doubt you, but I don't understand why renaming expression as x changes anything. Within the EvaluatePacket statement, they're both equivalent to Hold[bar=3], are they not? – ibeatty Sep 10 '15 at 15:15
• @ibeatty With is purpose-built for this exact situation. It does more than "simply rename expression" -- it evaluates it and then inserts its value into held expressions. This is quite different from, say, Module or Block. For details, see What are the use cases for different scoping constructs?. – WReach Sep 10 '15 at 15:19
• Thanks! It seems to work brilliantly, and I shall go read the discussion you linked to. (Again. I've read it a few times before, but each time, what I get out of it depends on what I'm wrestling with at the moment.) – ibeatty Sep 10 '15 at 15:22
• Um, I spoke too soon. When I try, it looks like bar is being given a value of 3 in the local content, not in the sandbox. Try bar^2 locally, and then do Clear[bar] locally and try LinkWrite[newKernel, Unevaluated@EvaluatePacket[bar^2]] again. I just get ReturnPacket[bar^2]. 8-( – ibeatty Sep 10 '15 at 15:35
• That is correct. When Unevaluated appeared directly within the With, it was removed when the inner With expression was evaluated, but before the LinkWrite was evaluated. Thus, the ReleaseHold was no longer being protected from local evaluation. By making With the outer expression, Unevaluated remains in place while the LinkWrite` is evaluated. – WReach Sep 10 '15 at 22:39