4

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?

Anentropic
  • 32,188
  • 12
  • 99
  • 147
  • 1
    Note that this is also supported directly in `pldb` which is a part of `core.logic`. Not adding this as an answer since this is an old question. – Peeyush Kushwaha Nov 29 '20 at 08:18

1 Answers1

4

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?

amalloy
  • 89,153
  • 8
  • 140
  • 205
Jason Hemann
  • 327
  • 1
  • 12
  • Ah cool, I had seen examples like this and wondered if it was somehow analogous. I think this makes it clear - there is no "facts db" but you can hard-code facts into the relation definition. The Clojure approach is nice I think as you can treat the relation like a db table and assert/retract facts from it at runtime, much like in Prolog. Thanks! – Anentropic Jul 20 '20 at 14:08
  • 1
    @Anentropic, It's not quite as nice, but you could, if you choose, redefine the relation itself during the program's execution. I believe that would allow you to express roughly the behavior of assert and retract behavior. By TRS 2/e, I meant https://mitpress.mit.edu/books/reasoned-schemer-second-edition – Jason Hemann Jul 20 '20 at 17:33
  • so, our whole global name space is our knowledge base. still, in Prolog I can also enumerate the available predicates, e.g. calling `current_predicate(A/2), atom_chars(A,[a|_]).` to list all binary predicates whose names start with `a`... – Will Ness Aug 04 '20 at 21:01
  • Too true! I this starts to run up against the limitations of supporting implementation via shallow embedding. The host language might provide some way to get at these same data (e.g.`(oblist)` in Chez Scheme). There are some all-around limitations of shallowly-embedding higher-order predicates as host-lang procedures. – Jason Hemann Aug 05 '20 at 22:19