Discussions related to Visual Prolog
Frank
Posts: 22
Joined: 23 Nov 2014 20:41

Deterministic Error

Unread post by Frank »

I capture a string variable(Title) in one part of the program in a class fact

Code: Select all

class facts text1:(string).   clauses  dotext(Title):-         retract(text1(Title)),         asserta(text1(Title)),!.     dotext(Title):-         asserta(text1(Title)).
then I try to use the variable in another part of the same program

Code: Select all

predicates     onDiagnosisClick : button::clickResponder. clauses     onDiagnosisClick(_Source) = button::defaultAction:-      .........      retract (text1(Title)),!,       Result= Title,       ..........
I get error c 631, The predicate'...onDiagnosisClick/1 >( i)' which is declared as procedure is actually 'determ'

I would appreciate your assistance.
Thanks
Martin Meyer
VIP Member
Posts: 328
Joined: 14 Nov 2002 0:01

Unread post by Martin Meyer »

Hello Frank,

the retract/1 predicate is nondeterm. A call to a nondeterm predicate can fail or can succeed and leave a backtrack point.

Appending a cut to a call to a nondeterm predicate, as in your code in retract(text1(Title)), !, results in a code fragment, which can fail or succeed without leaving a backtrack point. Thus the code fragment gives its surrounding code block a determ mode (provided that there is is nothing else in the code block, which supersedes the determ mode).

Omitting the predicate mode in the declaration of a predicate domain (as in declaration of onDiagnosisClick/1->) means, the predicate has procedure mode.

So, that retract/1 is nondeterm can be the reason of your error, but to be sure that it is, the entire code of the clauses of onDiagnosisClick/1-> needed to be examined.

Best regards
Martin
Peter Muraya
VIP Member
Posts: 147
Joined: 5 Dec 2012 7:29

Unread post by Peter Muraya »

Hello Frank,
I think Martin is right. Here's how I would solve the problem.

Code: Select all

predicates     onDiagnosisClick : button::clickResponder. clauses     onDiagnosisClick(_Source) = button::defaultAction:-      .........      if retract (text1(Title)),!      then            Result= Title      else           exception::raise_error("Check that you have not forgotten to set text1 before getting here")      end if
Mutall Data Management Technical Support
Martin Meyer
VIP Member
Posts: 328
Joined: 14 Nov 2002 0:01

Unread post by Martin Meyer »

Yes, Peter's solution should work out. Moreover (since VIP 7.5) the condition in an if-then-else statement is followed by an implicit cut. Thus in current VIP the ,! in Peter's code can be omitted.

Regarding my prior message, more exact is: Stating the predicate mode of onDiagnosisClick/1-> is actually omitted in the declaration of the predicate domain clickResponder in the button interface, to which your code refers by onDiagnosisClick : button::clickResponder.

Regards
Martin
User avatar
Thomas Linder Puls
VIP Member
Posts: 1398
Joined: 28 Feb 2000 0:01

Unread post by Thomas Linder Puls »

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:

Code: Select all

text1("Text1").
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:

Code: Select all

text1("Text2"). text1("Text1").
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,         ...
Regards Thomas Linder Puls
PDC
Frank
Posts: 22
Joined: 23 Nov 2014 20:41

Deterministic Error

Unread post by Frank »

Thanks Martin, Peter and Thomas for your valuable and detailed suggestions. I continued getting the error messages when I replaced the retract statement with text1(Title) and/or removed the cut.
The error message disappeared when I used Peter's suggestion:

if retract (text1(Title)),!
then
Result= Title
else
exception::raise_error("Check that you have not forgotten to set text1 before getting here")
end if

Million thanks to all of you.

Best regards,
Frank
Martin Meyer
VIP Member
Posts: 328
Joined: 14 Nov 2002 0:01

Unread post by Martin Meyer »

When you -only- replace your retract statement by text1(Title) the error persists, because text1(Title) can have none, one, or many solutions since fact text1 is still nondeterm. Followed by a cut, i.e. text1(Title), !, it gives either none or one solution. Thus both ways, with or without cut, don't fit well to onDiagnosisClick being a procedure.

Clearly best way is to follow Thomas' advice. There you can simplify the code even more by kicking out the dotext predicate and instead exposing text1 as a property in the interface:

Code: Select all

interface ..name..   ... properties     text1 : string.   ... end interface ..name..   %---   implement ..name..   ... facts     text1 : string := "".   ... predicates     onDiagnosisClick : button::clickResponder. clauses     onDiagnosisClick(_Source) = button::defaultAction :-         ...         Result = text1,         ...   ... end implement ..name..
Regards
Martin
Frank
Posts: 22
Joined: 23 Nov 2014 20:41

Deterministic Error

Unread post by Frank »

Hi Martin,
As I indicated earlier, when I followed Peter's suggestion, the problem was resolved and the code is running fine now. However, I will try Thomas and your suggestions to make the code even better.

Thanks again.

Best regards,
Frank
Post Reply