I have been playing around with miniKanren, trying to understand it by converting very basic Prolog tutorials into it.
I use Python habitually so I started with the LogPy library, which has since been forked and improved upon as a lib actually called miniKanren
From the example given in the lib's README we can see:
>>> from kanren import Relation, facts
>>> parent = Relation()
>>> facts(parent, ("Homer", "Bart"),
... ("Homer", "Lisa"),
... ("Abe", "Homer"))
>>> run(1, x, parent(x, "Bart"))
('Homer',)
This trivially corresponds to things you might see at the start of Prolog tutorial e.g.:
% facts.pl
parent(homer, bart).
parent(homer, lisa).
parent(abe, homer).
?- consult('facts')
true.
?- parent(X, bart).
X = homer
I was happy with this...
Later I found myself reading more of the MiniKanren literature (in the general sense, not the Python lib) and I realised I hadn't seen any examples using a facts database this way, or mention of one.
Have I missed it? Or this is not actually a feature of MiniKanren a la "A Reasoned Schemer"?
Where I did find such a thing is in the Clojure core.logic
implementation, where there is this:
https://github.com/clojure/core.logic/wiki/Features#simple-in-memory-database
It works in a very similar way, albeit nicer than the python one because the db is a distinct entity rather than a global var in the lib.
Did the python lib just borrow a non-kanren idea from core.logic
? Are there other MiniKanren implementations which have something similar? Or a different approach altogether?
This is an awesome question, and I think a great example to have around. It's supported but maybe not so neatly and straightforwardly as you're used to. We can describe a facts db, on a relation-by-relation basis, in the same style that you'd expect to write a recursive Kanren relation. I'm borrowing concrete syntax from The Reasoned Schemer, 2nd edition.
(defrel (parento f c)
(conde
((== f 'homer) (== c 'bart))
((== f 'homer) (== c 'lisa))
((== f 'abe) (== c 'homer))))
(defrel (stonecuttero p)
(conde
((== p 'abe))
((== p 'lenny))
((== p 'carl))
((== p 'glumplich))))
> (run* p (fresh (o) (stonecuttero p) (parento p o)))
(abe)
If your host language has a nice macro system, then you could probably write it succinctly, and expand out into a form like this.
Does that help?