As we have seen, in certain cases (for example, when performing arithmetic) Prolog lets us use operator notations that are more user-friendly than its own internal representations. Indeed, as we shall now see, Prolog even has a mechanism for letting us define our own operators. In this section we’ll first take a closer look at the properties of operators, and then learn how to define our own.
Properties of operators
Let’s start with an example from arithmetic. Internally, Prolog uses the expression is(11,+(2,*(3,3))) , but we are free to write the functors * and + between their arguments, to form the more user-friendly expression 11 is 2 + 3 * 3 . Functors that can be written between their arguments are called infix operators. Other examples of infix operators in Prolog are :- , --> , ; , ’,’ , = , =.. , == and so on. In addition to infix operators there are also prefix operators (which are written before their arguments) and postfix operators (which are written after). For example, ?- is a prefix operator, and so is the one-place - which is used to represent negative numbers (as in 1 is 3 + -2 ). An example of a postfix operator is the ++ notation used in the C programming language to increment the value of a variable.
When we learned about arithmetic in Prolog, we saw that Prolog knows about the conventions for disambiguating arithmetic expressions. So when we write 2 + 3 * 3 , Prolog knows that we mean 2 + (3 * 3) and not (2 + 3) * 3 . But how does Prolog know this? Because every operator has a certain precedence. The precedence of + is greater than the precedence of * , and that’s why + is taken to be the main functor of the expression 2 + 3 * 3 . (Note that Prolog’s internal representation +(2,*(3,3)) is not ambiguous.) Similarly, the precedence of is is higher than the precedence of + , so 11 is 2 + 3 * 3 is interpreted as is(11,+(2,*(3,3))) and not as the (nonsensical) expression +(is(11,2),*(3,3)) . In Prolog, precedence is expressed by a number between 0 and 1200; the higher the number, the greater the precedence. To give some examples, the precedence of = is 700, the precedence of + is 500, and the precedence of * is 400.
What happens when there are several operators with the same precedence in one expression? We said above that Prolog finds the query 2 =:= 3 == =:=(2,3) confusing. It doesn’t know how to bracket the expression: Is it =:=(2,==(3,=:=(2,3))) or is it ==(=:=(2,3),=:=(2,3)) ? The reason Prolog is not able to decide on the correct bracketing is because == and =:= have the same precedence. In such cases, explicit bracketings must be supplied by the programmer.
What about the following query though?
Does Prolog find this confusing? Not at all: it deals with it happily and correctly answers X = 9 . But which bracketing did Prolog choose: is(X,+(2,+(3,4))) or is(X,+(+(2,3),4)) ? As the following queries show, it chose the second:
Here Prolog has used information about the associativity of + to disambiguate: + is left associative, which means that the expression to the right of + must have a lower precedence than + itself, whereas the expression on the left may have the same precedence as + . The precedence of an expression is simply the precedence of its main operator, or 0 if it is enclosed in brackets. The main operator of 3 + 4 is + , so that interpreting 2 + 3 + 4 as +(2,+(3,4)) would mean that the expression to the right of the first + has the same precedence as + itself, which is illegal. It has to be lower.
The operators == , =:= , and is are defined to be non-associative, which means that both of their arguments must have a lower precedence. Therefore 2 =:= 3 == =:=(2,3) is an illegal expression, since no matter how you bracket it you’ll get a conflict: 2 =:= 3 has the same precedence as == , and 3 == =:=(2,3) has the same precedence as =:= .
The type of an operator (infix, prefix, or postfix), its precedence, and its associativity are the three things that Prolog needs to know to be able to translate user-friendly (but potentially ambiguous) operator notations into Prolog’s internal representation.
In addition to providing a user-friendly operator notation for certain functors, Prolog also lets you define your own operators. So you could, for example, define a postfix operator is_dead ; then Prolog would allow you to write zed is_dead as a fact in your database instead of is_dead(zed) .
Operator definitions in Prolog look like this:
As we mentioned above, precedence is a number between 0 and 1200, and the higher the number, the greater the precedence. Type is an atom specifying the type and associativity of the operator. In the case of + this atom is yfx , which says that + is an infix operator; the f represents the operator, and the x and y the arguments. Furthermore, x stands for an argument which has a precedence which is lower than the precedence of + and y stands for an argument which has a precedence which lower or equal to the precedence of + . There are the following possibilities for type:
|infix||xfx , xfy , yfx|
|prefix||fx , fy|
|suffix||xf , yf|
So your operator definition for is_dead might be as follows:
Here are the definitions for some of the built-in operators. You can see that operators with the same properties can be specified in one statement by giving a list of their names (instead of a single name) as the third argument of op .
|:- op( 1200, xfx, [ :-, --> ]).|
|:- op( 1200, fx, [ :-, ?- ]).|
|:- op( 1100, xfy, [ ; ]).|
|:- op( 1000, xfy, [ ’,’ ]).|
|:- op( 700, xfx, [ =, is, =.., ==, \==,|
|=:=, =\=, <, >, =<, >= ]).|
|:- op( 500, yfx, [ +, -]).|
|:- op( 500, fx, [ +, - ]).|
|:- op( 300, xfx, [ mod ]).|
|:- op( 200, xfy, [ ^ ]).|
One final point should made explicit. Operator definitions don’t specify the meanings of operators, they only describe how they can be used syntactically. That is, an operator definition doesn’t say anything about when a query involving this operator will evaluate to true, it merely extends the syntax of Prolog. So if the operator is_dead is defined as above, and you pose the query zed is_dead , Prolog won’t complain about illegal syntax (as it would without this definition) but will try to prove the goal is_dead(zed) , which is Prolog’s internal representation of zed is_dead . And this is all operator definitions do — they just tell Prolog how to translate a user-friendly notation into real Prolog notation. So, what would be Prolog’s answer to the query zed is_dead ? It would be no , because Prolog would try to prove is_dead(zed) , but would not find any matching clause in the database. But suppose we extended the database as follows:
Now Prolog would answer yes to the query.