Get SWI-Prolog to communicate with another application

How does communicating between SWI-Prolog and another application (e.g. that I write) work exactly on Windows? Would I start the SWI-Prolog application on my desktop and then consult my code that both establishes the initial database and rules and also establish itself as a TCP server? Then, my other application would query, assert, and retract over TCP as a client? I’d use Socket? Can anyone point me toward a simple working example (the SWI-Prolog code receives data and does something).

Take a look at these GitHub repositories.

If this is at all an option I would definitely consider running a web server, as it seems the Ludum Dare code does as well. Here is a good intro/howto: How to create a web service easily?

If you can define an interface that really only exchanges data you can use JSON as the format. If you need to pass code around then you might consider Pengines. This comes with working examples, too.

I’m looking at these examples now. I have no skill with or love for Javascript. I’ll be working in C#, probably within a Xamarin.Forms application. It looks like the dotnet interface for Pengines is written in F#. I’ve asked about use with C#, which is definitely possible but possible awkward.

Take a look at

mqi – Python and Other Programming Language Integration for SWI Prolog

I don’t think anyone has created a C# client yet but I would have this on my short list and if I were you would consider taking the time to make a C# client if necessary.

You note Xamarin which I have never used but in a quick read it notes

Open-source mobile app platform for .NET

Is this going to run on a mobile phone?

The idea of using a web server is that you don’t really need any specific client, just a generic HTTP client in your language of choice?

PS: this is why I also suggested using JSON as the data format, by now it has support in all major languages and is completely decoupled from JavaScript the language. It used to be XML but hopefully we can put those dark times behind us.

Xamarin.Forms can target Android, iOS, or Windows. I plan to target Windows.

@EricGT & @Boris I was just going to embed the SWI-Prolog DLL into my C# application. But another user pointed out that running SWI-Prolog separately permits using the debugging tools, which I’ve never used but can imagine being useful.

My code will have a lot of clauses like knows(john,dangerous(bob)) and I’d query for knows(john,dangerous(X)) to get a list of all the people john knows are dangerous. Some of the queries will be more complex and ideally I’d like to send a few in parallel that run on different threads. But, right now, I just want to take the simplest route.

The HTTP protocol is a good one for client-server (that’s how Google sets up its infrastructure - every server trivially provides a number of debugging and monitoring pages (see this Stackoverflow answer for some more details)

The tutorial is a bit out of date. A more up-to-date example is at GitHub - kamahen/swipl-server-js-client: Sample SWI-Prolog server with JavaScript client – you can ignore the JavaScript client if you don’t like JavaScript; and you could even use nc with HTTP/1.1 to see what’s going on:

(echo 'POST /json HTTP/1.1';
 echo 'Host: localhost:9999';
 echo 'Content-Length: 18';
 echo 'Content-Type: application/json'
 echo 'Accept: */*'
 echo 'Origin: http://localhost:9999';
 echo ''
 echo -n '{"query":"X = 1."}'
) | nc -C -N localhost 9999

which returns this from the server:

HTTP/1.1 200 OK
Date: Wed, 20 Apr 2022 21:01:42 GMT
Connection: Keep-Alive
Content-Type: application/json
Content-Length: 131

{"error":"", "printed_output":"", "query":"X = 1.", "query_after_call":"1=1", "success":true, "vars": [ {"value":"1", "var":"X"} ]}

JSON can be easily handled by almost all modern languages. E.g. json — JSON encoder and decoder — Python 3.10.4 documentation and SWI-Prolog -- Manual

As you point out, there are more than one XML schema system (similarly for JSON). But if you want the server to generate XML, library(http/html_write) should do the trick. (I’ll spare you my opinion of XML, except to note that I was at the first major conference about XML, which convinced me to do something else even though I greatly respect Tim Bray.) (I’ll also spare you my opinion of JavaScript.)

I was planning on adding protobuf communication to my example and possibly a Prolog front end (using Tau Prolog), but haven’t got around to it. If someone wants to do that - or wants to add a different communication protocol to my example, I’m happy to accept PRs.

(Discord Discourse forces me to add my own content)

After reading carefully, you asked:

Boris the Impossiblist predicted the question and answered it higher up in the thread, making disparaging remarks about XML in passing (but forgetting to do the same to Java… a terrible miss):

I have some code that communicated between Prolog and Python using JSON (it ran the Python code by using shell/2 and putting the result into a temporary file). I changed this to use Prolog terms, for a considerable speed-up. I would expect performance of protobufs to be somewhere in between using terms and JSON; and the performance of XML to be worse.

It’s a bit of work to fully support escaping of strings or atoms in Prolog terms, so it’s more convenient to use an already existing serialization such as JSON, XML, or protobufs – if the messages aren’t very large, there’s little reason to optimize their serialization/deserialization.

End-to-end throughput; that is - the time from starting to put the message into a buffer (from the “native” structure") to the time when the receiver finishes retrieving the message from the buffer into the “native” structure. There are a multitude of confounding issues: expansion/compression of the contents (e.g. XML vs variable-bit encoding), CPU/memory speed, L1/L2/L3/IO cache performance, calls between user- and kernel-space, network delays (and acknowledgments and retransmissions), type of message (e.g. numbers vs English text), etc. etc.

I’ve learned long ago to not trust my intuitions about performance, but to measure it. You might think you’ve found the best method, only to discover than when the channel is encrypted, you get very different results, for example.
I’ve seen situations where compression improved end-to-end throughput, and also situations where compression made things worse.

Thanks everyone. I think these are all good ideas. I’m going to try the webserver approach first. @Boris & @peter.ludemann , I have a few questions:

(Q1) My SWI-Prolog server complains the instant I send a browser to its port. Why? I believe I’m following the directions from the Javascript client example exactly. The simple_server.pl file starts fine:

C:\Program Files\swipl>swipl simple_server.pl --port=9999 --staticdir=static
% static dir: static
% Started server at http://localhost:9999/
% Starting REPL ...
?-

But when I place the URL http://localhost:9999/ in the browser, I get error messages in two locations. In the browser, I see:

Internal server error
goal unexpectedly failed: simple_server:http_reply_from_files(static_dir('.'),[cache(true)],[path_info('simple_client.html'),protocol(http),peer(ip(127,0,0,1)),pool(client('httpd@9999',http_server:http_dispatch,<stream>(0000013f7b326490),<stream>(0000013f7b325b00))),input(<stream>(0000013f7b326490)),method(get),request_uri('/static/simple_client.html'),path('/static/simple_client.html'),http_version(1-1),host(localhost),port(9999),connection('keep-alive'),upgrade_insecure_requests('1'),user_agent('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Safari/537.36'),accept([media(text/html,[],1.0,[]),media(application/'xhtml+xml',[],1.0,[]),media(image/avif,[],1.0,[]),media(image/webp,[],1.0,[]),media(image/apng,[],1.0,[]),media(application/'signed-exchange',[v=b3],0.9,[]),media(application/xml,[],0.9,[]),media(_1344/_1346,[],0.8,[])]),sec_fetch_site(none),sec_fetch_mode(navigate),sec_fetch_user('?1'),sec_fetch_dest(document),sec_ch_ua('" Not A;Brand";v="99", "Chromium";v="100", "Google Chrome";v="100"'),sec_ch_ua_mobile('?0'),sec_ch_ua_platform('"Windows"'),accept_encoding('gzip, deflate, br'),accept_language('en-US,en;q=0.9')])

SWI-Prolog httpd at LAPTOP-40VDCSO3

In the terminal where I started the server I see:

% [Thread httpd@9999_3] [1] get /static/simple_client.html ...
% [Thread httpd@9999_3] [1] 500 ERROR: goal unexpectedly failed: simple_server:http_reply_from_files(static_dir('.'),[cache(true)],[path_info('simple_client.html'),protocol(http),peer(ip(127,0,0,1)),pool(client('httpd@9999',http_server:http_dispatch,<stream>(0000013f7b326490),<stream>(0000013f7b325b00))),input(<stream>(0000013f7b326490)),method(get),request_uri('/static/simple_client.html'),path('/static/simple_client.html'),http_version(1-1),host(localhost),port(9999),connection('keep-alive'),upgrade_insecure_requests('1'),user_agent('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Safari/537.36'),accept([media(text/html,[],1.0,[]),media(application/'xhtml+xml',[],1.0,[]),media(image/avif,[],1.0,[]),media(image/webp,[],1.0,[]),media(image/apng,[],1.0,[]),media(application/'signed-exchange',[v=b3],0.9,[]),media(application/xml,[],0.9,[]),media(_1332/_1334,[],0.8,[])]),sec_fetch_site(none),sec_fetch_mode(navigate),sec_fetch_user('?1'),sec_fetch_dest(document),sec_ch_ua('" Not A;Brand";v="99", "Chromium";v="100", "Google Chrome";v="100"'),sec_ch_ua_mobile('?0'),sec_ch_ua_platform('"Windows"'),accept_encoding('gzip, deflate, br'),accept_language('en-US,en;q=0.9')])
% [Thread httpd@9999_5] Exception reading 1st line: read_util:read_line_to_codes/2: Timeout in read from <stream>(0000013f7b324c20)
% [Thread httpd@9999_3] Exception reading 1st line: read_util:read_line_to_codes/2: Timeout in read from <stream>(0000013f7b377e40)

(Q2) Do I send assert and retract and queries to the server by packaging these clauses as JSON? What is the format it expects? I don’t understand all the simple_server.pl code.

I’ve only tested the code on Ubuntu and my Chromebook; there might be something “special” about Windows (sorry, but I don’t do Windows – it always seems to be several times more painful than Un*x-ish systems). But a timeout from read_util:read_line_to_codes/2 seems strange … perhaps you can get a traceback at that point and figure out what it’s reading? (E.g., put a spypoint on http_reply_from_files/3 and read_line_to_codes/2, and look at the traceback)

One possibility is that there’s something wrong with the network configuration on your machine – perhaps some firewall issues? But that would be the case only if the read_line_to_codes/2 is reading from the request socket, not from a file.

Anyway, this is what I get, running the simple server on my ChromeBook and querying it with http://localhost:9999:

$ swipl simple_server.pl --port=9999 --staticdir=static
% static dir: static
% Started server at http://localhost:9999/
% Starting REPL ...
?- debug(http(request)).
true.

?- 
% [Thread httpd@9999_3] [1] get / ...
% [Thread httpd@9999_3] [1] 301 moved(/static/simple_client.html); 366 bytes
% [Thread httpd@9999_1] [2] get /static/simple_client.html ...
% [Thread httpd@9999_1] [2] 200 file(text/html,/home/peter/src/swipl-server-js-client/static/simple_client.html); 2,038 bytes
% [Thread httpd@9999_4] [3] get /static/simple_client.css ...
% [Thread httpd@9999_4] [3] 200 file(text/css,/home/peter/src/swipl-server-js-client/static/simple_client.css); 402 bytes
% [Thread httpd@9999_2] [4] get /static/simple_client.js ...
% [Thread httpd@9999_2] [4] 200 file(text/javascript,/home/peter/src/swipl-server-js-client/static/simple_client.js); 4,474 bytes
% [Thread httpd@9999_3] [5] get /static/favicon.ico ...
% [Thread httpd@9999_3] [5] 200 file(image/x-ico,/home/peter/src/swipl-server-js-client/static/favicon.ico); 32,038 bytes

You might also try turning on some of the debug options that are mentioned in the source at line 39 and see what you get with the “nc” command I gave above (you might need to install netcat first). I got:

% [Thread httpd@9999_2] [7] post /json ...
% [Thread httpd@9999_2] Request(json): json{query:'X = 1.'}
% [Thread httpd@9999_2] [7] 200 OK (0.002 seconds; 131 bytes)

As a last resort, you might see what Wireshark tells you.

Thanks. It is indeed annoying that it does not work out-of-the-box on Windows. I turned off my firewall temporarily. That didn’t solve it. I’ll try some of your suggestions.

EDIT: Is anyone using windows who might be able to test simple_server.pl (above) and see if it works for you? @jan , are you on Windows?

Luckily almost never :slight_smile: The web framework generally runs fine on Windows though. Most of the problems are character encoding and newline handling that may need to be programmed more carefully than on POSIX systems. Firewalls are another cause of trouble.

@jan thanks, I’ll continue to try to get this working. The firewall was not the problem.

You don’t have the static folder from the swipl-server-js-client project in C:\Program Files\swipl, or the folder is not accessible to the user running the swipl.
It is better to have separate folder for the projects and not to use the swi-prolog installation folder for this.