GoLang and external predicates

Hello,

Is it (in principle) possible to use GoLang instead of C++ to write external predicate?

thanks,

Dan

To answer my own question …

Most probably just by importing the c library

https://golang.org/cmd/cgo/

not sure if this is all that is needed – can a go compiler deal with the C marcro’s?

Dan

As long as it can generate a function with C compatible calling conventions and it can call the SWI-Prolog C API (assuming you want to access the predicate arguments). Many languages provide some way to directly call C functions. Many languages can be called from C, but some require some glue code in C. Some bindings can pick the constants and types from SWI-Prolog.h. If you cannot do that you need some conversion script to avoid the typing and make sure you can deal with changes to SWI-Prolog.h.

Changes (except for additions) to SWI-Prolog.h are rare, but the introduction of atomic rational numbers required some renumbering of constants.

1 Like

Hello!

I don’t suppose anyone has created a Go library to call SWI-Prolog yet? All my Googling hasn’t turned up anything at least.

Is writing such a fli very difficult? Does anyone know of any good place to start learning? I have seen swipl-rs/swipl-fli at master · terminusdb-labs/swipl-rs · GitHub. Maybe using this as an inspiration would help…

Thank you!

Edit: actually I see that that Rust lib just uses bindgen, so it didn’t help me learn at all.

In the end, the low level interface is to C. How you deal with that depends a lot on the target language. Various interfaces use the native call to DLL/SO functionality of the target language. Others exploit the C binding of both languages to create a bridge in C. Since some releases we try to keep all constants defined in SWI-Prolog.h unchanged and maintain full binary compatibility of the interfaces. Still, sometimes things need to change and you only get source compatibility.

There are quite a few examples around: C++, Java, Perl, Python, Rust, C# I can remember. Probably there are more.

Looking forward!

2 Likes

Hi Jan,

Thanks for the reply! I’ll take inspiration from the available interfaces and have a go at doing it in Go.

If anyone finds this and wants to help please let me know!

Thanks again!

I presume you’re referring to “Version 2” of the C++ interface. A few thoughts …

  • The “version 2” documentation is somewhat rough (I haven’t had time to improve it; and I’m not a good technical writer anyway).
  • The string-encodings part of the API is incomplete. I probably won’t be able to work on it until January (I have some ideas, but there are a lot of dark corners in C++ and I expect that I’ll have to modify my ideas quite a bit).
  • If you can wrap term_t, atom_t in some kind of unique struct, that makes the interface both cleaner and less error-prone. I tried doing this with the C interface (SWI-Prolog.h), but it proved to be a lot of work. The basic problem is that in C, term_t, atom_t, etc. all look like integers, so an attempt to overload on them makes an error-prone interface (in C++, PlTerm, PlAtom avoid some of the problems; but the constructors for PlTerm are more complicated than I like because of the overloading problems. [Yes, I know that Go doesn’t have overloading, except by variadic parameters, which are unsafe-ish]
  • Destructors can be tricky (PlQuery was a bit tricky to get right, for example).
  • The C++ “throw” statement is really handy because it allows a fairly clean mechanism for handling both failure (for the “unify” functions) and errors (e.g., “unify” creates a Prolog exception). The Go v, err := ...; if err != nil style is closer to the C interface than the C++ interface.
  • There are quite a few examples of using the C++ interface in test_cpp2.cpp, but I want to add a lot more.

Hi Peter,

Thanks for all the information! I’ll keep those points in mind when I take a look at the C++ interface.

It seems that writing a Go interface will be a lot harder than I first thought. haha.

The C++ interface turned out to be harder than I had first thought - and I had @jan’s “version 1” to get me started.

The C++ interface has some methods that aren’t really needed, but are there for either backwards compatibility or because they follow fairly closely to the C interface (which has evolved over time, so there are still some anachronisms in the C++ API, which I’m slowly getting rid of).

BTW, there’s an alternative C++ interface here: SwiProlog-C++ Homepage

Writing an interface that “works” is not so hard. Just wrap the C interface. Writing one that looks “natural” from the host language point of view is a lot harder. Other examples to look at are JPL (Java), The JavaScript interface of the WASM version and @oskardrums’s interface to Emacs Lisp (sweep: SWI-Prolog Embedded in Emacs). Unlike most of the other interfaces, these interfaces translate Prolog terms to corresponding native objects/data structures (recursively). That has advantages and disadvantages. Lifetime management and constructing terms typically gets easier. But, you may be translating far too much data and some things are just impossible, like traversing a Prolog structure and binding some variables you may find in them.

All in all I’m pretty happy with the overall design on the JavaScript interface (although it has some rough edges, notably when it comes to exception handling). Note that details always depend on the target language. For example, in C++ we are still struggling with Unicode handling as std::string is just a byte sequence and std::wstring is only a wchar_t sequence using some Unicode encoding (in practice USC-4 except for Windows where it is UTF-16, but the C(++) standard also allows for 8-bit wchar_t using UTF-8). In JavaScript, strings are Unicode, so we can always perform a lossless and easy to understand translation.

Thanks to @peter.ludemann the C++ interface is getting comprehensive and effectively cleans up most of the dark history of the C interface. The C interface is a bit ugly as it carries too much history. When it comes to raw performance it is still best. Notably it gives more control over managing term_t references and avoids the overhead of C++ exception handling (at the price of a lot of if()).

My measurements indicate a negligible overhead in the C++ interface, unless a throw is done (the try in PREDICATE has zero cost by itself). And the C++ interface allows returning a true or false instead of doing throw PlFail(), if the code is performance-critical. You can also use PL_domain_error() (and return false) if you wish. The various constructors (for PlTerm, PlAtom etc) throw a C++ exception (which is caught in PREDICATE) for things like resource errors; but such situations are rare and shouldn’t affect performance.

1 Like