Search code examples
common-lisp

How to Call a Go Program from Common Lisp


I have a Go program which cannot be rewritten in Common Lisp for efficiency reasons. How can I run it via Common Lisp?

Options so far:

1. CFFI

Using the foreign function interface seems to me like the "correct" way to do this. However, the research I did lead directly to a dead end. If this is the winner, what resources are there to learn about how to interface with Go?

2. Sockets

Leaving the Go program running all the time while listening on a port would work. If this is the best way, I'll continue trying to make it work.

3. Execute System Command

This seems all kinds of wrong.

4. Unknown

Or is there an awesome way I haven't thought of yet?


Solution

  • It depends on what you want to do, but 1-3 are all viable options

    1. CFFI

    To get this to work you will need to use FFI on both the go and lisp side. You will need to extern the appropriate function from Go as C functions, and then call them using cffi from lisp. See https://golang.org/cmd/cgo/#hdr-C_references_to_Go on how to extern function in go. In this case you would create a dynamically linkable library (dll or so file) rather than an executable file.

    2. Sockets (IPC)

    The second option is to run your go program as a daemon and use some form of IPC (such as sockets) to communicate between lisp and go. This works well if your program is long running, or if it makes sense to have a server and one or more clients (the server could just as easily be the lisp code as the go code). Sockets in particular are also more flexible, you could write components in other languages or change languges for one component without having to change the others as long as you maintain the same protocol. Also, you could potentially run the components on seperate hardware. However, using sockets may hurt performance. There are other IPC methods available, such as FIFO files (named pipes), SHM, and message queues, but they are more system dependent than sockets.

    3. System command (subprocess)

    The third way is to start a sub-process. This is a viable option, but it has some caveats. First of all, the behavior of starting a sub process is dependent both on the lisp implementation and the operating system. UIOP smooths out a lot of the details for implementation differences, but some are too great to overcome. In particular, depending on the implementation you may or may not be able to run a subprocess in parallel. If not you will have to run a seperate command every time you want to communicate with go, which means waiting for the process to start up every time you need it. You also may, or may not be able to send input to the subprocess after starting it.

    Another option is to run a command to start a go process, and then communicate with it using sockets or some other IPC, and then running a command to stop the process before closing the lisp program.

    Personally, I think that using sockets is the most attractive option, but depending on your needs, one of the other options might be better suited.