Clojure + ClojureScript with SWI-Prolog

Hi folks, I’m new here. Just want to share some interesting project I’ve been working on: Maurício Szabo / spock · GitLab

The idea is to call prolog from Clojure. Initially, I used the JPL bridge but then I started to experiment with the WASM version and while it does work, it’s tricky because I’m using the WASM version by submitting forms into STDIN and capturing the output via STDOUT. But hey, it’s a start right?

So to tease a little bit: the idea is to write ClojureScript forms, send them to Prolog, and get back results like so:

(solve {:runtime swi} '(and (member :m [1 2 3])
                            (is :res (+ 1 2))))
;; returns
;; [{:res 3, :m 1} {:res 3, :m 2} {:res 3, :m 3}]
5 Likes

Not seeing wasm code in the linked repository. Instead of stdio, it should be also possible to use the SWI-Prolog wasm JavaScript interface directly with ClojureScript JavaScript interop. The JavaScript interface is direct translation of SWI-Prolog FLI though the Emscripten JavaScript WebAssembly interop. The last means you could also try to use it directly from ClojureScript JS interop too, potentially skipping one layer.

SWI-Prolog JavaScript interface (there is also example in the directory): swipl-devel/prolog.js at 1297605d5acbcb32665e5d05d368d25cf15224e9 · SWI-Prolog/swipl-devel · GitHub

1 Like

Is this specifically for using Prolog in the browser, or is it for communicating with a Prolog server? If the latter, I have some straightforward Javascript code for communicating via JSON, and the corresponding Prolog server code; probably this would not be difficult to rewrite in another language.

Yeah, it was on pull request, I just merged it. Wanted to make sure CI would work.

Thanks for the suggestion, it was going to be my next question on this forum :slight_smile: . I found lots of examples that didn’t quite work very well, and the only one that did work was this one: SWI-Prolog WebAssembly demo, so I translated it to the WASM compiled binary that I was able to produce (because the format they use on that page is outdated I believe

About the Javascript interface, I did try to use with call_string but it only returns me true or false. And, in fact, the example that’s on the directory is the one I tried that did not work for me, and I have no idea how to use the Javascript interface to list unifications and all goals for a query…

You can always wrap a goal in findall/3, bagof/3, or similar, to get all results. (forall/2 can be used if you’re outputting to a stream.

My code is at GitHub - kamahen/swipl-server-js-client: Sample SWI-Prolog server with JavaScript client , and if you’d like me to put together a specific query and response, I could do that for you. (Currently, the code expects the query as a sting; but it shouldn’t be difficult to use, e.g. JSON for the query itself and pack/unpack it using the the JSON support. [Protobufs are another possibility.] I’m not familiar with Clojure forms, but they shouldn’t be difficult to handle either.

It is specifically for using Prolog in the browser, or on node.js environments. Not to communicate with a server.

That’s actually not my problem, sorry if I wasn’t clear. @rla suggested to use the WASM Javascript interface, but I can’t find a way to return the unifications. For example, prolog.call_string('A = 1.') gives me just true as a result, not a way to get the value of A in the result.

I also tried with other functions that the JS interface offers me, but they all return numbers and I’m not sure if this is some “internal representation”, or if it’s something else entirely. What I did was interface with STDIN and STDOUT (well, “WASM STDIN” and “WASM STDOUT”, their respective callbacks, not a “real” stdin/stdout) because that’s what one of the examples I saw did, and it indeed was the only way I could find to control the results. I’m aware that’s quite brittle and may fail if Prolog sends a result that my code can’t understand (for example, a write(...) somewhere) so that’s why I’m looking for a better alternative :slight_smile:

“EDN” is Clojure’s “native” format, but Transit is more often used for better performance while still supporting Clojure’s native types. It sort of “piggie-backs” on top of JSON or MessagePack; I once wrote a little bit of code to handle that (here (warning: terrible code that I wrote in probably the first two weeks I was learning Prolog >_<)), but I don’t think it’s terribly complete.

That being said, JSON is also pretty easy to deal with in Clojure.

JavaScript communicates with SWI WebAssembly version using FLI. I missed to give a link to FLI documentation. It’s available here: https://www.swi-prolog.org/pldoc/man?section=foreign

The “numbers” are term references used by SWI-Prolog. I think it’s both the internal representation and communication format for SWI and C language interface. The prolog.js file provides JavaScript bindings to these C functions (they are no C anymore as they are compiled but the calling convention is so-called C calling convention).

To call a predicate and exact an argument, you need something like this:

// Example code how to call predicate_name(hello, Var)
const ref = prolog.new_term_ref();
prolog.put_functor(ref,
  prolog.new_functor(
    prolog.new_atom('predicate_name'),
    2
  )
);
const helloArgRef = prolog.new_term_ref();
const helloValueRef = prolog.new_term_ref();
// Gets term reference to the argument at
// the index (1-based).
prolog.get_arg(1, helloArgRef, formal);
prolog.put_chars_string(helloValueRef, value);
if (!prolog.unify(helloArgRef, helloValueRef)) {
  throw new Error('Failed to unify the call argument.');
}
if (prolog.call(ref)) {
  const varRef = prolog_new_term_ref();
  prolog.get_arg(2, ref, varRef);
  // Analyze varRef using matching functions in prolog.js
  // https://www.swi-prolog.org/pldoc/man?section=foreign-term-analysis
}

More info (to get all solutions for example): https://www.swi-prolog.org/pldoc/man?section=calling-prolog-from-c