Skip to content

Comment on Turning the IDE Inside Out with Datalog

Comments

Nice article and great idea, but as is traditional there are some slight fudges of what Datalog is or what Horn clauses are etc, that I'd like to unfudge, slightly. It's Sunday! What better than to start our day with a very quick and almost not completely fudgy intro to logic programming? Le'ts go!

To begin with, Datalog is not a "cousin" of Prolog as stated in the section "Interlude: Brief Intro to Datalog". Datalogs (there are many variants!) are subsets of Prolog. For example, a typical datalog is the language of definite clauses with no function symbols [¹] and with no negation as failure [²]. Another datalog may allow only the cons function in order to handle lists; etc.

Otherwise the syntax of datalog is identical to Prolog, but there is a further difference, in that Prolog is evaluated from the "top down" whereas Datalog is evaluated from the "bottom up". What that means is that given a "query" (we'll come to the scare quotes in a moment) Prolog will try to find "rules" whose heads unify with a literal in the query (A literal is an atom, or the negation of an atom; "p(χ,α)" is an atom.) whereas datalog will first generate the set of all ground atoms that are consequences of the program in the context of which the query was made, then determine whether the atoms in the query are in the set of consequences of the program [³]. The reason for the different execution model is that the bottom-up evaluation is guaranteed to terminate [⁴] whereas Prolog's top-down evaluation can "go infinite" [⁵]. There is of course another, more subtle difference: Prolog can "go infinite" because of the Halting problem, from which datalog does not suffer because, unlike Prolog, it does not have Universal Turing Machine expressivity [⁶].

So in short, datalog is a restricted subset of Prolog that has the advantage of being decidable, while Prolog in general is not, but is also incomplete while Prolog is complete [⁷].

Now, the other slight fudge in the article is about "rules", "facts" and "queries". Although this is established and well-heeled logic programming terminology, it fudges the er fact that those three things are the same kind of thing, namely, they are, all three of them, Horn clauses [⁸].

Specifically, Horn clauses are clauses with a single positive literal.

Crash course in FOL: an atom is a predicate symbol followed by a set of terms in parentheses. Terms are variables, functions or constants (constants are functions with 0 arity, i.e. 0 arguments). A literal is an atom, or the negation of an atom. A clause is a disjunction of literals. A clause is Horn when it has at most 1 positive literal. A Horn clause is a definite clause when it has exactly 1 positive literal.

The following are Horn clauses:

  ¬P(χ) ∨ ¬P(υ)
  P(χ) ∨ ¬Q(χ)
  Q(α)
  Q(β)
In logic programming tradition, we write clauses as implications (because ¬A ∨ B ≡ A → B) and with the direction of the implication arrow reversed to make it easier to read long implications with multiple premises. So the three clauses above are written as:
  ←P(χ), P(υ) (a)
  P(χ) ← Q(χ) (b)
  Q(α)←       (c)
  Q(β)←       (d)
And those are a "query", (a), a "rule", (b) and two "facts", (c) and (d).

Note that (b,c,d) are definite clauses (they have exactly one positive literal, i.e. their head literal, which is what we call the consequent in the implication). Facts have only a positive literal; I like to read the dangling implication symbol as "everything implies ...", but that's a bit idiosyncratic. The bottom line is that definite clauses with no negative literals can be though of as being always true, hence "facts". Queries, i.e. Horn clauses with no positive literals, are the opposite: "nothing implies" their body literals (my idiosyncratic reading) so they are "always false". Queries are also called "goals". Finally, definite clauses with both positive and negative literals can be thought of as "conditionally true".

Prolog and datalog programs are written as sets of definite clauses, i.e. sets of "facts" and "rules". So, when we want to reason about the "facts" and "rules" in the program, we make a "query". Then, the language interpreter, which is a top-down resolution theorem prover [⁹] in the case of Prolog, or bottom-up fixpoint calculation in the case of datalog [¹⁰], determines whether our "query" is true. If the query includes any variables then the interpreter also returns evey set of variable substitutions that make the query true.

In the example above, (a) has two variables, χ and υ and evaluating (a) in the context of (b,c,d) would return a "true" result with the variable substitution {χ/α,υ/β}, i.e. (a) is true iff χ = α and υ = β.

And that's how Horn clauses and definite clauses become "rules", "facts" and "queries".

Next time: how the leopard got its stripes and the hippopotamus learned to love the first order predicate calculus.

_________________

[¹] This is my (first) slight fudge because constants are also functions, with 0 arguments. So, to be formal, the typical datalog above has "no functions of arity more than 0".

[²] Negation-as-failure makes a language non-monotonic, in the sense that introducing new "facts" can change the meaning of a theory, i.e. a program.

[³] So, its Least Herbrand Model, or its Least Fix-Point (LFP).

[⁴] Because it finds the LFP of the query and the program.

[⁵] Unless evaluated by SLG resolution, a.k.a. tabling, similar to memoization.

[⁶] Although higher-order datalogs, that allow for predicate symbols as terms of literals have UTM expressivity, indeed a UTM can be defined in a higher-order datalog fragment where clauses have up to two body literals with at most two arguments:

  utm(S,S) ← halt(S).
  utm(S,T) ← execute(S,S₁), utm(S₁,T).
  execute(S,T) ← instruction(S,P), P(S,T).
Originally in:

Tärnlund, S.-A. (1977). Horn clause computability. BIT Numerical Mathematics, 17(2), 215–226.

[⁷] Less fudgy, definite programs are refutation complete under SLD resolution, meaning that any atom that is entailed by a definite program can be derived by SLD resolution. A definite program is a set of definite clauses, explanation of which is coming right up.

[⁸] Long time ago, I explained this to a colleague who remarked that all the nice syntactic elegance in query languages falls apart the moment you try to make a query, which usually has a very different syntax than the actual rows of the tables in the database. So I said "that's the point! Queries are also Horn clauses!" and his immediate remark was "That just blew my mind". It's been so long and I'm so used to the idea that I haven't a clue whether this is really mind blowing. Probably, being my usual excited self, I just said it in a way that it sounded mind blowing (gesticulating widely and jumping up and down enthusiastically, you can picture the scene) so my colleague was just being polite. That was over drinks at the pub after work anyway.

[⁹] Resolution is an inference rule that allows the derivation of new atoms from a set of clauses. In theorem proving it's used to refute a goal clause by deriving the empty clause, □. Since a goal is a set of negated literals, refuting the goal means basically that the negated literals are true. So our query is true in the context of our program.

[¹⁰] Datalog's bottom-up evaluation uses something called a TP operator. It basically does what I said above, starts with the ground atoms in a program and then derives the set of consequences of the clauses in the program. In each iteration, the set of consequences are added to the program and the process is repeated, until no new consequences are derived. As stated above, the process is guaranteed to complete because every datalog definite program has a least fixpoint, which is also its Least Herbrand Model (we won't go into Herbrand Models and Herbrand interpretations, but, roughly, an LHM is the smallest set of atoms that make the union of a definite program and a goal true). A more complete introduction to LHMs and LFPs and how they are used in bottom-up evaluation for datalog can be found here:

https://www.doc.ic.ac.uk/~mjs/teaching/KnowledgeRep491/Fixpo...

Sorry, but you are also fudging things.

Datalogs (there are many variants!) are subsets of Prolog.

No. First of all, this is not true syntactically. There are Datalogs that allow non-Horn clauses with several terms in a goal head:

    a, b :- c.
This is not allowed in Prolog. So (such) Datalogs are not subsets of Prolog syntactically.

Second, it is not true semantically either, not even for the common syntactic subset. Consider:

    ancestor_of(Parent, Child) :-
        child_of(Child, Parent).
    ancestor_of(Ancestor, Person) :-
        ancestor_of(Ancestor, Parent),
        child_of(Person, Parent).
This is left-recursive, so typical queries will not terminate in Prolog, i.e., have no finite solutions. But as you say, Datalogs are decidable and any query terminates, so you will get solutions, which is different semantics from Prolog. So it's not meaningful to say that Datalog is a semantic subset of Prolog.

Datalog and Prolog are like C++ and Java: One is an extension of a subset of the other, or equivalently, there is a non-empty common subset with similar-ish semantics. This is not a very useful statement? I agree! But it is what it is. They are different languages.

Yes, you're right and I'm also fudging things- but didn't I say that upfront? I start my comment by announcing an "almost not completely fudgy intro to logic programming"!

More seriously, you're right about syntax so thanks for the correction.

But, regarding semantics, the ancestor_of/2 program above can terminate in Prolog, evaluated by SLG resolution, as per my footnote 5. There are still situations where Prolog will not terminate when evaluating a normal program even under SLG resolution, but left recursion is not one of those.

Edit: also, if a Datalog program is also Prolog, and assuming that the program terminates under Prolog, then Prolog and Datalog will both compute its LHM. So it makes sense to say that the two languages are semantically at least very similar and to explain one in terms of the other. They are both much closer than what each is to ASP, for example. It really depends on what assumptions one makes- and that's where the "fudging" comes in.

Anyway, thanks for the correction. I sure could have done a better job of that comment. Did you find anything other that was very wrong in my comment? I'd appreciate it if you pointed it out.

Did you find anything other that was very wrong in my comment?

I'm not sure about the statement that "Datalog is evaluated from the "bottom up". [...] datalog will first generate the set of all ground atoms that are consequences of the program in the context of which the query was made, then determine whether the atoms in the query are in the set of consequences of the program". I think it's true that Datalog behaves as if it were evaluated like this, but AFAIK Datalog systems can do lots of very aggressive optimizations that change the actual evaluation. I'm not an expert on Datalog.

Anyway, I was mostly dissatisfied with the general thrust of the comment, trying to establish a "subset" relationship. I think the article's "cousins" comment is fair. Prolog is older and influenced Datalog very much. Prolog is also Turing complete, so it is strictly more powerful. But Datalog's semantics allow some optimizations that wouldn't be possible in Prolog, so it can be a lot faster on appropriate classes of problems.

Thanks for this additional comment.

I thought more of your earlier comment about how "p,q:- r" is Datalog. I accepted this because I figured you know what you're talking about but, to be honest, I don't know what you're talking about. My understanding is that without definite clauses (and "p,q:-r" is not definite) there are no fixpoint semantics and without fixpoint semantics there is no guarantee of program termination.

So I have to ask: where does this information come from? Could you point me to a source? To be honest, I suspect that I am confused because of a lack of an ISO standard for Datalog. Even Prolog, with an ISO standard, has various extensions like, if memory serves, B-Prolog (which includes OOP elements). If Datalog has no commonly recognised standard, then basically anything can be called "Datalog" as long as someone, somewhere, can recognise it as Datalog. Is that the case here? If so, that would clear my confusion.

It would also make more sense for me to say that "Datalog is a subset of Prolog", at least in terms of syntax. In that case I'd have to clarify that I'm talking aout a simple, commonly accepted language fragment of definite clauses with at most one function symbol, which I think everyone would readily recognise as "Datalog" without much fuss.

I confess that the extent of my knowledge about Datalog comes from conversations with (senior) colleagues and not directly from original sources. Of course, original sources go way, way back (but obviously not as back as original Prolog sources and I'm familiar with those, so that's not a complete excuse). In any case, I had a look at, e.g. "What you always wanted to know about Datalog (and never dared to ask)" by Geri, Gotlob and Tanca (also in the bibliography section of the wikipedia article on databases).

The article, which is from 1989 and clearly addressed at the databases community (rather than the logic progamming, or AI community) states that Datalog is "in many respects a simplified version of general Logic Programming", referencing J. W. Lloyd for the latter. Since it's 1989, "Logic Programming" clearly means Prolog (given that it's too early for, e.g. ASP).

Further, the article makes it explicity that "In the formalism of Datalog both facts and rules are represented as Horn clauses of the general shape ..." and gives an example of a definite program clause. Later, the article states "From the syntactic point of view, Datalog is a subset of Prolog" (but then goes on to point out the difference in semantics).

Finally, the article does agree with you that is is possible to evaluate Datalog programs in a "top-down" fashion, using the query-subquery algorithm which is, from what I can tell, backwards chaining implemented by breadth-first search. So the semantics of Datalog can be different than Prolog's. My mistake I think is in equating the use of a TP operator (which is "bottom-up") to Datalog execution, always.

So I have to ask: where does this information come from?

Half-remembered university courses from >= 10 years ago, I'm afraid. I now think I was probably thinking of ASP, not Datalog. See for example the "Disjunctive logic programs" section of https://www.cs.uni-potsdam.de/~torsten/Lehre/ASP/Folien/asp-.... Since the syntax is very Prolog/Datalog-like, I probably got mixed up. My bad, sorry. The first few sources on Datalog I looked at only talked about Horn clauses, not ones with more heads.

My understanding is that without definite clauses (and "p,q:-r" is not definite) there are no fixpoint semantics and without fixpoint semantics there is no guarantee of program termination.

Skimming https://en.wikipedia.org/wiki/Answer_set_programming and https://en.wikipedia.org/wiki/Stable_model_semantics I get the impression that even disjunctive logic programs appear to always terminate, though the complexity might be daunting.

Ah, phew, OK. I was really confused by that. No worries, I'm still grateful for your comments :)

Yes, ASP is not Horn so it has rules with multiple literals in the head. "Choice rules", writen as {s,t}:- p. Also, like you say, I believe it doesn't suffer from Prolog's non-termination, again because unlike Prolog it's not Turing-complete. But in any case ASP is based on stable model semantics, not fixpoint semantics.

However I'm really very far from being anything like an expert in ASP! I really should learn a bit of it because it's actually necessary in my research.

Hi, author here. Thank you so much for this unfudging! I knew I was fudging things up a bit, and I didn't really understand the relationships between Prolog, Datalog, Horn clauses, and first-order logic. This clears things up a lot, though I think I'll need to read it a couple more times for it to sink in! Thanks again.

Thank you for your article! Also note tom_mellior's corrections above, particularly about datalogs with two head literals. I'm fudging things up also!

Hey dang, I added some whitespace twice (I needed to add a newline above the first set of Horn clauses to make it into a blockquote) right after I made my comment and the comment "fell" maybe five places. Is that as it should be?

Edit: I did make two or three more changes after adding the whitespace that weren't whitespace, but single characters.

(Not dang!) New comments start at the top, so they have a chance to get upvotes, and quickly drop down if they don't get votes relative to other comments. It sounds normal.

Yes, but a new comment doesn't immediately sink five places, unless the comments above it are very highly upvoted - or the comment is very large. That is, there is a grace period that keeps a comment on top of a thread for a little while, presumably to give it a chance to be read. But, I've noticed that large comments are automatically pushed down into a thread so the grace period for them is much reduced (and probably weighted by the size of the comment).

That's not absolute, because I remember it stated elesewhere by dang that larger comments are considered more likely to be more substantial (for sound reasons). But it seems that holds up to a certain point. There is probably some kind of comment size range, of maximum and minimum comment size, outwith which a comment is pushed down.

There are other factors, also that cause a comment to automatically sink. For example, I have the (admittedly very bad) habbit of editing and re-editing my comments over and over again. I've noticed that if I do this in a sufficiently large comment, the comment immediately sinks when I refresh the thread page after an edit. If the comment is smaller, it can take quite a bit more editing. I think this behaviour is also weighted by user karma or time since account creation because my comments started sinking less often as my karma increased. Or maybe the rules of the site changed in the meantime.

Bottom line: large comments with some edits sink like lead.

I do quite a few little edits too, but I don't often write long comments, so I haven't seen this behaviour.

AboutSource Built by g1lg1l

Hackerly is an independent reader for Hacker News, built on the public HN API. Not affiliated with Y Combinator.