Often we want to visualise some program data in a graphic display. This can be done using many different Tk widgets in a quite straightforward way. The situation becomes slightly more complicated if we want to trace changes in Prolog data, e.g. instantiations of free variables, changes of domain variables in constraint programs and undoing these changes on backtracking. An elegant and powerful mechanism for this purpose are suspensions attached to involved Prolog variables. These are the deamons like the one used in our 'money' example (sec. 5.2.4). A slightly more complicated case is to display the current value of a domain variable, because the display might be progressively modified several times. A simple finite domain deamon looks as follows:
Note that display_var/6 has the previous domain in Tcl format in its second argument, which is used to restore the display on failure. Such a deamon can be used to trace successive changes of a domain variable:display_var(Var, _, Canvas, X, Y, Tag) :- dvar_domain_list(Var, Domain), tcl_string(Domain, String), % Tcl format of the domain tcl('## delete ##', [Canvas, Tag]), tcl('## create text ## ## -text ## -anchor w -tags ##', [Canvas, X, Y, String, Tag]), (var(Var) -> suspend(display_var(Var, String, Canvas, X, Y, Tag), 2, Var->any) ; true ). display_var(_, PrevString, Canvas, X, Y, Tag) :- tcl('## delete ##', [Canvas, Tag]), tcl('## create text ## ## -text ## -anchor w -tags ##', [Canvas, X, Y, PrevString, Tag]), fail.
Each variable modification wakes the sleeping display_var/6 goal which erases the previous text and replaces it with a new one. At the same time, it creates a choice point so that when the execution backtracks over it, the previous text is restored.[eclipse 31]: lib(tk), lib(fd). yes. [eclipse 32]: tk([]), tcl 'canvas .c; pack .c'. yes. [eclipse 33]: X::1..15, display_var(X, "{}", '.c', 20, 20, t), sleep(1), X ## 8, sleep(1), X #> 3, sleep(1), X #< 10, sleep(1), X = 6. X = 6 More? (;) no (more) solution.
This method is sufficient for pure Prolog code. With cuts, however, we might end up with a wrong display: when a choice point created by the display_var/6 predicate is removed by a cut and then the program fails, the previous text is not restored and the display does not correspond to the current value:
After the fail, the displayed value is 8, although the current domain is 1..8. As cuts are used internally in various more or less pure predicates, this problem may occur even with pure user programs. ProTcXl offers a possibility to cope with this problem. The predicate tcl_cut_fail/1 has a Tcl script as argument. When it is called, the script is stored in an ECLiPSe internal data structure (the trail). If later this predicate is being cut or if the execution fails over it, the script is executed. This predicate can also be used to restore the previous display instead of creating a choice point. Since it is necessary to create an explicit command string, the previous deamon would then look as follows:[eclipse 40]: X :: 1..8, display_var(X, "{}", '.c', 20, 20, t), (once(X #> 7), fail; X::D). X = X{[1..8]} D = [1..8]
When we run the same query, the display will contain the correct list "1 2 3 4 5 6 7 8".display_var(Var, RestoreCmd, Canvas, X, Y, Tag) :- dvar_domain_list(Var, Domain), tcl_string(Domain, String), concat_string([Canvas, ' delete ', Tag, '; ', Canvas, ' create text ', X, ' ', Y, ' -text ', String, ' -anchor w -tags ', Tag], ThisCmd), tcl(ThisCmd), tcl update, tcl_cut_fail(RestoreCmd), (var(Var) -> suspend(display_var(Var, ThisCmd, Canvas, X, Y, Tag), 2, Var->any) ; true ).
This predicate should be used only to cope with a cut followed by failure, as is the case e.g. in the branch and bound search with min_max/2 . If a cut is simply executed and the execution succeeds, it is not necessary to repair the display, however tcl_cut_fail/1 would trigger the script anyway.