SWI-Prolog
A good free implementation of Prolog is SWI-Prolog. Licensed under the LGPL and with good bindings to other languages, it’s ideal for embedding in projects written in other languages.
Once you have SWI-Prolog installed, use it to load the program from the previous example:
$ swipl -q -f animals.pro
The -q option tells SWI-Prolog to suppress the standard welcome message and drop us straight to the prompt. We can now interrogate the program:
?- isMammal(cat). Yes
This is a fairly simple query. All it does is ask whether the isMammal/1 predicate holds (is true) for the atom cat. It does, since we defined it. We can also ask Prolog to tell us what things the isMammal/1 predicate holds for by specifying a variable instead of an atom as an argument:
?- isMammal(X). X = cat ; X = dog ; X = tiger ; X = mouse ; X = elephant ; No
After each answer, we add the semicolon (;) to get more answers. In Prolog, semicolon means "or," whereas a comma means "and." Since we defined two predicates, we can try using both of them in the same query. The following example states "find an X such that isMammal(X) holds and isDomesticated(X) also holds":
?- isMammal(X),isDomesticated(X). X = cat ; X = dog ; No
So far, we’ve only tried defining unary predicates. Now let’s add a binary predicate. The animals2.pro file in the source file for this article adds the following binary predicate to the example:
eats(tiger,cat). eats(tiger,dog). eats(cat,mouse). eats(dog,mouse). eats(tiger,elephant).
This example defines a relation between two atoms. After loading this file into Prolog, we can ask which animals are eaten by domesticated animals:
?- isDomesticated(X),eats(X,Y). X = cat Y = mouse ; X = dog Y = mouse ; No
Defining relationships between atoms allows us to use Prolog as a simple database, but it doesn’t go very far toward exploring the flexibility of the system. The next step is to try defining a predicate that depends on others. We’ll create a lowerDownTheFoodChain/2 predicate. To do this, we’ll use an inductive definition, which is a common pattern in Prolog programs. We begin with a base case:
lowerDownTheFoodChain(X,Y) :- eats(Y,X).
The colon and hyphen (:-) operator should be read as "if." This example says that the predicate lowerDownTheFoodChain(X,Y) holds—with any values of X and Y—if eats(Y,X) also holds, with the same values for X and Y. Our next step is to define the inductive step. We say that an animal, X, is lower down the food chain than another, Y, if it’s eaten by another animal that’s lower down the food chain than Y:
lowerDownTheFoodChain(X,Y) :- eats(Y,Z),lowerDownTheFoodChain(X,Z).
Once we’ve loaded this back into Prolog (see the file animals3.pro), we can ask it for which animals the lowly mouse is lower down the food chain:
?- lowerDownTheFoodChain(mouse,X). X = cat ; X = dog ; X = tiger ; X = tiger ; No
Notice that tiger is given twice as an answer here. That’s because Prolog has two possible ways of generating it as an answer; tiger eats cat eats mouse, and tiger eats dog eats mouse.