For the past few weeks I was travelling in the USA and had the pleasure of meeting several friends, some of whom are senior developers and architects. It was interesting to learn that many of them were either using Go language in their daily work, or were passionately endorsing the language! Due to lack of time, I had not looked at the language so far, but having returned to Chennai, I decided to devote some time to learning the language. After going through the basic language features, which definitely impress me for their simplicity and uniformity, I wanted to see how I could interface Go with my other favourite language, Lisp. That is the focus of today’s post. Please note that I am not going to discuss the language in any detail; there are several good books and tutorials for that.

There are two parts to interfacing Go with Lisp – calling Go from Lisp and vice versa. Today, I will describe calling Go from Lisp. The other direction, namely from Go to Lisp, will be part of a future article.

What makes mixing Go with other languages possible is that there is a well-defined procedure for interfacing Go with C. So any language that can interoperate with C, can technically interact with Go as well.

I found this article to be a good starting point.

A brief description about my dev environment is in order here. I am using Windows 10 and my Lisp is LispWorks (64 bit) Enterprise Edition. There are several IDEs for Go but I chose Atom with two plug-ins: go-plus (Go package) and platformio-ide-terminal (Terminal package). The latter facilitates building and running Go from the IDE. As part of installing Go system on my Windows, I configured the GOPATH environment variable to point to my Go workspace directory. Finally, although I use Microsoft Visual Studio for my C++ development, I stuck with GCC (as part of MinGW) for the purpose of this project.

I decided to keep the code very minimal because the focus is not on the language itself. So here is the Go program that exports a function called addInt to be called by another language, in this case, Lisp.

package main

import “C”

//export addInt

func addInt(a, b int64) int64 {

return a + b

}

func main() {

}

Two things are quite important here:

1) You have to include import “C” (C is called a pseudo package)

2) The functions that need to be exported should have //export <function name> annotation before their definition. Note also that there is no space after “//”.

I explicitly used int64 as the argument and return type to make sure the code works correctly across the language boundaries (my Lisp is also 64 bit). Other than this, there is nothing else to explain in the code. The source file is Example.go and is saved under “..\Go Projects\src\C-export\” directory (“Go Projects” is my workspace directory, pointed to by GOPATH environment variable).

The next step is to compile this file. Run the following command from a separate command prompt or from the IDE terminal:

> go build -o Example.lib -buildmode=c-shared Example.go

This will generate Example.lib and Example.h. The header file contains several Go to C type mappings, and the exported function signature. The following figure shows a part of the emitted file:

What next? If we want to call this function from a C program, it is enough to link Example.lib with the rest of the C code. However, we want to call this function from Lisp. LispWorks (on Windows) supports calling foreign language functions, as long as they are bundled as a DLL. Since there is no way (that I know of) to compile the Go module into a DLL, we need another layer. We define a C file that acts as a stub/wrapper for the actual Go function, and compile this file into a DLL. Note this introduces an additional function call overhead, but that seems unavoidable.

Here is the stub file Example-wrapper.c:

#include “Example.h”

__declspec(dllexport) GoInt64 addIntWrapper(GoInt64 a, GoInt64 b){

return addInt(a, b);

}

Note the __declspec(dllexport) decoration on the stub function. This is needed to export the function in our DLL. Also, as a good practice (even though it is not required for use with LispWorks), we declare the exported function in a separate header file Example-wrapper.h.

Now we can compile this file into a DLL, linking it with Example.lib:

> gcc -c Example-wrapper.c

> gcc -shared -o Example-wrapper.dll Example-wrapper.o Example.lib

It is a good idea to make sure the DLL contains our exported function. I used the tool dllexp-x64 for this purpose. See the screen shot:

For use in another language environment, we have to distribute the files Example.lib, and Example-wrapper.dll. For use with a C/C++ program, we also need Example.h and Example-wrapper.h.

Let us now come to the LispWorks part. Calling foreign functions (on Windows platform, defined in a DLL ) is fairly straightforward. Here is the code:

(in-package “CL-USER”)

(hcl:change-directory “G:/LispWorks Projects/Ranga/Golang/Interfacing with Go”)

(fli:register-module “Example-wrapper.DLL”)

(fli:define-foreign-function (add-int-wrapper “addIntWrapper” :source)

((x :int64)

(y :int64))

:result-type :int64

:language :ansi-c)

(defun test-go-function (arg1 arg2)

(declare (int64 arg1) (int64 arg2))

(format t “Sum of ~d and ~d is: ~d~%” arg1 arg2 (add-int-wrapper arg1 arg2)))

Since we have copied Example.lib and Example-wrapper.dll to the current project directory, we switch to that directory (otherwise it is necessary to include the DLL location in PATH environment variable). The function define-foreign-function allows us to declare the signature of the foreign function, in this case, the wrapper function available in the DLL. You can see that I have declared the parameters and return type as int64 so that it is consistent with the Go function signature.

The function test-go-function calls our wrapper function to compute the result. The wrapper, in turn, calls the actual Go function. That is it!

We can evaluate this expression to see the result:

CL-USER 1 > (test-go-function 250 350)

Sum of 250 and 350 is: 600

NIL

Perfect! Works as expected.

Hope I have explained in sufficient detail the process of calling Go function from LispWorks. There could be minor differences with respect to other Lisp environments, but it should not be hard to get things working.

Please note that calls across language runtime boundaries are fairly expensive, so they should be used only if justified. For me, Go is a new language in my toolkit, and I really enjoyed learning it!

Have fun!