~1
                       LOGIC PROGRAMMING LESSON 7


In this lesson, I describe an entirely different way of looking at
Prolog from the Prolog-as-logic view I have taught so far.

When we write programs, we not only need to make correct deductions; we
must also be able to display what we've deduced, perhaps after
reformatting or translating it in some way. For example, in a copy of
Eliza, we must be able to display messages like

      WHY DO YOU SAY YOU HATE YOUR FATHER?

Conversely, we must be able to read sentences typed at the terminal and
turn them into facts in the knowledge base from which we can generate
output.

~2
So we must be able to:

(1) Display output on the terminal, or print it on a printer, or send it
    to a file.

(2) Read input from the terminal, or from a file.

(3) Place new facts into the knowledge base, and remove unwanted ones,
    as a program runs.

~3
We can do these in Prolog. But we need to use it in a different way from
what we've already learnt. Prolog can be used, not only as a language
for expressing facts, but also as a language for controlling the
computer: as a language for giving commands, and stating in what order
these commands are to be obeyed. When used like this, it resembles
conventional languages like Basic and Pascal. We call this part of
Prolog the CONTROL LANGUAGE, as distinct from the LOGIC LANGUAGE of
previous lessons.

Unfortunately, the syntax of the CONTROL LANGUAGE is identical to that
of the LOGIC LANGUAGE. This means that, merely by looking at the form of
a program, you can't tell whether it's to be treated as commands or
facts. It also confuses programmers who forget which context they're
working in.

~4
I shall now introduce Prolog as a control language. Please put to the
back of your mind its use for logic. Eventually, you'll be skilled
enough to switch quickly from one view to the other; but if you try
doing so now, it will only confuse you. Bring to the front of your mind
anything you know about other programming languages, so you can make
analogies between them and Prolog as a control language.

Before continuing, please give the command "prolog." to switch your
Tutor into Prolog mode.

~5
Now, type these questions.

      write(1)?
      write(fred)?
      write(a_very_long_name)?

~6
In each case, Prolog displayed "write"s argument. In general, "write"
takes one argument, and commands Prolog to display it on the terminal.
"write" acts like "write" in Pascal, or "print" in Basic.

~7
Suppose we want to write messages that contain spaces. How do we do it?

By using the quote marks '' around the message:

      write('Hello world')?
      write('WHY DO YOU SAY YOU HATE YOUR FATHER?')?

Try these. What happens if you omit the quotes?

~8
All these arguments:
      fred              a_very_long_name
      'Hello world'     'WHY DO YOU SAY YOU HATE YOUR FATHER?'
are atoms. At the end of Lesson 4, we saw that atoms like fred and
a_very_long_name do not need quotes. But to use atoms that start with a
capital letter, we need quotes, otherwise Prolog would think they are
variables. And to use atoms that contain spaces or other funny
characters, we again need quotes. If the message 'Hello world' did not
have quotes, Prolog would think it was a variable followed by an atom,
and not one single atom.

Try this again by comparing
      write(Hello world)?
      write('Hello world')?

~9
With the first one, no quotes, you will have got a syntax error. Prolog
has tried to treat Hello and world as two different items, and got upset
because of the space between them.

In the second one, the quotes prevent Prolog analysing what's inside. It
just skips from the opening quote to the closing one, and treats
everything between as one item. Note that Prolog doesn't display the
quotes when commanded to write.

~10
Now, try

      write(X)?
      write('X')?

~11
The first will have displayed something like _1. That's because Prolog
treats the capital X as a variable. When you write it as above, it has
no value, and conventionally, variables without a value are displayed as
an underline followed by a number (the number says something about where
their contents is stored in the computer).

To insulate the X from being treated as a variable, and make it into
something that's printed just as it is, we must again quote it.

~12
So - when using "write", if you want to write a message, and it contains
capitals, spaces, etc., put quotes round it.

~13
A point of terminology. Prolog programmers call "write" a PREDICATE, in
the same way that they call ">" and "loves" predicates. This can confuse
the beginner, because "predicate" implies something that has a meaning
in logic, and "write" doesn't.

For the moment, just accept this terminology. I shall try to refer to
such predicates as COMMAND PREDICATES (though that's not standard
terminology).

Do not confuse command predicates with commands, like "next" and
"analyse", that you give to the Tutor. The word "command" means the same
in both - something you do to change things - but otherwise, there is no
connection between the two.

Like the comparison predicates of Lesson 5, some command predicates are
BUILT-IN (provided by the implementor). "write" is one such. Others, you
can write for yourself, and I will ask you to do so soon.

~14
Beginners sometimes have trouble when they want to write several things
in one go. In Lesson 6, I explained why the question

      not(is_a(joe,man), lives_in(joe,paris) )?

does not do as expected. The comma is treated as an argument separator,
and this makes Prolog think you're calling a predicate called "not" with
two arguments. Since there's no such thing, the question fails.

Similarly with
      write('When',did,you,last,see,your,father)?
The commas will be treated as argument separators, making Prolog think
of this as a 7-argument predicate called "write". But the only version
of "write" that's built-in is the one-argument form, so the question
will fail.

~15
Now I shall introduce another command predicate: nl.

Try the question

      nl?

~16
That commands Prolog to display a blank line. "nl" is short for
"newline".

~17
How can we make Prolog obey more than one command predicate?

Try the question

      write('Happy'), nl, write('Birthday'), nl?

~18
The comma, which means "and" in the logic language, means "do next" in
the control language. It's like the semicolon in Pascal, or putting one
statement after another in Basic.

~19
Now, type this definition:

      greet(A) :-
          write('Happy'), nl,
          write('Birthday'), nl,
          write('Dear '), write(A), write('!'), nl.

It looks like a logical predicate, but it's in Prolog's control
language, so you should interpret it differently, as a sequence of
commands:

      To greet someone (A), do this:
          Write 'Happy', then take a newline.
          Write 'Birthday', then take a newline.
          Write 'Dear', then write the value of A, then write '!',
          then take a newline.

~20
Try questions like

      greet(john)?
      greet('John')?

~21
"greet" is like a procedure in Pascal. After the ":-" go the statements
that define what commands it must obey. Before the ":-" come any
arguments. Think of the ":-" as meaning "this is how to do it".

Note incidentally that you can't say
      write( 'Dear ', A, '!' )?
This would be calling a 3-argument write, and there is no such thing.

Note also that "analyse", if you try to use it on "greet", will give
some rather confusing output. It doesn't know about the control
language, and assumes everything is in the logic language, so will
translate "greet" as a logical predicate. Don't get confused by this!

~22
I have now introduced two command predicates for outputting ("write" and
"nl"); and I've shown you how to sequence them with comma; and how to
use them in definitions.

In practice, "write" is tedious to use because, as in "greet", you need
to do a separate "write" for each message. If you need to do output in
your projects, I'll provide a more convenient command predicate. .

~23
I shall now turn to input. Prolog provides a command predicate called
"read" for reading stuff typed at the terminal. I shall not describe it
in detail, because (like "write"), it's not very convenient for the kind
of tasks your projects require. I shall supply more convenient
predicates if you need them.

But, just to demonstrate, after the end of this section, please type the
question

      read(X)?

When you've done so, you will see the symbols |: at the left hand side
of the screen. When they appear, type

      fred.

and RETURN.

~24
If you did that correctly, you will have got the answer "X = fred".
"read" reads something from the terminal, and puts it into the variable
given as argument.

The reason that read isn't very convenient is that what it reads has to
be in Prolog syntax. Most things you'll want to type, such as sentences,
aren't, and "read" will refuse to accept them. But as I said above, I
can supply command predicates that will accept sentences, or other kinds
of non-Prolog input, and leave them in a suitable form for your programs
to process.

~25
Now, some practice with "write", "nl", and "read".

To start with, can you write a command predicate that just says "Hello".

~26
That's not hard. Use a definition like

      say_hello :-
          write('Hello').

Then you can use it in a question like

      say_hello?

~27
But there's one thing that may confuse you. This predicate has NO
ARGUMENTS - i.e. there is nothing in brackets after its name.

That's not surprising. When a predicate has arguments, what it does must
depend on their value. For example, "greet(john)?" will output a
different message from "greet(winnie)?".

But "say_hello" has to do the SAME thing every time, so its effect must
not depend on anything else. Hence, there's no point at all in giving it
arguments.

This applies to command predicates. Unless a logic predicate has
arguments, it can only be true every time, or false every time. Hence,
logic predicates will always have arguments.

~28
Now, can you write a command predicate that
(a) Outputs the message "Please type your name";
(b) Reads a name;
(c) Outputs "You typed " followed by the name.

When testing it, and typing in a name, you must type an atom (like
"fred" above), followed by a dot:

      frank.
      dav.

or whatever.

~29
Like "say_hello", this command predicate will have no arguments. If you
can't see how to do it, elaborate the definition below:

      echo :-
          write( ... ),
          read( ... ),
          write( ... ).

~30
Well, you should have something like

      echo :-
          write('Please type your name'), nl,
          read(X),
          write('You typed '), write(X), nl.

~31
In Lesson 6, we saw how to do arithmetic. Can you write a command
predicate that takes two numbers as arguments and writes

      The sum of ... and ... is ...

where the dots are to be replaced by the two numbers and the sum
respectively.

~32
If you're not sure, try elaborating on this framework:

      sum( A, B ) :-
          write(...), write(...), write(...), write(...),
          ... is ...,
          write(...), write(...), nl.

where you should fill in the dots.

~33
You should have had something like:

      sum( A, B ) :-
          write('The sum of '), write(A), write(' and '), write(B),
          S is A+B,
          write(' is '), write(S), nl.

Notice that I've chosen a new variable (S) to hold the sum. I've also
put the message atoms in quotes to insulate the spaces and capital
letters from Prolog, and I've had to use one "write" for each part of
the message.

~34
Now, can you write a predicate that reads two numbers typed at the
keyboard, and writes the same message as above. This predicate will not
have any arguments; instead, it will call "read" twice to get the
numbers.

As with names, you can type numbers to "read" by following them by a
dot:

      2.
      3.

~35
If you're having trouble, try filling in the dots here:

      doit :-
          read(A), read(A),
          write(...), write(...), write(...), write(...),
          ... is ...,
          write(...), write(...), nl.

~36
You should have had something like:

      doit :-
          read(A), read(B),
          write('The sum of '), write(A), write(' and '), write(B),
          S is A+B,
          write(' is '), write(S), nl.

To call "doit", you'd give the question

      doit?

You would then see the symbol |: appear. Type a number followed by a dot
(and RETURN). You'd then see another |: . Type another number. Then the
sum would appear.

~37
I have now talked about input and output, and arithmetic. In computer
science, output is said to be a SIDE-EFFECT, because it doesn't just
calculate results, but also changes some part of the program's
environment. Arithmetic isn't a side-effect, because it doesn't change
the environment.

As the final part of this lesson, I shall discuss command predicates
with a different kind of side-effect, that of altering the knowledge
base.

~38
If you tried Traveller, you will have seen how, as a trader's status
varies, these changes are recorded as facts in the database, and as
corresponding text in the textbase. The way Traveller does this is by
calling three predicates, "add", "adda", and "del".

"add" and "adda" both take one argument. That argument is something more
complicated than a variable, atom or number: it's a Prolog fact. They
add this argument to the knowledge-base. "add" adds it to the end;
"adda" to the start.

~39
Demonstrate this with questions like

      add( loves(john,mary) )?
      add( loves(mary,fred) :- likes(fred,wine) )?
      adda( loves(bert,ethel) )?
      adda( loves(mark,julia) :- likes(julia,beer) )?

After each one, do a
      show.
to see how the textbase has changed. Try asking questions about loves to
see how the database has changed.

~40
What's the difference between "add" (or "adda"), and putting a fact in
by typing it, as in
      10 loves(john,mary).
?

Why bother to have "add" and "adda" at all? Think about this before
reading the next section.

~41
The point about "add" and "adda" is that they can be called under
program control. You can include calls to them in your command
predicates. This is exactly what "run" does in Traveller.

To demonstrate, type in the following clause for predicate "match".
      match(X,Y) :-
          add( loves(X,Y) ).

Then try questions such as
      match(john,mary)?

~42
Note that the values that X and Y have are "frozen" into the fact.

~43
There's also a command predicate for removing facts, called "del". x{facts!deleting at run-time|(ee}

The question
      del( loves(john,mary) )?
does the obvious thing.

The question
      del( loves(X,mary) )?
will remove the first fact about anyone who loves Mary.

The question
      del(loves(john,X))?
will remove the first fact about John loving anyone.

Guess what
      del( loves(X,Y) )?
will do.

~44
It removes the first fact about anyone loving at all.

~45
Note that these predicates are NOT standard Prolog. Standard Prolog has
"assert" and "asserta" as equivalents to "add" and "adda"; "retract" as
equivalent to "del".

It also has something called "retractall" which is stronger than
"retract" because it will remove ALL facts for a given predicate in one
go.

I have implemented add, adda and del for you to use with the Tutor,
because they update the textbase, so you can see what's been added or
deleted. Normal Prolog doesn't store text of facts in the same way, so
it's harder to see what's going on. There is a way to do so, but it's
not very convenient.

~46
You have now almost reached the end of Lesson 7. In it, I've introduced
the notion of Prolog as a CONTROL LANGUAGE: a language for giving
commands and controlling their order of execution.

The control language uses the same symbols as Prolog's logic language
(comma and :- for example), but they mean different things.

I have introduced the predicates "write" and "nl" for OUTPUT; and "read"
for INPUT.

Messages for "write" that contain spaces or other odd characters should
be enclosed in single quotes.

Input for "read" must be in Prolog syntax, and followed by a dot. That's
not always convenient, but there are other ways to do input.

~47
I have shown you the predicates "add" and "adda" for ADDING facts, and
"del" for DELETING them.

These AREN'T STANDARD PROLOG; their standard equivalents are "assert",
"asserta", and "retract" (plus "retractall" to delete all facts for one
predicate).

These predicates take PROLOG FACTS as arguments. All others you've met
take variables, numbers, or atoms (except for "is", which takes a
formula as its second argument).

~48
You have now reached the end of Lesson 7. In the remaining lesson, I
shall tell you how to combine the logic language and the control
language, so you can use the results of inferences to control the way
that commands operate. That will conclude the Logic Programming Tutor
sessions; it will then be time to move to a system which makes it more
convenient to write large programs.

Please do any exercises for this lesson before proceeding to the next
one.
