Just to set the record straight and in case anyone else tries to
implement this, I had to introduce an extra cut in ndet_getmsg1/2
to get this to work properly, because the choice-point created by
retract(pushed_message(Q,M)) was messing things up in a way that
I don't fully understand, but seems to be because of the interaction
between asserta/1 and retract/1 when asserta is called while retract
is active. This is all because I took the cut out of bt_call/2
thinking it
was a good idea...
So, the solution in full is
----------------------------------------------------
:- dynamic pushed_message/2.
ndet_getmsg(Queue,Message) :-
bt_call(ndet_getmsg1(Queue,Message),push_back(Queue,Message)).
ndet_getmsg1(Queue, Message) :-
retract(pushed_message(Queue, Message)), !. % This is the extra
necessary cut.
ndet_getmsg1(Queue, Message) :-
thread_get_message(Queue, Message), !. % This cut is probably not
necessary.
push_back(Queue, Message) :-
asserta(pushed_message(Queue, Message)).
bt_call(Do,Undo) :- Do, (true; once(Undo), fail).
---------------------------------------------------
With this it is possible to extract successive elements from the
queue and apply non-deterministic predicates to them in a
fully backtrackable way.
Richard, re your suggestion
> Why not simply represent the queue as a process,
> and let that process store the data any way it feels like?
Forgive me if I'm being dense but what exactly do you mean by
a process in this context? Do you have a particular Prolog
implementation
in mind?
Cheers,
Samer
On 13 Jan 2007, at 17:11, Samer Abdallah wrote:
>
> On 12 Jan 2007, at 20:04, Jan Wielemaker wrote:
>
>> On Friday 12 January 2007 20:19, Samer Abdallah wrote:
>>> On 12 Jan 2007, at 16:27, Jan Wielemaker wrote:
>>>> Another disadvantage is that it makes thread_get_message
>>>> non-deterministic. Somewhat more in line with the desing might be
>>>> a thread_send_message that puts the new message at the head rather
>>>> than at the tail. That allows for a design like:
>>>>
>>>> ndet_get_message(Queue, Message) :-
>>>> thread_get_message(Queue, Message),
>>>> ( true
>>>> ; thread_send_message(Queue, Message, [at(head)]),
>>>> fail
>>>> ).
>>>>
>>>> Note however that this implementation is not correct with respect
>>>> to the
>>>> cut.
>>
>> Simply consider
>>
>> ( ndet_get_message(Queue, Message)
>> -> fail
>> ; true
>> ).
>>
>> With a truly logical predicate this would not change Queue. After
>> pruning the choicepoint however the message will not be put back.
>>
>> You can do the above with the current system easily (not tested):
>>
>> :- dynamic
>> pushed_message/2.
>>
>> ndet_getmsg(Queue, Message) :-
>> ndet_getmsg1(Queue, Message),
>> ( true
>> ; push_back(Queue, Message)
>> ).
>>
>> ndet_getmsg1(Queue, Message) :-
>> retract(pushed_message(Queue, Message)).
>> ndet_getmsg1(Queue, Message) :-
>> thread_get_message(Queue, Message).
>>
>> push_back(Queue, Message) :-
>> asserta(pushed_message(Queue, Message)).
> Thanks Jan, that did the trick, except that I used a backtracking
> meta-predicate
> that I was already using elsewhere (to manage backtrackable
> database updates)
>
> ndet_getmsg(Queue,Message) :-
> bt_call(ndet_getmsg1(Queue,Message),push_back(Queue,Message)).
>
> bt_call(Do,Undo) :- Do, on_backtracking(Undo).
>
> on_backtracking(_).
> on_backtracking(G) :- G, !, fail.
>
> though now I'm getting paranoid about cuts and whether or not they're
> doing the right thing! For example, I originally had
> bt_call(Do,Undo) :- Do, !, on_backtracking(Undo).
> but just now I removed the cut so that Do can be non-deterministic.
>
>>
>>>> So, I'd prefer to consider the problem at a somewhat higher level
>>>> first
>>>> to see whether there is an elegant solution you missed there.
>>>
>>> Well, I would like to be able to solve the problem without invoking
>>> another thread - essentially what I want is a findall that returns
>>> a *lazy* list that carries enough information about the state of
>>> solution search to build itself on demand as it goes along. I can't
>>> see how to do it without using a meta-interpreter which handles
>>> the search tree explicitly at the meta level.
>>> My current transaction logic interpreter still relies on
>>> backtracking
>>> in the underlying interpreter - doing otherwise would make it much
>>> more complex. Hence the quick and i hope not-too-dirty
>>> multithreading
>>> approach.
>>
>> I'm not very good at that, but you can hack some of these things
>> using
>> coroutining or attributed variables. Its a really nice puzzle :-)
> corouting --> next rainy sunday!
>
> Thanks,
> Samer
>
>>
>> Success --- Jan
>>
>>
>> ------------
>> For further info, please visit
http://www.swi-prolog.org/>>
>> To unsubscribe, send a plaintext mail with "unsubscribe prolog <e-
>> mail>"
>> in its body to
majordomo@...
>
>
> ------------
> For further info, please visit
http://www.swi-prolog.org/>
> To unsubscribe, send a plaintext mail with "unsubscribe prolog <e-
> mail>"
> in its body to
majordomo@...
------------
For further info, please visit
http://www.swi-prolog.org/To unsubscribe, send a plaintext mail with "unsubscribe prolog <e-mail>"
in its body to
majordomo@...