A few questions about multi-clause rules

Hi. I’ve got a data model that looks similar to this:

(def datoms
  [[boat :boat/captain alice]
   [boat :boat/passenger bob]
   [boat :boat/passenger doris]
   [boat :boat/passenger eddy]])

…and I’m implementing a rule which accepts a boat and returns all of the people on it.

My initial impulse is that the clearest way to express this is with a multi-clause rule, like this:

(def rules
  '[[(inhabitant ?boat ?person)
     [?boat :boat/captain ?person]]
    [(inhabitant ?boat ?person)
     [?boat :boat/passenger ?person]]]])

…but I’m having a little trouble understanding the execution semantics of these types of rules and what I should expect for performance. (For one thing, it wasn’t immediately obvious to me that datomic would execute both of these clauses and merge the results of each into one big set, versus selecting the first clause that did not fail or something.) Is this a recommended approach for this kind of thing?

I’m also wondering if there is a performance difference between this approach and just using an (or) to do some joining, as in:

(def rules
  '[[(inhabitant ?boat ?person)
     (or
       [?boat :boat/captain ?person]
       [?boat :boat/passenger ?person])]])

The actual model is a lot more complex than this, with several attributes involved and some data that needs to be in the set which is accessed through traversing some other entities, etc.

Rules with the same name are implicitly or, i.e., ?person will be bound to persons that are the value of :boat/captain or :boat/passenger.

I prefer the first approach.

1 Like