Taking union of two rule clauses

I’m working on a Prolog program that will model Google Zanzibar style rewrite rules. The program I have looks like the following:

:- dynamic(config/4).
:- dynamic(tuple/6).

:- assertz(config("document", "editor")).
:- assertz(config("group", "member")).

:- assertz(tuple("document", "1", "editor", "user", "jon")).
:- assertz(tuple("document", "1", "editor", userset("group", "eng", "member"))).
:- assertz(tuple("group", "eng", "member", "user", "andres")).

checkWR(ObjectType, ObjectId, Relation, UserObjectType, UserObjectId) :-
    tuple(ObjectType, ObjectId, Relation, UserObjectType, UserObjectId),
  tuple(ObjectType, ObjectId, Relation, userset(UsersetObjectType, UsersetObjectId, UsersetRelation)),
  check(UsersetObjectType, UsersetObjectId, UsersetRelation, UserObjectType, UserObjectId).

check(ObjectType, ObjectId, Relation, UserObjectType, UserObjectId) :-
    config(ObjectType, Relation),
    checkWR(ObjectType, ObjectId, Relation, UserObjectType, UserObjectId), !.

The behavior I’d like to see is the following:

?- check("document", "1", "editor", "user", "jon").
true

?- check("document", "1", "editor", "user", "andres").
true

?- check("document", "editor", userset("group", "eng", "member")).
true

The idea is that direct relationships such as document:1#editor@user:jon or relationships through indirect relationships such as document:1#editor@group:eng#member and group:eng#member@user:andres should lead to a path.

The problem I’m having is expressing the effective union in checkWR. What I really want is the outcome or union of either

tuple(ObjectType, ObjectId, Relation, UserObjectType, UserObjectId),

or

tuple(ObjectType, ObjectId, Relation, userset(UsersetObjectType, UsersetObjectId, UsersetRelation)),
check(UsersetObjectType, UsersetObjectId, UsersetRelation, UserObjectType, UserObjectId).

If a direct fact/relationship exists or if a userset relationship exists and the check of that is solvable, then checkWR should yield a solution.

Can someone help me express this? I tried with a union rule composing these two with the HEAD and TAIL, but that didn’t work for me.

Actually, I think I figured it out. I was missing a simple understanding of Prolog conjunctions and disjunctions. The following seems to work:

checkWR(ObjectType, ObjectId, Relation, UserObjectType, UserObjectId) :-
  tuple(ObjectType, ObjectId, Relation, UserObjectType, UserObjectId);
  tuple(ObjectType, ObjectId, Relation, userset(UsersetObjectType, UsersetObjectId, UsersetRelation)),
  check(UsersetObjectType, UsersetObjectId, UsersetRelation, UserObjectType, UserObjectId).

e,g. use the disjunction operator ; between the two predicates.

You can also write it as two clauses (there’s an implicit “or” between clauses):

checkWR(ObjectType, ObjectId, Relation, UserObjectType, UserObjectId) :-
  tuple(ObjectType, ObjectId, Relation, UserObjectType, UserObjectId).
checkWR(ObjectType, ObjectId, Relation, UserObjectType, UserObjectId) :-
  tuple(ObjectType, ObjectId, Relation,
        userset(UsersetObjectType,UsersetObjectId,UsersetRelation)),
  check(UsersetObjectType, UsersetObjectId, UsersetRelation, UserObjectType, UserObjectId).
3 Likes

That’s an implicit “AND” actcherly :slight_smile:

This is a weird thing, exposing the difference between the declarative semantics of Prolog and the imperative way of thinking about it. Formally speaking, clauses are disjunctions of literals, logic programs are conjunctions of (definite) clauses. But, in practice, it does feel like a set of Prolog clauses is an “OR”… <_<