The important point to remember is that when you modify a deftemplate
fact, the fact is retracted and a new (modified) fact is asserted. Your rule is also matching the modified fact, which is then again modified and matched, etc., resulting in an infinite loop. If you were to watch facts and activations when you run your code, you would see something like this:
<== f-1 (application (app-id 123) (app-complete nil) (transcript-received Yes))
==> f-2 (application (app-id 123) (app-complete TRUE) (transcript-received Yes))
==> Activation 0 complete: f-2
<== f-2 (application (app-id 123) (app-complete TRUE) (transcript-received Yes))
==> f-3 (application (app-id 123) (app-complete TRUE) (transcript-received Yes))
==> Activation 0 complete: f-3
<== f-3 (application (app-id 123) (app-complete TRUE) (transcript-received Yes))
==> f-4 (application (app-id 123) (app-complete TRUE) (transcript-received Yes))
==> Activation 0 complete: f-4
(etc.)
You can prevent this by only matching applications that aren't already complete. Here is a modified version of your code that adds a default FALSE
value to the app-complete
slot and only matches applications that are not complete:
(deftemplate application "structure of an application"
(slot app-id (type INTEGER))
(slot app-complete (type SYMBOL) (default FALSE))
(slot transcript-received (type SYMBOL)))
(defrule complete "rule for app completeness"
?f <- (application (transcript-received Yes) (app-complete FALSE))
=>
(modify ?f (app-complete TRUE)))
Now, if you watch facts and activations, you should see the following when you assert your fact and run:
CLIPS> (assert (application (app-id 123) (transcript-received Yes)))
==> f-0 (application (app-id 123) (app-complete FALSE) (transcript-received Yes))
==> Activation 0 complete: f-0
<Fact-0>
CLIPS> (run)
<== f-0 (application (app-id 123) (app-complete FALSE) (transcript-received Yes))
==> f-1 (application (app-id 123) (app-complete TRUE) (transcript-received Yes))
CLIPS>