Learning Pilog - 2: Facts, Rules, Queries
Tags: picolisp, lisp, prolog, pilog
Published: 2022-06-06 13:32:34

This post follows the tutorial from ["Learn Prolog Now!", Chapter 1](http://www.let.rug.nl/bos/lpn//lpnpage.php?pageid=online).

The goal of this post is to introduce the basic concept and syntax of Pilog and to write some first small programs.

--------------------


### The Knowledge Base

In imperative programming, the programmer literally tells the computer what to do step by step. In logical programming it works differently: The programmer writes **everything they know into a file** and then creates a query on it. The program then deducts logically from the given facts if a statement can be true or false. The file with all the known facts is called the **knowledge base**. 

The knowledge base contains **facts** and **rules**. We can work with them by using **queries**. Facts, rules and queries are **clauses**. Let's see how they can look like.


------------------

### Facts

Facts are used to state that things are **unconditionally** true. For example, let's write the following statements in Pilog (you can start the REPL with ``pil +``):

```
: (be likes (John Susie))                   # John likes Susie 
: (be likes (@X Susie))                      # Everyone likes Susie 
: (be likes (John @Y))                       # John likes everybody 
``` 

``John`` and ``Susie`` are *constants*, ``likes`` is a *predicate*. Constants can start with an uppercase or lowercase letters (unlike in Prolog, where constants need be lowercase). 

On the other hand, ``@X`` and ``@Y`` are *variables*. All variables start with an ``@`` symbol. 

--------------------

### Rules

Rules state things that are **conditionally true** depending on the given rules. 

```
# X and Y are friends if they like each other
(be friends (@X @Y)
   (likes @X @Y)
   (likes @Y @X) )

# X hates Y if X does not like Y. 
(be hates (@X @Y)
   (not (likes @X @Y)) )

# X and Y are enemies if they don't like each other.
(be enemies (@X @Y)
   (not (likes @X @Y))
   (not (likes @Y @X)) )
```

*Let's call the predicate names with the dependencies ``friends (@X @Y)`` the "left side" and the following statements the "right side" of a clause (this terminology comes from the mathematical notation of logic terms).* 

We can read the rules as: *If the left side is true, then the right side is true.* This notation is called a [**Horn clause**](https://en.wikipedia.org/wiki/Horn_clause).  In Horn clause logic, the left hand side of the clause is the conclusion, and must be a single positive literal. The right hand side contains the premises.

In boolean logic, statements can be linked with ``and`` or ``or``. The "and" is expressed by two consecutive statements in Pilog (see examples), the "or" using the ``or`` function. The negation of a statement is ``not``.

----------------------

### Creating the knowledge base


Now let's write the following statements into a file called ``friends.l``.

```
(be likes (John Susie))
(be likes (John Pizza))
(be likes (Susie John))

(be friends (@X @Y)
   (likes @X @Y)
   (likes @Y @X) )

(be hates (@X @Y)
   (not (likes @X @Y)) )

(be enemies (@X @Y)
   (not (likes @X @Y))
   (not (likes @Y @X)) )
```

We have six clauses: three facts and three rules. This is our knowledge base. Let's open it in PicoLisp:

```
$ pil friends.l +
```

Now we can run **queries** no our knowledge base.

--------------------------

### Running queries

We can run interactive queries in the REPL with ``?``:

```
: (? (likes John Susie))
-> T
: (? (friends John Susie))
-> T
```

Susie and John are friends because they mutually like each other. 

Now let's try to query for something where we don't have any information about.

``` 
: (? (likes Susie Pizza))
-> NIL
? (? (not (likes Susie Pizza)))
-> T
```

As you can see in the following example, if something cannot be proven correct, then Pilog/Prolog conclude that it is wrong. This is called **strong negation**. See the following example:

```
: (? (enemies Susie Pizza))
-> T
```

We don't know if Susie likes Pizza, and we also don't know if the Pizza likes Susie. Therefore the logical deduction concludes they must be enemies. 

This is probably not what we intended. There are alternative ways to define *weak negation* (if something cannot be proven false, it is correct)
 that we will talk about in a later post. For the moment, keep in mind that anything not defined is considered as ``NIL``.

---------------------

### Working with variables

Now let's write a more interesting one. Let's ask Pilog to return everything that John likes. This "everything" will be represented by a **variable** in our query. Pilog recognizes variables by the ``@`` symbol at the beginning.

```
: (? (likes John @X))
 @X=Susie
```

John likes Susie: In other word, we can say that the term ``(likes John @X)`` **unifies** with the term ``(likes John Susie)`` with ``(@X = Susie)``.  If we now press ``<Enter>``, we will get additional results:

```
: (? (likes John @X))
 @X=Susie
 @X=Pizza
-> NIL
```

-----------------------

What if we ask for predicates that are not defined in the knowledge base? 

```
: (? (love John Susie))
-> NIL
```

The query returns ``NIL``. Similarly when the number of arguments is not matching to the defined predicate:

 ```
: (? (likes John Susie Pizza))
-> NIL
```

So everything we haven't defined is simply ``NIL`` (note that this is different to Prolog where we would receive errors for these cases).

------------------------

With this, we have already covered the basic Pilog syntax. In the next post we will see how the unification and proof search process actually works.

The ``friends.l``  file can be found [here](https://gitlab.com/picolisp-blog/web-applications/-/blob/main/pilog/friends.l).

---------------------

### Sources
- https://software-lab.de/doc/ref.html#pilog  
- http://www.let.rug.nl/bos/lpn//lpnpage.php?pageid=online  
- http://www.cs.trincoll.edu/~ram/cpsc352/notes/prolog/factsrules.html