@CapelliC mentioned (thank you!) that logtalk supports the idea of local knowledge bases. Since I am on a system (NixOS) for which a predefined installer doesn’t work, and I read that logtalk is also available as a swipl pack, I’d like to ask people using logtalk here if anyone did that (just install pack(logtalk) and what the limitations are.
In the end, Logtalk compiles to Prolog, so it doesn’t add anything that really can’t be done. It depends what you mean with local knowledge bases. SWI-Prolog allows for
- Putting predicates in a name space (module)
- Making predicates per-thread (thread_local/1)
- Make predicate changes invisible and reversible (snapshot/1)
AFAIK, Logtalk allows for predicates local to an object, which is the same encapsulation as modules provide. Logtalk does provide inheritance. Of course, you can make that in vanilla Prolog (otherwise Logtalk could not have it either), but that is a little harder.
Yes, I agree that what Logtalk does is basically modules but with a better interface (It seems to me that the whole notion of Logtalk object
is this idea of local knowledgebase). So it’s just writing a library with a prolog api I’m happy with. One could start with the idea:
local_assert(World, Pred) :-
term_hash(World, Hash),
atomic_concat('local_knowledge_', Hash, Local),
assertz(Local:Pred).
local_get(World, Pred) :-
term_hash(World, Hash),
atomic_concat('local_knowledge_', Hash, Local),
Local:Pred.
In this way one could assert and retrieve facts from ‘worlds’ indexed by arbitrary terms.
For example, one can now say:
local_assert(w(1), foo(a)).
to express that in the world named w(1)
, foo(a)
is true. I can also query this information via
local_get(w(1), foo(X))
But
Say now that I want to express (globally) the rule:
bar(X) :- foo(X), baz(X).
where baz(X)
is a predicate that is true for all worlds (not world-specific).
It seems to me that I have a couple of options:
- meta-interpret the call, so when I do
local_knowledge(w(1), bar(X))
only the predicates that belong tow(1)
(in this casefoo/1
) are module-qualified. This is quite cumbersome, and I’d avoid meta-interpretation if possible. - include a copy of the
bar/1
predicate inbaz
so that I can simply dolocal_knowledge(w(1), bar(X))
in the context ofw(1)
. But should I also includebaz
? - module import somehow, but I don’t think it solves the issue in 2 (?)
- probably there’s a smarter idea?
What would you suggest in this case @jan? What strikes you as the better solution here?
I just discovered I could write in user
:
Local:bar(X) :- Local:foo(X), baz(X).
but it feels like a lot of magic. Would you advise against this?