Well, part of this is my personal axe to grind, having occasionally played with Prolog and Prolog derivatives and really liking part of what I saw, but only part of it. And logic programming isnât necessarily the best or only way forward. But, the part of LP that I like is that it seems to unify three otherwise separate things- âfunctionsâ, âtypesâ, and âdata structuresâ - into a single concept, the âpredicate/relationâ. Given that we seem to be facing a crisis of accidental complexity in programming, a major foundation-level simplification like that seems like a fairly important breakthrough to me.
The current trending logic programming âlanguageâ - MiniKanren or MicroKanren - isnât even a language, but rather an algorithm that is usually implemented in a functional/procedural/OOP programming language. Itâs a very nice thing to exist, and the Kanren family of algorithms I think has made some important steps forward, but I still would like a Kanren derivative to be its own fully self-hosting language.
Anyway, what I mean by âparseable, iterable networkâ is this:
In Prolog you define a predicate as a series of assertions, which can be either literals or function-like rules, and can intermingle them freely, eg:
city(seattle,washington).
city(losangeles,california).
city(nyc,newyork).
region(westcoast,california).
region(westcoast, washington).
region(eastcoast, newyork).
region(R,City) :- region(R,State),city(City,State).
So now you can treat âregionâ as a mixture of a data structure/table, a function testing membership, and also a computed query / simulated table that calculates and returns (or constructs) data on the fly, as itâs queried.
region(westcoast, nyc) â if thatâs an assertion, then itâs just a dumb data literal. But if itâs a query then itâs a query thatâs currently not true (the âregionâ table/predicate does not contain these values, or a rule that allows you to infer those values), so this query âfailsâ, which is either like exiting your whole function or like returning false, depending on how you handle it
region(westcoast, Place) â if thatâs a query, then variable Place becomes bound to a keyword which can be found defined in the region table/predicate, but it stores state somehow on the stack/heap and will backtrack if a later expression using those variable bindings fails. So now you have something like an iterator or a generator function / coroutine. Every function can return multiple (and potentially infinite) values, thatâs automatically baked in.
region(R,Place) â now you have an iterator/generator/coroutine with two free variables.
So where in functional programming or even OO programming youâd probably implement âregionâ as three or four very different literal data structures / abstract data types / objects / functions, here we just have one thing, a predicate, that can sort of shapeshift into being a definition, an evaluator, a query, or just a dumb set of data, depending on the circumstances.
Itâs a very powerful abstraction in my opinion, and would be really great for situations like personal databases, configuration files, build systems, a lot of things that we use a lot of incompatible ad-hoc config/code languages for at the moment. But thereâs a cost. The logic programming mindset is tricky to connect up to more ordinary code because of all the backtracking, and the variables having to be carried around everywhere. You never just have a function that returns a simple value, you always have a bit of a âreturning a gorilla holding the banana and the whole jungleâ problem in that youâre returning âa set of variable mappings that somewhere hold your value, plus a call stack to generate a new set of mappings in case those donât work for youâ.
If this could be simplified and streamlined a bit, I feel like we might have something really nice that could meet our config, data and programming needs for the next 100 years. And could potentially work to define systems both in the small and in the large. But itâs not quite there yet.
For example (to bring this back to the purpose of this thread), defining how a âpredicate callâ would/should work between two distributed systems: thatâs a tricky part. We could probably think of it like an OOP message send, but since every predicate call creates backtracking state that can later be resumed, itâs probably like every message send would have to create a state object on the machine itâs sent to, and return not a value but a reference to that state object, so that the call could be resumed at any point. Thatâs a lot of object creation and persistence. And how does garbage collection work? Does a server have to hold on to state objects forever, or can it silently let them go at some point? Does the whole thing become an instant DDOS machine?
tldr: In logic programming, the equivalent of a âfunction callâ doesnât return simple values but rather a metadata structure containing statements about those values (ie, variable mappings, and a backtracking stack). And that metadata structure is the thing that can be âparsed and iteratedâ in a way that a simple function return value canât be. That metadata-ness is what gives 1970s-era Prolog its startling ability to do very complex things with very simple definitions. But perhaps itâs just shoved the complexity under the carpet and the total amount of complexity remains the same. Still, shoving complexity around is what we do in programming, so I think itâs worthwhile to look at different ways of doing it.
Edit: Ok, on reflection, a distributed predicate call wouldnât need to create a state object on the server that could be resumed later. It could instead return something like source or object code for a query that would return the remaining values. But that then hits another question of privacy/security: we arenât really in the habit of understanding functions that routinely return, across the wire, parts of their own code and/or call stack. We like our functions to be black boxes that conceal whatever is within, and exposing the runtime internals to the world makes us nervous. Doing this probably could still be done in a secure manner, but it would need serious thinking about, because it violates a lot of our standard intuitions.