Confusing results from engine_next

Hi folks,

I’m considering making use of the engines functionality in Prolog but am having trouble getting basic stuff to work. Probably I’m just misunderstanding the documentation and examples, but I figured I’d ask for help rather than floundering.

SWI-Prolog version 9.0.4

engines_scratch.pl

create_simulator(Handle) :-
    engine_create(_, process_commands() ,Handle).

process_commands() :-
    engine_fetch(Command),
    Command,
    engine_yield(Command),
    process_commands().
  
:- create_simulator(alice_sim).
:- engine_post(alice_sim, assertz( f(a) ), _ ).
:- engine_post(alice_sim, assertz( f(b) ), _ ).

REPL

1 ?- ['c:\\Users\\Elean\\Perforce\\mainline\\BoH\\test_engines.pl'].
true.

2 ?- engine_post(alice_sim, f(X)).
true.

3 ?- engine_next(alice_sim, Term1).
Term1 = f(a).

4 ?- engine_next(alice_sim, Term2).
ERROR: term `delivery' does not exist in alice_sim (Use engine_post/2,3)
ERROR: In:
ERROR:   [10] engine_next(alice_sim,_23322)
ERROR:    [9] toplevel_call(user:user: ...) at c:/program files/swipl/boot/toplevel.pl:1173

I expected the fourth call to generate Term2 = f(b). via backtracking, but clearly it’s not, and I don’t get why. Any thoughts?

In case folks are curious about the background:

I’m building an agent based simulation in Prolog for a game I’m working on. Central to the game is that each agent has its own perspective on the world, including both facts about the world, the ontology over which those facts are defined. I’ve implemented that as fully separated knowledge bases, with actions and responses passing between as messages. However, currently these knowledge bases are stored in different modules, which significantly complicates code that needs to be common across these agents.

It seems like switching to using engines will allow me to cleanly run each of the agents separately, and remove the need to thread Module:Predicate variables throughout a bunch of my code!

Edit 1: Added code blocks for easier reading
Edit 2: After experimenting with this more, it seems as though the above behavior may actually be a bug, possibly related to #1021
Edit 3: Although I think the above issue is still valid, it seems I misunderstood how engines handle dynamic and global data; unlike threads they don’t keep their own. Is that only achievable with threads, or is there a way of configuring engines to do that?

An engine can be considered a coroutine that can asks for something to do using engine_fetch/1, does its work and replies using engine_yield/1. So, your process_commands is fine. The post and fetch in [2] and [3] are also ok. But now, the engine is in its engine_fetch/1 state and you must first post something before you can get its next answer. If you make an engine_next/2 call, there is no waiting yielded message and the target engine is waiting in engine_fetch/2, you’d get a deadlock, so you get this -not so great- error message. Note that engine_post/3 combines the post and next in one call.

Where did you get this? As is, engines and threads are identical with respect to the visibility of data. Notably thread_local/1 predicates inside an engine are local to the engine. There is indeed a plan to allow engines to share all data with the thread that runs them, i.e. thread local predicates, flags, standard I/O. That will be an option though.

Thank you. I appreciate both your response and swi-prolog as a whole!

Okay, so every engine_next/2 needs to be paired with an engine_post/2.

What’s confused me is that engine_next/2 will happily backtrack over the goal given at creation, without any paired engine_post/2 calls. For example, with the previous setup:

1 ?- [‘c:\Users\Elean\Perforce\mainline\BoH\test_engines.pl’].
true.

2 ?- engine_create(X,f(X),engine2).
true.

3 ?- engine_next(engine2,Result).
Result = a.

4 ?- engine_next(engine2,Result).
Result = b.

…but it seems the goal in engine_create/3 is handled differently from the term sent via engine_post/2, which makes sense.

I set up a little test to see if f/1 was visible outside my engine and found that it was. I then looked for a way of making predicates local to the engine and didn’t find one; I didn’t realize that the thread_local/1 directive would apply to engine predicates, but I do now, so you’ve precisely answered my question. :slight_smile:

No. the Engine may call engine_yield/1 multiple times. You can call engine_next/2 as many times. It is just, if you call engine_next/2 on an engine that itself is waiting in engine_fetch/1 it makes no sense as the engine is waiting for input and you are waiting for output, so the thing will deadlock. You should consider engine_yield/1 similar to write/1 and engine_next/2 similar to read/1., while engine_fetch/1 and engine_post/2 are the read/write version in the other direction. Hope that is not making it more confusing :slight_smile:

Seems you’re getting the idea on the other issues :slight_smile: