Not an answer, but an extended comment since I am also very interested in this.
The following example is inspired by the last example in this blog post on running julia and python together.
Consider a recursive formulation of the Fibonacci sequence as follows:
wlFib2[n_, fib_] := If[n <= 2, 1, fib[n - 1, wlFib2] + fib[n - 2, wlFib2]]
with the slight caveat that we're passing a second function as an argument to recurse with. We can then define a second function which does the same, and also prints either (W1
or (W2
when it's switching back and from the two functions.
Purely in the WolframLanguage this (naturally) works:
wlFib1[n_, fib_] := Block[{result},
WriteString["stdout", "(W1"];
If[n <= 2,
result = 1,
WriteString["stdout", "(W2"];
result = fib[n - 1, wlFib1] + fib[n - 2, wlFib1];
WriteString["stdout", ")"];];
WriteString["stdout", ")"];
result]
In[1]:= wlFib1[6, wlFib2]
During evaluation of In[1]:=(W1(W2(W1(W2(W1)(W1)))(W1(W2))(W1(W2))(W1)))
Out[1]= 8
The question is, can we define one of these functions in python and communicate back and from? Here's an non-working attempt:
from wolframclient.language import wl
from wolframclient.language import wlexpr
from wolframclient.evaluation import WolframLanguageSession
wlSession = WolframLanguageSession()
wlSession.start()
def pyfib(n, wlfib):
print('(P', end='')
if n <= 2:
r = 1
else:
print('(W', end='')
# here we tell WL (wlfib) to recurse using Python
r = wlfib(n-1, pyfib) + wlfib(n-2, pyfib)
print(')',end='')
print(')',end='')
return r
wlSession.evaluate(wlexpr('''
wlFib[n_, pyFib_] := If[n <= 2, 1, pyFib[n - 1, wlFib] + pyFib[n - 2, wlFib]]
'''))
wlfib = wlSession.function(wlexpr('wlFib'))
pyfib(6,wlfib)
which fails with the following exception:
NotImplementedError: Cannot serialize object of class <class 'function'>
the source code for which is found here.
Looking through the source code a bit, it seems that maybe one could write an Encoder with allow_external_objects=True
? but my attempts have so far been fruitless..
func = Echo; ExternalEvaluate["Python", "from wolframclient.language import wl; wl.Global.func(3)"]
? $\endgroup$func[3]
, which evaluates after return. I basically want to evaluate stuff before the Python function returns, and I want to evaluate it in the calling kernel (not in a separate, independent kernel). $\endgroup$func[1]
andfunc[2]
while in the Python function, then compute the sum of results in Python, then return the result to Mathematica. $\endgroup$WolframKernelController
object, and then talk through a session wrapped around that. It seems like an interesting little project, but probably gonna be a pain to get the bi-directionality working well. $\endgroup$ExternalEvaluate
doesn't complete until python returns, so at the time of the callback wouldn't this just put the Mathematica function to call at the end of the evaluation queue? i.e it will not run untilExternalEvaluate
has finished. I can't see a way around it without launching a new kernel, assumingExternalEvaluate
is atomic like this and Mathematica is not interruptable. $\endgroup$