In the previous two methods, one of the language systems was the master
and the other slave, the master called the slave to perform some action
or calculation, the slave sits waiting until the master calls it. We
have seen that this has disadvantages when Prolog is the slave in that
the state of the Prolog call is lost. Each Prolog call starts from the
beginning unless we save the state using message database
manipulation through calls to assert
and retract
.
Using the Prolog event queue, however, it is possible to get a more balanced model where the two language systems cooperate without either really being the master or the slave.
One way to do this is the following:
tcl_eval
calls
What can processing a Prolog event mean? Well, for example, a button press from Tk could tell the Prolog program to finish or to start processing something else. The Tcl program is not making an explicit call to the Prolog system but sending a message to Prolog. The Prolog system can pick up the message and process it when it chooses, in the meantime keeping its run state and variables intact.
To illustrate this, we return to the 8-queens example. If Tcl/Tk is the master and Prolog the slave, we have shown that using a callback to Prolog, we can imagine that we hit a button, call Prolog to get a solution and then display it. But how do we get the next solution? We could get all the solutions, and then use Tcl/Tk code to step through them, but that doesn't seem satisfactory. If we use the Prolog is the master and Tcl/Tk is the slave model, we have shown how we can use Tcl/Tk to display the solutions generate from the Prolog side: Prolog just make a call to the Tcl side when it has a solution. But in this model Tcl/Tk widgets do not interact with the Prolog side; Tcl/Tk is mearly an add-on display to Prolog.
But using the Prolog event queue we can get the best of both worlds: Prolog can generate each solution in turn as Tcl/Tk asks for it.
Here is the code on the Prolog side that does this. (We have left out parts of the code that haven't changed from our previous example, see Queens Display).
:- use_module(library(tcltk)). :- use_module(library(lists)). setup :- tk_new([name('SICStus+Tcl/Tk - Queens')], Tcl), tcl_eval(Tcl, 'source queens2.tcl', _), tk_next_event(Tcl, Event), ( Event = next -> go(Tcl) ; closedown(Tcl) ). closedown(Tcl) :- tcl_delete(Tcl). go(Tcl) :- tcl_eval(Tcl, 'clear_board', _), queens(8, Qs), show_solution(Qs), tk_next_event(Tcl, Event), ( Event = next -> fail ; closedown(Tcl) ). go(Tcl) :- tcl_eval(Tcl, 'disable_next', _), tcl_eval(Tcl, 'clear_board', _), tk_next_event(Tcl, _Event), closedown(Tcl). show_solution(Tcl, L) :- tcl(Tcl), reverse(L, LR), tcl_eval(Tcl, [show_solution, br(LR)], _), tk_do_all_events.
Notice here that we have used tk_next_event/2
in several places.
The code is executed by calling setup/0
. As usual, this loads in
the Tcl part of the program, but then Prolog waits for a message
from the Tcl side. This message can either be next
, indicating
that we want to show the next solution, or stop
, indicating that
we want to stop the program.
If next
is received, the program goes on to execute
go/1
. What this does it to first calculate a solution to the
8-queens problem, displays the solution through show_solution/2
,
and then waits for another message from Tcl/Tk. Again this can be
either next
or stop
. If next
, the the program
goes into the failure part of a failure driven loop and generates and
displays the next solution.
If at any time stop
is received, the program terminates
gracefully, cleaning up the Tcl interpreter.
On the Tcl/Tk side all we need are a couple of buttons:
one for sending the next
message, and the other for sending the
stop
message.
button .next -text next -command {prolog_event next} pack .next button .stop -text stop -command {prolog_event stop} pack .stop
(We could get more sophisticated. We might want it so that when the button it is depressed until Prolog has finished processing the last message, when the button is allowed to pop back up. This would avoid the problem of the user pressing the button many times while the program is still processing the last request. We leave this as an exercise for the reader.)