Combining Prolog with Imperative Programming

Hi folks,

I’d like to discuss approaches and examples of combining Prolog (or logic programming in general) with imperative programming. I’m currently using csprolog, which is a basic implementation in C#. This allows me to write imperative code (e.g. GUI) for desktop, mobile, and web but query an embedded Prolog database for functionality best expressed in logic.

I write the C# code in Visual Studio and the Prolog code in Visual Studio Code (VSC) with the VSC-Prolog extension and call SWI-Prolog occasionally (from within VSC) for interactive querying and debugging. I load this Prolog program into my C# application when the application starts to initialize the csprolog database.

I’m encountering all sorts of interesting questions on how to integrate C# and Prolog. For example, GUI buttons often trigger a command. If that command will ultimately query the Prolog database, should the Prolog code for the query be hard-coded in C#? Should it be part of the Prolog database itself–or is that strangely circular?

Is anyone else combining Prolog with Imperative Programming? Are you embedding? Calling one or the other as some kind of external resource?

-david

3 Likes

I am also interested in this – from an API perspective as well (as i had mentioned in an earlier posting).

What interests me the most is the mismatch between the “ABI” of imperative vs. logic languages – and swi-prolog in particular – the runtime data structures are distinctly different because they also serve different purposes – but, in the end there needs to be some kind of simple and deterministic (user) interaction at the edge …

“ABI” in quotes since I don’t mean binary calling conventions, as practiced in, say, C to assembly compilations, of function calls, but more broadly what arguments are, how they bind, the lack of input and output arguments in Prolog, trail of choice points that is unique to Prolog calls, etc. and, last but not least, the different “philosophy” of what a predicate / called goal is and that it has a truth value.

Would love to see more discussions on that topic …

Dan

p.s.

re: hard-coding or not

I am struggling with this question as well, even within Prolog – and in my case its a question of performance, understandable code (also for me after a few months) and the degree clean generic code can be write across varying requirements.

Might LINQ be a reasonable interface for Prolog to support?

My general comment on this is to try and revert the embedding, i.e., make the foreign parts that your application requires available as Prolog predicates and write Prolog :slight_smile: That of course is not always a great idea. While Prolog looks a bit odd from other languages though, imperative languages look fine from Prolog. Same holds for data abstractions such as tables, trees, graphs, etc.

Still, you may want to embed Prolog :slight_smile: The core API is PL_open_query(), PL_next_solution() … PL_close_query(). This triple defines an iterator over a table of solutions. Many high level languages have good abstractions for this. Then there are the (dynamic) data types that include logical variables. My general rule of thumb is to make sure that the Prolog predicates used by the embedding simply use basic types (strings/atoms/numbers), typically making sure all rows of the table use the same types, e.g., the 2nd column is always an integer.

Given that, the embedding is not that hard. You’ll want some abstractions such as for semidet predicates (only one answer, see PL_call_predicate()). And you may want the type conversion abstractions from the C++ interface which also maps exceptions between the two languages.

Bottom line: keep the interface simple from the perspective of the embedding language. Do not try to setup complex goals but use simple goals with atomic arguments when possible and do the necessary fiddling with arguments in Prolog. This, instead of trying to call e.g. write_term(Stream, Term, [portray(true),numbervars(true), ...]). write a Prolog predicate that passes the options the way you like and call that with the data needed.

Note that if you have data in the foreign code you can translate that to Prolog and pass it to the predicate. You can however also add predicates that allows Prolog to access this data.

We also have to choose between embedding as in linking to a single process and using some interface such as MQI, Pengines, etc. Both have advantages and disadvantages. In general I’d first consider using two processes for high level languages, in particular those that have garbage collection and when there is a mismatch in the threading capabilities (also depending on the need for those in the application). Low-level languages such as C/C++/Rust/… can typically more easily embed SWI-Prolog.

Other good options is to use SWI-Prolog’s HTTP framework and exchange JSON messages or use SWISH. It all depends on the application though. Latency and data exchange required, is there a need for a lot of code development in Prolog or do you interface to a mostly given bit of Prolog code? Etc. Note that also for embedded Prolog you can get toplevel access using ssh and graphical debugging working. The details depend on OS and embedding language.

1 Like

In my work it seems that there are several approaches to goal calls, including across an API – these are currently half-baked considerations …

  1. if arguments must be ground, and they are not, then prolog should raise an exception – mainly because otherwise arguments could get bound unintentionally leading to unintended consequences.

2, 3. if arguments cause a domain problem, then this could either raise an exception (and cut all choice points), or succeed with an error passed back and choice points intact – depending on how the client prefers processing such an occurrence.

  1. arguments can selectively be unbound, and the call either succeeds or fails, and which argument is bound and which left unbound can determine “data” access approaches for efficiency. This is usually for querying and not for changing states.

I am trying to see what use cases I have to firm up the above cases for an API …

Regarding remote calls – there is the issue of how much data to pass back – sometimes one result may be to little with too much overhead to get to a next one – and passing back all result might be too much – so this may need some adjusting across a messaging API …

Dan

I’m not a Prolog expert. I just want to clarify your idea, i.e. what kind of embedding do you want?

  1. Prolog-like program as a DSL in your host language.

    For example, miniKanren or core.logic provide logic-programming paradigm (core.logic also provides facts Database extension). Your program is just using these libraries to do something interesting, e.g. writing predicates or querying.

  2. Embedding a Prolog interpreter into your host language.

    For example, many video games (written in C++) use Lua as a script language, which is used by game designers or players to extend game logic. In this context, you’re just replacing Lua with Prolog.

So what kind of embedding do you want?

1 Like

I’m not sure if what I’m doing is technically even embedding. I added csprolog just like any other C# library. I created a static singleton-like instance of a Prolog interpreter and got a reference to that object, which permits asserts, retracts, and queries via text strings. The Prolog interpreter is just like any other object instance in the application. This approach permits my application to run on an Android phone and (possibly) an iPhone without needing an Internet connection to post queries to a server running Prolog. I’d love to embed a version of Prolog with more bells and whistles (e.g. DCG, constraint programming), but I have no idea how to do that without losing Android compatibility and complicating my code. @peter.ludemann , I’m not familiar enough with LINQ yet to know what it offers in this context. I’ll read up on it. @ jan , I’ve programmed a bit with Rust and really liked it, but since I’m targeting mobile devices from a single cross-platform code base, it is not an option for me. @chansey97 , my use case falls into you first option. I’m using Prolog to store, modify, and query world state in a turn-based text game. Maybe the Prolog side will grow into a sort of ECS for my particular game.

@j4n_bur53 , I’m using C# with Xamarin-Forms which will run on iOS, Android, and Windows from a single code base.

Since my primary project is in C#, one way to combine imperative and Prolog programming is by using versions of Prolog available as a .NET compatible DLL file (dynamic link library). Is anyone aware of such versions besides the one I’m already using (csprolog). I don’t think SWI-Prolog has this functionality, correct?

There is swipl_cs.
Embedding those sources in your project is the easiest way to go.

But then you’re limited to run your program on systems where SWI-Prolog is available, that’s not what you’re looking for.

1 Like

Right. I’m looking for a version of Prolog (or other logic programming language) that I can embed inside C# so that it functions without any other requirements.

1 Like

(Nothing of Relevance here)

FWIW, many years ago I tried P#, a transliteration of Prolog Cafe from Java to C#, that offers a linear logic extension.

Another system you could evaluate, it’s YieldProlog. This is pretty fast, see the benchmarks.

But these are mostly ‘toy’ engines, generally unmantained and lacking various basic Prolog features.

Just for the fun, I re-ran this benchmark for SWI-Prolog 5.10.2 which did it in 4.95 on the current version which does it in 0.287 sec on AMD3950. The CPU is surely an important part. Still, it seems SWI-Prolog got a bit faster too :slight_smile:

@CapelliC , what do you mean by “a linear logic extension”? I considered using YieldProlog. The syntax looked forced to me–but the speed is certainly attractive. I have no idea how fast csprolog is (what I’m using now). I should probably run some tests, but I can say that the syntax is convenient.

IIRC P# exposes resources, see this Wikipedia entry about the programming concept. It’s rather difficult to grasp, as everything related to logic theory… In the end, some problems can be solved more efficiently when we can find an appropriate encoding of the algorithm in terms of resources allocation and consuming…

OK. Thanks @CapelliC , I’ll read up on P#. I remember finding it early in my search for a c# compatible logic programming language, but I did not pursue it–I think because it looked abandoned and I like having people to talk to :slight_smile:

That’s the most important thing. Indeed, when I tried P#, almost immediately found a bug about negation, and - shamlessly - asked on the SWI-Prolog list for a solution, and got advice from Bart Demoen - nothing less.

I think that is supposed to happen according to ISO if you get an integer result that you cannot represent. SWI-Prolog with bounded integers converts to float silently. That is pretty dubious. Probably my engineering background where numbers are typically real/float :slight_smile: If you care about big integers in SWI-Prolog, make sure to have the full version …

If your use case falls into the first option, why use a Prolog interpreter?

I think you are using the second option.