How do I execute a q function call with parameters over IPC?
Short answer:
q)phandle: hopen `:serverhost:5012
q)phandle (function; arg1; arg2 ..)
..
q)hclose phandle
q)
To invoke a function in another q process (i.e., a function that already exists in that q process), you need the following:
- a process handle to the other q process,
- the name of the pre-existing function in the other process, and
- the parameters you want to pass.
Then all you need to do is to apply the process handle to a list whose first element is the function name (as a symbol) and whose remaining elements are the arguments.
For example, let’s start one q process, which will be our server, listening on TCP port 5012. In our server, we’ll define a function square (we’ll make the background color different for the server to make it easier to distinguish from the client):
$ q -p 5012 // server
..
q)\p // display the listening port
5012
q)square: {x * x}
q)square 3
9
q)
Next, we’ll start up another q process (the client), create a process handle connected to the first q process (the server), and request the first process to compute square 5:
$ q // client
..
q)phandle: hopen `:localhost:5012
q)phandle (`square; 5) // remote execution
25
q)
To call a function with more parameters, simply add them to the end of the list. We’ll demonstrate by defining a 2-argument function on the server that calculates the length of a right triangle’s hypotenuse:
q)hypotenuse: {sqrt sum square x,y} // server
q)hypotenuse[3;4]
5f
q)
q)phandle (`hypotenuse; 5; 12) // client
13f
q)
What if the function you’re calling doesn’t take any parameters? For example, we’ll define a function in the server called serverTime that returns the local time according to the server:
q)serverTime: {[] .z.T} // server
q)serverTime[]
11:51:34.762
q)
You can’t pass zero parameters over IPC:
q)phandle enlist `serverTime // a list with just
'length // the function name
q) // is not allowed
However, you can pass whatever you like, and it won’t matter:
q)phandle `serverTime`ignored
11:52:28.537
q)phandle (`serverTime; ()) // () is often used
11:52:40.923
q)phandle (`serverTime; ::) // so is ::
11:52:47.338
q)
See :: (generic null).
So far, all of our examples involved a client invoking a predefined function on the server. You can also pass a function defined on the client to be executed on the server. To see this, let’s define a global variable on the server:
q)SERVERGLOBAL: 47 // server
q)
Now, on the client, we’ll define a function called getSERVERGLOBAL to retrieve the value of SERVERGLOBAL on the server. Instead of passing the name of the function (i.e., `getSERVERGLOBAL), we pass the function’s value:
q)getSERVERGLOBAL: {[] SERVERGLOBAL} // client
q)getSERVERGLOBAL[]
'SERVERGLOBAL // not defined here
q)phandle (getSERVERGLOBAL; ::) // note the missing `
47
q)
Notice that this operation does not cause getSERVERGLOBAL to become defined on the server:
q)getSERVERGLOBAL // server
'getSERVERGLOBAL
q)
This technique can be used to run built-in functions and anonymous functions (aka lambdas) on the server as well:
q)phandle (+; 2; 4) // client
6
q)phandle ({til SERVERGLOBAL - x}; 42)
0 1 2 3 4
q)
There is one more way to convey code to the server to run: you can pass the code in a string.
q)phandle "SERVERGLOBAL + 4" // client
51
q)
We prefer passing a function over a string, because – especially as the expression to be passed gets more complex – it’s easier to read.