{------------------------------------------------------------------------------
                                   SEARCHING

The `solve' function is the logical inference mechanism which allows the expert
system to search for solutions to goals, by making deductions from the stored
definitions and from the answers to the questions which it asks the user. This
is essentially the same as the inference mechanism which is built into logic
programming languages, with two main differences.  The first is that the search
algorithm has to be programmed explicitly, and the second is that interaction
with the user cannot be handled as a side effect; questions are returned as
part of the result, and answers are fed in as part of the argument.
------------------------------------------------------------------------------}

module Search where
import Result
import Table
import Knowledge
import Match

-- A call to `solve' returns a list of solutions and questions of type
-- `Solution'. Each solution will be preceded by the questions to which `solve'
-- needs answers in order to form that solution, and the answers to these
-- questions are passed to `solve' in its database argument. A solution
-- consists of an environment giving information about variables, and a list of
-- variable names which are not mentioned in the environment and are therefore
-- available for general use. In particular, the search procedure often calls
-- for a copy of a goal to be made using fresh variables, and the `freshCopy'
-- function performs this, returning a modified solution along with the copy.

data Solution = Soln Environment [String] | Question String

freshCopy (Soln env vs) p =
   ((Soln env (drop n vs)), subst tab p) where
   tab = updateList newTable (zip xs [Var v | v <- take n vs])
   xs = vars p
   n = length xs

-- The arguments to `solve' are: a database of stored definitions and
-- information gained from answers to questions, a partial solution
-- representing the information gained about variables so far in the search,
-- and a goal to be satisfied. The first equation allows questions which are
-- generated deep within the search to be passed up and out in the main
-- solution stream. Compound goals are solved by solving the two subgoals and
-- combining the solutions. In the case of `and', information gained in each
-- solution to the first subgoal is used in solving the second. A simple goal
-- (a relation) is solved either by consulting the stored definitions, or by
-- asking the user a question, depending on the verb in that relation.

solve db (Question q) g = [Question q]

solve db soln (Term "or" [g1,g2]) =
   solve db soln g1 ++ solve db soln g2

solve db soln (Term "and" [g1,g2]) =
   concat [solve db res g2 | res <- solve db soln g1]

solve db soln g =
   if not (null rs) then lookup db soln g rs else ask info soln g
   where
   (defs,info) = db
   rs = relevant defs g

-- To `lookup' a simple goal using the list of rules `rs', a fresh copy of each
-- rule is made (to avoid name clashes with variables about which information
-- is already known), and `try' is used to see if the left hand side of the
-- rule matches the goal. If it does, the goal on the right hand side of the
-- rule is used to continue the search for solutions.

lookup db soln g rs =
   concat [try db soln' g r' | (soln',r') <- copies] where
   copies = [freshCopy soln r | r<-rs]

try db (Soln env vs) g (Term "if" [p,newg]) =
   if fails m then [] else solve db (Soln (answer m) vs) newg
   where
   m = match env g p

-- If the solver must ask a question then that question is returned in the list
-- of solutions. The answer is then looked up in the table `info' of
-- questions-and-answers passed as an argument. If the answer is `yes', then
-- the current partial solution is returned. This assumes that questions
-- contain no variables, eg `the animal has stripes?'. Note that, as with other
-- interactive i/o functions, `ask' must return the question before testing the
-- answer.

ask info (Soln env vs) g =
   Question (showPhrase (subst env g)) :
   if ans then [Soln env vs] else [] where
   ans = answer (find info (showPhrase (subst env g)))
