GOFAI: A Simple Prolog Expert

10th June 2018

There was a time, long ago, before SVMs and Neural Nets, when AI was all rule based. Nowadays, with the prevalence of "curve-fitting" AI, these techniques are known as GOFAI, or "Good Old Fashioned Artificial Intelligence". In this post we look at a super simple example and consider why you'd use this instead of a more modern method.

AH, GOFAI! It seems to be a dying art these days, I look through AI journals and it seems everyone’s caught up in the hype of neural-nets, SVMs, and other such ‘curve-fitting’ methods. The days of “Good Old Fashioned AI” seem to be numbered, except they’re not!

So why the hype about modern methods? They pretty much out perform GOFAI across the board on accuracy, recall, precision etc. They’re cheaper to make in some ways: no need to try and extract knowledge from your domain experts. But they’re more expensive in some ways too: they need lots of data. Modern methods are achieving great things, like playing Go and identifying things in images or video.

So why bother with GOFAI? Well, only GOFAI can explain itself. When you’re just playing a game of Go or playing with finding cats in photos, it doesn’t matter too much why the computer decided what it did. But, when it’s a safety critical system, or diagnostic, or legal, or a myriad of other reasons, you really want to know why the computer made those decisions. With GOFAI you know what the computer has learnt, you can inspect it’s learning and make adjustments. This is particularly important in cases of bias.

Like all solutions, neither approach is the answer to all problems and a middle path combining the two will likely prove best in the end. In this post we’ll look at how to do a simple expert system to identify fruits, in Prolog.

Knowledge Base

WE STILL need data, but this time it’s from an expert. Ok, not such an expert, I wrote this knowledge base. Feel free to add more fruit, make sure each one can be uniquely identified.

% Data: fruit(X) :- attributes(Y)
fruit(banana) :- colour(yellow), shape(crescent).
fruit(apple) :- (colour(green); colour(red)), shape(sphere), stem(yes).
fruit(lemon) :- colour(yellow), (shape(sphere);shape('tapered sphere')), acidic(yes).
fruit(lime) :- colour(green), shape(sphere), acidic(yes).
fruit(pear) :- colour(green), shape('tapered sphere').
fruit(plum) :- colour(purple), shape(sphere), stone(yes).
fruit(grape) :- (colour(purple);colour(green)), shape(sphere).
fruit(orange) :- colour(orange), shape(sphere).
fruit(satsuma) :- colour(orange), shape('flat sphere').
fruit(peach) :- colour(peach).
fruit(rhubarb) :- (colour(red); colour(green)), shape(stick).
fruit(cherry) :- colour(red), shape(sphere), stem(yes), stone(yes).
This is the attribute-value data structure, which is the simplest available in Prolog. Look out for the single quote marks, they let us use spaces in our ‘atoms’, you’ll need to include them at the prompt too. Notice how we don’t need to include every predicate for every fruit, just enough to make each one unique.

The Expert System

IN THIS system the ‘expert’ needs to ask the user about the fruit, when it can identify the correct one, it will. We have two types of values, boolean ones that can be ‘yes’, and ones that can be from a set of values, like colour. For boolean values we can ask if something is correct, for the set of values we’ll make a menu.

% Asks
colour(X) :- menuask(colour, X, [red, orange, yellow, green, purple, peach]).
shape(X) :- menuask(shape, X, [sphere, crescent, 'tapered sphere', 'flat sphere', stick]).
acidic(X) :- ask(acidic, X).
stem(X) :- ask(stem, X).
stone(X) :- ask(stone, X).

Now we need to define ask/2 and menuask/3. I’m going to intersperse them both so we can do the predicates by job, so I’ll need to tell Prolog the definitions aren’t grouped by using the discontiguous predicate. I’m also going to get Prolog to remember answers using a known/3 predicate, so I’ll need to tell Prolog this will be changing in runtime with the dynamic/1 predicate.

So first we want Prolog to check if it has a fact in known/3. Then we want it to fail if it doesn’t have that fact. Then we want it to fail if it has that same attribute known with a different value, this catches cases where we’ve told it the colour is red and it is asking if the colour is yellow. Finally, if we’ve not caught anything from known/3, we must not know it, so we ask.

:- dynamic(known/3).
:- discontiguous menuask/3.
:- discontiguous ask/2.

% Remember what I've been told is correct
ask(Attr, Val) :- known(yes, Attr, Val), !.
menuask(Attr, Val, _) :- known(yes, Attr, Val), !.
% Remember what I've been told is wrong
ask(Attr, Val) :- known(_, Attr, Val), !, fail.
menuask(Attr, Val, _) :- known(_, Attr, Val), !, fail.
% Remember when I've been told an attribute has a different value
ask(Attr, Val) :- known(yes, Attr, V), V \== Val, !, fail.
menuask(Attr, Val, _) :- known(yes, Attr, V), V \== Val, !, fail.
% % I don't know this, better ask!
ask(Attr, Val) :- write(Attr:Val), write('? '),
                           asserta(known(Ans, Attr, Val)), Ans == yes.
menuask(Attr, Val, List) :-
                            write('What is the value for '), write(Attr), write('? '), nl,
                            write(List), nl,
                            check_val(Ans, Attr, Val, List),
                            asserta(known(yes, Attr, Ans)),
                            Ans == Val.
check_val(Ans, _, _, List) :- member(Ans, List), !.
check_val(Ans, Attr, Val, List) :-
                            write(Ans), write(' is not a known answer, please try again.'), nl,
                            menuask(Attr, Val, List).
This is quite a bit of code to jump through in one go, for a more detailed breakdown I’d recommend going to the book this is from, it’s called “Expert Systems in Prolog” and is an excellent read.

Just to make our user’s life easier, we’ll make a starting predicate.

go :- fruit(Fruit), write('The fruit is '), write(Fruit), nl.

Now we can call this in Prolog, and our expert will get to work!

?- go.
What is the value for colour?
[red, orange, yellow, green, purple, peach]
What is the value for shape?
[sphere, crescent, tapered sphere, flat sphere, stick]
The fruit is rhubarb

The Code And The Book

CREDIT WHERE credit is due, check out the book “Expert Systems in Prolog” by Dennis Merritt for much more on this topic and a method to separate the knowledge base from the expert system. SWI-Prolog have an fantastic online Prolog IDE, which means you can run this code online and identify that fruit.


IT’S CLEAR the greatest investment in such an expert system will be in the knowledge/data base. It takes quite some thought even on a simple (?!) topic like fruit to be able to identify them. It also relies on experts actually having the knowledge and being able to describe it.

I argued earlier that the great benefit of this method is that it can explain why it made a decision, but in this code no explanation is provided. It won’t be too hard to add in a print out, but in this example I wanted to keep the code as short and simple as possible. If you want an explained version, you can always run trace, go. for a really detailed breakdown.

Post Tags

All Tags

LIFE IS better when we share.