Hi, Frank.
Since this is an extract of a program and I don't know the intension of things I can be wrong, but your code is suspicious in several respects.
- That text1 is a class fact
- You use of retract
- That text1 is nondeterm
Class fact
A button click responder belongs to a dialog or a form (or to a compound control). Dialogs/forms have instances and each of those instances corresponds to an object.
text1 is a class fact and therefore it is shared among all the instances of the dialog, it seems they should each have their own instance of this fact (meaning that the class keyword should be removed).
It may not be trivial to change it into an object fact, because that will mean that
dotext will also need to be an object predicate otherwise it cannot access the fact. Nevertheless it seems that an object fact is better here.
retract
retract removes facts from the fact database. But it seems that you use retract in places where you don't really want to remove the fact. Are you really sure that the fact should be removed when the Diagnosis button is clicked?
I.e. it seems that
onDiagnosisClick should not
retract:
Code: Select all
predicates
onDiagnosisClick : button::clickResponder.
clauses
onDiagnosisClick(_Source) = button::defaultAction:-
...
text1(Title),
!,
Result= Title,
...
Likewise, the use of
retract in
dotext is suspicious, but that seems related to the
nondeterm issue, so let us take that first.
nondeterm
A nondeterm fact can have zero, one or many instances. But several things indicate that you actually only imagine one instance:
- onDiagnosisClick only uses the first instance
- dotext uses asserta to make the new text the first instance
Consider this call sequence:
Code: Select all
dotext("Text1"), % (1)
dotext("Text2"), % (2)
dotext("Text1"), % (3)
After (1) the
text1 fact contains:
When executing (2) nothing will be retracted in the first
dotext clause because there are no matching
text1 facts. So a second fact will be
asserta-ed in the second
dotext clause and the
text1 fact will now contain:
When executing (3) we find a matching text1 fact in the first
dotext clause. This fact will be retracted and then it will be
asserta-ed. Meaning that it will be moved to the front of the facts, and therefore the new state is:
Code: Select all
text1("Text1"). % "Text1 is now in front
text1("Text2").
If this is indeed your intension then everything is obviously in order.
But I suspect that you actually only ever want the first of these facts.
In that case the fact should be declared
determ meaning that there can only be zero or one instance of the fact. To retain this property we will need to remove any instance of the fact before asserting a new instance:
Code: Select all
/* class */ facts
text1 : (string) determ. % Determ because we do not want more than one instance of the fact
clauses
dotext(Title):-
retractAll(text1(_)), % if the fact already has an instance then retract it
assert(text1(Title)).
Finally, lets consider the error you get. Currently (both in your code and with my changes) it can be the case that there is no
text1 fact (this is indeed the initial state). Your code will have to deal with this situation.
One possible way to deal with it could be to let
text1 be a fact variable rather than a determ/nondeterm fact. The compiler assumes that a fact variable always have a value (exactly one value). Therefore access to a fact variable is
procedure rather than
determ or
nondeterm.
Since a fact variable is assumed "procedure" it will have to have an initial value. The simplest is if there exists a suitable dummy value. If the empty string is such a suitable dummy value your code can look like this:
Code: Select all
/* class */ facts
text1 : string := "".
clauses
dotext(Title):-
text1 := Title.
predicates
onDiagnosisClick : button::clickResponder.
clauses
onDiagnosisClick(_Source) = button::defaultAction:-
...
Result= text1,
...