Search code examples
clojure-core.logicminikanrenreasoned-schemerknowledge-base-population

Is a "facts database" not a core feature of miniKanren?


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?


Solution

  • 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?