Operators for a Controlled Natural Language
First we'll need a code base, let's start with some classic family tree facts and rules. We'll keep this here for later comparison.
isMale(bob).
isParentOf(pam, bob).
isParentOf(tom, bob).
isParentOf(tom, liz).
isParentOf(bob, ann).
isParentOf(bob, pat).
isParentOf(pat, jim).
isGrandParentOf(GP, GC) :-
isParentOf(GP, P),
isParentOf(P, GC).
isGrandFatherOf(GF, GC) :-
isMale(GF),
isGrandParentOf(GF, GC).
isSiblingOf(S1, S2) :-
isParentOf(P, S1),
isParentOf(P, S2),
S1 \= S2.
isGrandFatherOf(Who, jim), isSiblingOf(Who, liz).
op(+Precedence, +Type, :Name)
Let's declare our operators! Precedence determines the order of evaluation, SWI-Prolog have the precedence for built-in operators in their docs. Type is how the operator is evaluated: xf, yf, xfx, xfy, yfx, fy or fx
, where f is a functor, x is an argument and y is an argument or lower precedence functor.
Let's turn our predicates into operators and enjoy querying in a somewhat more natural way. Precedence for and
as well as or
is taken to be the same as that of ,
and ;
. Note how we can then use the operators in our declarations of facts and rules.
:- op(1000, xfx, and).
:- op(1100, xfx, or).
:- op(500, xf, isMale).
:- op(400, xfx, isParentOf).
:- op(400, xfx, isGrandParentOf).
:- op(400, xfx, isGrandFatherOf).
:- op(400, xfx, isSiblingOf).
and(A, B) :-
A, B.
or(A, B) :-
A ; B.
bob isMale.
pam isParentOf bob.
tom isParentOf bob.
tom isParentOf liz.
bob isParentOf ann.
bob isParentOf pat.
pat isParentOf jim.
isGrandParentOf(GP, GC) :-
GP isParentOf P and P isParentOf GC.
isGrandFatherOf(GF, GC) :-
GF isMale and GF isGrandParentOf GC.
isSiblingOf(S1, S2) :-
P isParentOf S1,
P isParentOf S2,
S1 \= S2.
Who isGrandFatherOf jim and Who isSiblingOf liz.
The Good, the bad and the ugly
Just because we can do something, doesn't mean we should! Personally I appreciate this representation, but understand that some people prefer the standard syntax. Declaring operators is especially useful when working within a domain that already uses them, such as logic, especially if your implementation of Prolog supports unicode. However, the most common use seems to be to develop a structure for passing data around.