Prolog++ programs

Prolog++ is a product by LPA Associates for object-oriented programming extensions of LPA Prolog. Most Prolog++ programs can be easily converted into SICStus Objects programs. The following is a translation of a program for fault diagnosis in LPA's Prolog++ manual, page 83. The program illustrates a top-down diagnosis method starting from general objects to more specific objects. The problem is fault diagnosis for car maintenance. The objects have the following structure:

     - faults
             - electrical
             |       - lights
             |       - starting
             |               - starter_motor
             |               - sparking
             |                       - plugs
             |                       - distributer
             - fuel_system
             - mechanical
     

The general diagnosis method is defined in the object faults, whereas the cause-effect relationships are defined in the specific objects e.g. the object distributor.

This program heavily uses the sub/1 method. We have tried to be as close as possible to the original formulation.

     faults :: {
     
             super(utility) &
             dynamic(told/2) &
     
             /* no fault is the default */
             fault(_, _) :- :fail &
     
             findall :-
                     <: restart,
                     :: sub(Sub),
                     Sub :: find(Where, Fault),
                     <: print(Where, Fault),
                     :fail &
             findall &
     
             print(Where, Fault) :-
                     :writeseqnl('Location       : ', [Where]),
                     :writeseqnl('Possible Fault : ', [Fault]),
                     :nl &
     
             find(Where, Fault) :-
                     self(Where),
                     fault(FaultNum, Fault),
                     \+ (effect(FaultNum, S),
                         contrary(S, S1),
                         exhibited(S1)
                        ),
                     \+ (effect(FaultNum, SymptomNum),
                         \+ exhibited(SymptomNum)) &
     
             find(Where, Fault) :-
                     sub(Sub),
                     Sub :: find(Where, Fault) &
     
             exhibited(S) :-
                     :: told(S, R), !,
                     R = yes &
             exhibited(S) :-
                     symptom(S,Text),
                     (   :yesno([Text]) -> R = yes
                     ;   R = no
                     ),
                     :: asserta(told(S,R)),
                     R = yes &
     
             restart :-
                     :: retractall(told(_,_))
     
             }.
     electrical :: {
             super(faults)
             }.
     
     fuel_system :: {
             super(faults)
             }.
     
     mechanical :: {
             super(faults)
             }.
     
     lights :: {
             super(electrical)
             }.
     
     sparking :: {
             super(electrical)
             }.
     
     starting :: {
             super(electrical)
             }.
     
     starter_motor :: {
             super(electrical)
             }.
     
     plugs :: {
             super(sparking)
             }.
     
     engine :: {
             super(mechanical)
             }.
     
     cylinders :: {
             super(engine)
             }.
     distributor :: {
             super(sparking) &
     
             /* faults */
             fault('F1001', 'Condensation in distributor cap') &
             fault('F1002', 'Faulty distributor arm') &
             fault('F1003', 'Worn distributor brushes') &
     
             /* symptoms */
             symptom('S1001', 'Starter turns, but engine does not fire') &
             symptom('S1002', 'Engine has difficulty starting') &
             symptom('S1003', 'Engine cuts out shortly after starting') &
             symptom('S1004', 'Engine cuts out at speed') &
     
             /* symptoms contrary to each other */
             contrary('S1002', 'S1001') &
             contrary('S1003', 'S1001') &
     
             /* causal-effect relationship */
             effect('F1001', 'S1001') &
             effect('F1002', 'S1001') &
             effect('F1002', 'S1004') &
             effect('F1003', 'S1002') &
             effect('F1003', 'S1003')
     
             }.
     
     yesno(Value) :- write(Value), nl, read(yes).
     
     writeseqnl(Prompt, L) :- write(Prompt), write_seq(L).
     
     write_seq([]).
     write_seq([X|L]) :- write(X), write(' '), write_seq(L), nl.
     
     faults :- faults :: findall.
     | ?- faults.
     [Starter turns, but engine does not fire]
     |: yes.
     Location       : distributor
     Possible Fault : Condensation in distributor cap
     
     [Engine cuts out at speed]
     |: yes.
     Location       : distributor
     Possible Fault : Faulty distributor arm
     
     | ?- faults.
     [Starter turns, but engine does not fire]
     |: no.
     [Engine has difficulty starting]
     |: yes.
     [Engine cuts out shortly after starting]
     |: yes.
     Location       : distributor
     Possible Fault : Worn distributor brushes