The special forms :eval and :test take a Lisp expression including Algernon variables, substitute any bindings available for the Algernon variables, and evaluate the expression in Lisp.
We have already seen the :test special form used in the rule
to test whether the value bound to the variable ?t satisfies a numerical relation.
For this and several following examples, we will use a small family tree and a few properties of people.
(tell '((:taxonomy (physical-objects (people Adam Beth Charles Donna Ellen))))) (tell '((:slot child (people people)) (:slot happy (people booleans) :cardinality 1) (:slot friendly (people booleans) :cardinality 1) (:slot age (people :number) :cardinality 1))) (tell '((child Adam Charles) (child Adam Donna) (child Adam Ellen)))
After asserting this information, we use the Algernon interface to inspect the contents of the frame describing Adam:
algy> vf adam Adam: Name: (adam) Isa: objects physical-objects people things Child: ellen donna charles
The following query looks up Adam's children and prints their names.
(ask '((child Adam ?kid) (:eval (format t "~%Adam has a child named ~a." '?kid))) :comment "Print names of all three children")
On querying the first predicate, (child Adam ?kid), the path will branch three ways, one binding the variable ?kid to each child. Along each branch, the :eval special form will evaluate the format expression to print a line. Notice that '?kid is quoted in the format expression. This is because the binding for the Algernon variable ?kid is substituted into the expression before Lisp evaluates it. Since that binding is an Algernon frame, it will give an error if it is evaluated as a Lisp symbol, so it must be quoted.
QUERYING: Print names of all three children Adam has a child named ELLEN. Adam has a child named DONNA. Adam has a child named CHARLES. Result (1 of 3): Binding: ?kid --- ellen "[ellen]" Result (2 of 3): Binding: ?kid --- donna "[donna]" Result (3 of 3): Binding: ?kid --- charles "[charles]" => T
Now let's query the user (via the Lisp function y-or-n-p) to find out whether he or she likes each child, and assert that the child is happy if so.
(tell '((child Adam ?kid) (:test (y-or-n-p "Do you like ~a?" '?kid)) (:eval (format t " ~a is happy!" '?kid)) (happy ?kid true)) :comment "Check whether each kid is liked by user.")
This path is asserted rather than queried because we want to assert (happy ?kid true) if the previous forms succeed, rather than checking whether it is currently known.
In this case, the user likes Ellen and Charles, but not Donna. Two paths from the three-way branch on (child Adam ?kid) succeed, while the other fails at the :test. Notice that the order in which the questions are asked and results are printed demonstrates that branches of the same path are followed in parallel.
ASSERTING: Check whether each kid is liked by user. Do you like ELLEN? (Y or N): y Do you like DONNA? (Y or N): n Do you like CHARLES? (Y or N): y ELLEN is happy! CHARLES is happy! Result (1 of 2): Binding: ?kid --- ellen "[ellen]" Result (2 of 2): Binding: ?kid --- charles "[charles]" => T
After this interaction, we view the three frames and verify that, indeed, Ellen and Charles are now known to be happy. Although we could not infer that Donna is happy, we don't know that she is unhappy.
algy> vf ellen Ellen: Name: (ellen) Isa: objects physical-objects people things Happy: true algy> vf donna Donna: Name: (donna) Isa: objects physical-objects people things algy> vf charles Charles: Name: (charles) Isa: objects physical-objects people things Happy: true
This illustrates an important principle of user-interface design: the user must always have a way to refuse to answer. In this case, we satisfied this requirement by interpreting a ``No'' answer to y-or-n-p as providing no information.