# Modules design for multi-agent modeling

**URL:** <https://swi-prolog.discourse.group/t/modules-design-for-multi-agent-modeling/557>\
**Category:** Help!\
**Created:** [April 18, 2019, 8:10pm UTC](https://swi-prolog.discourse.group/t/modules-design-for-multi-agent-modeling/557 "2019-04-18T20:10:17Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![ssardina](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/ssardina/32/189_2.png) [@ssardina](https://swi-prolog.discourse.group/u/ssardina)\
**Post date:** [April 18, 2019, 8:10pm UTC](https://swi-prolog.discourse.group/t/modules-design-for-multi-agent-modeling/557/1 "2019-04-18T20:10:17Z")

</div>

I’m using: SWI-Prolog version 7.6.4 and 8.0.2

Basically, I want the code to **model knowledge bases for many agents**. There is a common set of predicates that all the agents use, and each agent has its own percept info.

**Approach taken:**

Each agent stores their own data (e.g., perception) in their own module. For example, `agt23:percept/1`

All agents share static predicates for reasoning; those predicates are in a module called, say, `kb`, which exports many predicates. All agents will use that module `kb`, by using `agtXX:use_module(kb)`. So, modules `agtXX` will have access to all predicates in module `kb`.

However, many predicates in `kb` make use of predicates defined in the specific calling agent module. For example, they use `percept/1`. For example, in module `kb` I have:

```prolog
location(X) :- 
  percept(P),
  extract_location(X,P).

```

Now, because `location(X)` is in module `kb`, it tries to find `percept/1` in `kb` and not in the module that called `location/1`.

My only solution was to use:

```prolog
:- module_transparent(location/1).
location(X) :- 
  context_module(M),
  M:percept(P),
  extract_location(X,P).

```

This works but it is a **lot of work** as many many predicates in `kb` are meant to have goals against the calling module. The alternative is just to not use modules and have one huge database with all predicates, and duplicate them in every agent module.

Is there a best way to do this? Am I missing something on modules here?

Thanks a lot to whoever can give me a hint!

Sebastian

---

<div class="post-metadata">

**Author:** ![pmoura](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/pmoura/32/11_2.png) [@pmoura](https://swi-prolog.discourse.group/u/pmoura)\
**Post date:** [April 18, 2019, 8:21pm UTC](https://swi-prolog.discourse.group/t/modules-design-for-multi-agent-modeling/557/2 "2019-04-18T20:21:59Z")

</div>

Logtalk provides a straight-forward solution for multi-agents/multi-words design patterns. What you’re asking for is the ability to call predicates in _self_. Using your sample code:

```prolog
:- object(kb).

    :- public([location/1, percept/1]).

    location(X) :- 
        ::percept(P),
        extract_location(X,P).

:- end_object.

:- object(agt23, extends(kb)).

    percept(foo).

:- end_object.

```

---

<div class="post-metadata">

**Author:** ![ssardina](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/ssardina/32/189_2.png) [@ssardina](https://swi-prolog.discourse.group/u/ssardina)\
**Post date:** [April 18, 2019, 8:41pm UTC](https://swi-prolog.discourse.group/t/modules-design-for-multi-agent-modeling/557/3 "2019-04-18T20:41:55Z")

</div>

Thanks @pmoura, makes total sense in an object-oriented setting.

Now, are you suggesting this cannot be done in SWI modules? I guess that is probably the case, but just want to confirm before finding another approach.

---

<div class="post-metadata">

**Author:** ![pmoura](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/pmoura/32/11_2.png) [@pmoura](https://swi-prolog.discourse.group/u/pmoura)\
**Post date:** [April 18, 2019, 10:04pm UTC](https://swi-prolog.discourse.group/t/modules-design-for-multi-agent-modeling/557/4 "2019-04-18T22:04:06Z")

</div>

Note that the module transparent mechanism you describe is marked as **deprecated** in the documentation. But even if that’s not the case, that workaround, as other workarounds that are sometimes mentioned, require making explicit what’s implicit in Logtalk’s message to self control construct (`::/1`). E.g. extending most or all predicates with an extra argument. But that makes for cumbersome code (as you observed) and in itself doesn’t get you very far. Typical in multi-agents/multi-worlds applications, you also end up also requiring _super_ calls, inheritance (e.g. to represent different kinds of agents or worlds), interfaces/protocols, … You have all that and more in Logtalk as it was **designed** to provide those features. But Prolog modules were simply not designed for these scenarios. No amount of hacking with Prolog modules will provide you with a clean, comprehensive, efficient, equivalent alternative. The required underpinnings are simply not there.

---

<div class="post-metadata">

**Author:** ![jan](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/jan/32/4_2.png) [@jan](https://swi-prolog.discourse.group/u/jan)\
**Post date:** [April 19, 2019, 7:03am UTC](https://swi-prolog.discourse.group/t/modules-design-for-multi-agent-modeling/557/5 "2019-04-19T07:03:08Z")

</div>

As Paulo correctly remarks, `module_transparent` is indeed deprecated, though I do not expect it to be dropped anytime soon and probably never. Paulo is also right that the module system isn’t really designed for this and Logtalk is. That said, Logtalk is not more than a preprocessor for Prolog and if you need only one aspect of Logtalk you can quite easily replace this with some term/goal expansion and additional syntax that you like.

The normal way to do this would be for your KB module to have an additional argument for the agent and use that to make module qualified goals. From the agent module this would mean something like this (adjust syntax to your preference)

```prolog
agent(Self) :-
    context_module(Self).

...

    ...,
    ^location(X),
    ....

```

where `^location(X)` is translated using goal\_expansion/2 into

```prolog
    agent(Self),
    kb:location(X, Self)

```

And similarly you use term\_expansion/2 in `kb` to add additional arguments and if you see e.g.,  
`::percept(P)`, you translate that into `Agent:percept(P)`.

You can even omit the ^ and :: annotations by assuming any predicate defined in kb requires ^-transformation in the agent and in the `kb`, any not defined predicate requires the `::` translation. In that scenario you do not have to change a letter to your bulk code, but you must write some not that simple expansion code.

It is not obvious what route is better. That depends a lot on your preferences, workflow, the rest of the application, etc. Personally I’d seriously consider the above if it is the only OO work you want/need. If you want more OO type abstractions in your code Logtalk is probably a better long term choice.

---

<div class="post-metadata">

**Author:** ![jan](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/jan/32/4_2.png) [@jan](https://swi-prolog.discourse.group/u/jan)\
**Post date:** [April 19, 2019, 7:13am UTC](https://swi-prolog.discourse.group/t/modules-design-for-multi-agent-modeling/557/6 "2019-04-19T07:13:32Z")

</div>

> [@jan](#):
>
> The normal way to do this would be for your KB module to have an additional argument for the agent and use that to make module qualified goals

That said, I’ve now seen so many cases where I would like to share variables with sub goals that I’m to some time seriously thinking about providing such a beast. Note that except for argument passing which is either tedious or requires term rewriting, there is no good way in Prolog. In conventional languages you can use a global variable, save the old value on the stack, assign it and restore after return (not thread safe), but in Prolog that doesn’t work due to additional backtracking control flow.

I don’t think it is technically hard to do. It basically means creating nested environments on the stack and search them to find a name/value pair. If people have ideas on how to expose this in a nice way to the Prolog programmer, I’m all ears. I guess one way is to provide some sort of lexical scope that exceeds clauses and the other is something like `with_environment(Goal, [v1 = Val1, ...]).` and predicates to get and set the environment variables.

---

<div class="post-metadata">

**Author:** ![ssardina](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/ssardina/32/189_2.png) [@ssardina](https://swi-prolog.discourse.group/u/ssardina)\
**Post date:** [April 19, 2019, 7:57am UTC](https://swi-prolog.discourse.group/t/modules-design-for-multi-agent-modeling/557/7 "2019-04-19T07:57:07Z")

</div>

Hi Jan,

Thanks for your reply, I think I am almost there. I will see if I add LogTalk support in the next version, but that would have to be framed as a bit of a different project. For this particular, case I would like to see how far I can go with plain SWI.

Term/goal re-write is a good and clean approach. I understand how to expand the call to `location(X)` form the agent module, but didn’t get much of this:

> [@jan](#):
>
> And similarly you use term\_expansion/2 in `kb` to add additional arguments and if you see e.g.,  
> `::percept(P)` , you translate that into `Agent:percept(P)` .

The point is how to use `term_expansion/2` in the kb so that the call to `pecept(P)` becomes `Agent:percept(P)`.

The fact is that `location(X)` is a clause, not a goal. I have something like:

```prolog
location(X) :-
   percept(P),
   member(loc(X), P).

```

And this would need to be converted via `term_expansion/2` into:

```prolog
location(X, Agent) :-
   Agent:percept(P),
   member(loc(X), P).

```

is that what you meant? But this requires expanding the **head** of the clause. Unfortunately, I cannot get much insight on the [doc for term\_expansion/2](http://www.swi-prolog.org/pldoc/man?predicate=term_expansion/2), but I have found some example in the doc for [goal\_expansion/2](http://www.swi-prolog.org/pldoc/man?predicate=expand_goal/2).

It is possible to just use `term_expansion/2` to do the syntactic conversion of above? That would solve my problem I suspect.

Sebastian  
PS: how good is this forum, the use of markdown and on-the-fly preview is just great!

---

<div class="post-metadata">

**Author:** ![jan](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/jan/32/4_2.png) [@jan](https://swi-prolog.discourse.group/u/jan)\
**Post date:** [April 19, 2019, 8:28am UTC](https://swi-prolog.discourse.group/t/modules-design-for-multi-agent-modeling/557/8 "2019-04-19T08:28:23Z")

</div>

Yes, that is the transformation you need to make. It basically means adding a rule for term\_expansion/2 before the code in `kb`. That should look something like this:

```prolog
:- module(kb, ...).

transform_kb_clause(ClauseIn, ClauseOut) :-
   ...

term_expansion(ClauseIn, ClauseOut) :-
    transform_kb_clause(ClauseIn, ClauseOut).

<the old code>

```

The term\_expansion/2 rule gets the _complete_ term read by the compiler and can translate it into anything it likes, including a list of terms to create multiple clauses or a combination of directives and clauses. The output is analysed and the goals are handed to goal\_expansion/2.

Hope this helps. And yes, I think the move from Google groups to Discourse is a great improvement. Thank these guys for making this freely available to us, including free managed hosting.

---

<div class="post-metadata">

**Author:** ![pmoura](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/pmoura/32/11_2.png) [@pmoura](https://swi-prolog.discourse.group/u/pmoura)\
**Post date:** [April 19, 2019, 10:48am UTC](https://swi-prolog.discourse.group/t/modules-design-for-multi-agent-modeling/557/9 "2019-04-19T10:48:02Z")

</div>

> [@jan](#):
>
> As Paulo correctly remarks, `module_transparent` is indeed deprecated, though I do not expect it to be dropped anytime soon and probably never. Paulo is also right that the module system isn’t really designed for this and Logtalk is. That said, Logtalk is not more than a preprocessor for Prolog and if you need only one aspect of Logtalk you can quite easily replace this with some term/goal expansion and additional syntax that you like.

That’s like saying that Prolog is not more than a preprocessor for C. 🙂 It diverts the focus from the implemented **semantics** to the syntactic aspects. Its equivalent in the context of, say, constraints, would be to write that CLP(FD) is no more than a preprocessor and that you can easily replace it by hacking around `is/2` with some term/goal expansion.

Of course, you can (re)implement Logtalk using the term-expansion mechanism. As I had the opportunity to publicly clarify several times, including in SWI-Prolog forums, that’s not the implementation solution I use as it would reduce the number of Prolog systems capable of running Logtalk from (currently 12) to **1**.

> [@jan](#):
>
> The normal way to do this would be for your KB module to have an additional argument for the agent and use that to make module qualified goals. From the agent module this would mean something like this (adjust syntax to your preference)
> 
> ```prolog
> agent(Self) :-
> context_module(Self).
> 
> ```

Isn’t that definition for `agent/1` is missing the `:- module_transparent(agent/1).` directive?

> [@jan](#):
>
> ```prolog
> ...
> 
> ...,
> ^location(X),
> ....
> 
> ```
> 
> where `^location(X)` is translated using goal\_expansion/2 into
> 
> ```prolog
> agent(Self),
> kb:location(X, Self)
> 
> ```

How do you see that working when the user types:

```prolog
?- agt23:location(Location).

```

Sebastian is asking for a **multi-agent** application. With multiple agents loaded, you need to use explicitly module-qualified calls to call predicates such as `location/1`. In order to use your suggested `^location(X)` solution, every single agent would need to have a local definition for all public predicates from the `kb` module that would make the _super_ call in order to add the additional `Self` argument. Otherwise, as `location/1` is now `location/2`, **any** query to an agent would also need to be term-expanded. But if you do that, you have an additional problem: which `:/1` calls should be expanded and which `:/1` calls should be left alone (note that it doesn’t matter if the qualification is implicit or explicit). To distinguish those calls you need machinery, book-keeping, …, maybe a different operator?

One consequence of your suggestion is that **all** `kb` predicates would need to be term-expanded to carry one additional argument as (1) they may need to use that argument or (2) they may need to pass it to a sub-goal for a predicate that needs it. But then you need to take care of knowing when to add that additional argument and when to omit it (e.g. calling a predicate defined elsewhere).

But there’s still more. Your suggestion is to compile a _super_ call such as `^location(X)` to an explicit `:/2` goal with a fixed module argument. While the now explicit `Self` argument takes care of the fact that _super_ calls preserve _self_, you’re now constrained (without additional hacks) to a **flat** hierarchy model. The reason why Logtalk’s `^^/1` control construct abstracts the object that contains the predicate definition that is eventually called is the same reason why the `::/2` control construct abstracts the object that received the original message: the required information may not be available at compile time. I.e. your suggested compilation of a _super_ call is a possible compile-time optimization only when **valid**.

It should also be noted that, in the hack Sebastian describes, all public predicates in the `kb` module would need to be declared as module transparent. But that have more consequences than just enabling the use of `context_module/1`: it also changes the interpretation of any meta-call by the predicates.

> [@jan](#):
>
> And similarly you use term\_expansion/2 in `kb` to add additional arguments and if you see e.g.,  
> `::percept(P)` , you translate that into `Agent:percept(P)` .
> 
> You can even omit the ^ and :: annotations by assuming any predicate defined in kb requires ^-transformation in the agent and in the `kb` , any not defined predicate requires the `::` translation. In that scenario you do not have to change a letter to your bulk code, but you must write some not that simple expansion code.

As I illustrated above (and, to be clear, I only mentioned **some** of the hidden issues), even with annotations, the expansion code is “not that simple”. Your “quite easily replace” opening statement is challenged even in the limited scenarios discussed/hinted here. Add a few more sensible usage scenarios, typical of a multi-agent/multi-worlds applications, and you’re in trouble.

> [@jan](#):
>
> It is not obvious what route is better. That depends a lot on your preferences, workflow, the rest of the application, etc. Personally I’d seriously consider the above if it is the only OO work you want/need. If you want more OO type abstractions in your code Logtalk is probably a better long term choice.

Indeed, clear application requirements, now and foreseen as the application evolves, and a clear understanding of pros and cons of each solution is key.

Cheers,  
Paulo

---

<div class="post-metadata">

**Author:** ![pmoura](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/pmoura/32/11_2.png) [@pmoura](https://swi-prolog.discourse.group/u/pmoura)\
**Post date:** [April 19, 2019, 10:59am UTC](https://swi-prolog.discourse.group/t/modules-design-for-multi-agent-modeling/557/10 "2019-04-19T10:59:35Z")

</div>

P.S. For some discussion of the pros and cons of Logtalk vs Prolog modules, see [https://logtalk.org/rationale.html](https://logtalk.org/rationale.html)

---

<div class="post-metadata">

**Author:** ![jan](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/jan/32/4_2.png) [@jan](https://swi-prolog.discourse.group/u/jan)\
**Post date:** [April 19, 2019, 11:35am UTC](https://swi-prolog.discourse.group/t/modules-design-for-multi-agent-modeling/557/11 "2019-04-19T11:35:23Z")

</div>

> [@pmoura](#):
>
> That’s like saying that Prolog is not more than a preprocessor for C. 🙂

Not really. Logtalk compiles to Prolog. Prolog isn’t compiled to C but to a VM that is implemented in C. Of course it is _possible_ to compile Prolog to C, as _wamcc_ does/did.

I’m not going to argue with you that Logtalk has better support for this than plain (SWI-)Prolog. It clearly has. It is not true that you cannot do such things in plain (SWI-)Prolog though. Its module system is pretty versatile.

The complete rewrite of the `kb` module to pass the additional argument is of course painful. Can we do better? Performance-wise probably not. If performance is less of an issue we have some alternatives. One is to use the [user:exception/3](http://www.swi-prolog.org/pldoc/doc_for?object=exception/3) hook. You can use that to trap undefined calls in the agents. Considering location/1, we let the hook create a wrapper that provides access to _self_:

```prolog
location(X) :-
     context_module(Self),
     with_self(Self, kb:location(X)).

```

And we define with\_self/2 in e.g. kb as an exported predicate like below. The `no\_lco\_ is needed to avoid last call optimization while calling Goal. We pass this Module to avoid it being garbage collected.

```prolog
with_self(Module, Goal) :-
     Goal,
     no_lco(Module).

no_lco(_).

```

Now we can define call\_self/1 as

```prolog
call_self(Goal) :-
    prolog_current_frame(F),
    prolog_frame_attribute(F, parent_goal, kb:with_self(Module, _)),
    call(Module:Goal).

```

And all we need to do is use goal expansion in `kb` to do the call\_self. Wait, we can also use the above undefined predicate exception to define on the fly

```prolog
percept(P) :-
    call_self(percept(P)).

```

Instead of doing this all on demand you can of course also add some meta description to the interface and generate all this in advance. We can also use meta-programing for the user call. There are endless options, providing trade offs between work, readability, maintainability and performance.

With Logtalk you get a well designed and documented total package. With plain Prolog you have to shape the world first, but you can keep it lightweight, make it do exactly what you want and make a trade off between all the options.

---

<div class="post-metadata">

**Author:** ![ssardina](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/ssardina/32/189_2.png) [@ssardina](https://swi-prolog.discourse.group/u/ssardina)\
**Post date:** [April 19, 2019, 1:33pm UTC](https://swi-prolog.discourse.group/t/modules-design-for-multi-agent-modeling/557/12 "2019-04-19T13:33:05Z")

</div>

wow, this is indeed a very nice exchange.

I implemented a tou example using `term_expansion/2` and `goal_expansion/2` so that the KB can call to the corresponding “caller” module.

What I realized doing that is what both of you just pointed out:

> The complete rewrite of the `kb` module to pass the additional argument is of course painful.

What makes it more complicated is that I don’t have the kb reasoning toolkit in just one module, but in _many modules_ (actions, charge, items, etc…). So, a predicate X in the actions module may call one Y in charge module, which itself will need to post a goal Z _against the original agent module_ (the one who called X in actions)! This means that the caller module should be passed across all predicates.

This makes me wonder that it is not so easy to design a mechanism, because in a sequence of goal calls, there will be many different contexts. which one we would want? the very first one? Why? not easy.

I am studying @jan last solution, it’s pretty smart, although using exceptions for this sounds a bit non-natural (of course, I am expecting everything can be done with any of this systems, as they are all turing-complete 🙂 )

Thanks nonetheless to both for the rich exchanges.

Sebastian  
PS: @Jan, maybe would be good to add something like this as example for `term_expansion/2`?

```prolog

:- module(test_kb, [far_away/2]).

% operator to signal when the predicate should be expanded with context module call
:- op(1, fx, ^).

% List all predicates that need to be added 1 more argument to track the calling module
transform_kb_clause(far_away(G):-Body, far_away(G, Agent):-Body2) :-
	add_agent_module(Agent, Body, Body2).

% domain-independent
term_expansion(ClauseIn, ClauseOut) :-
    transform_kb_clause(ClauseIn, ClauseOut).

% Add Agent as calling module in SOME predicates; here, only on percepts/1	
add_agent_module(Agent, ^G, Agent:G2) :- !,
	G =.. [F|Args],
	append(Args, [Agent], Args2),
	G2 =.. [F|Args2].
%add_agent_module(Agent, percepts(X), Agent:percepts(X)) :- !.
add_agent_module(Agent, (G1, G2), (G1_New, G2_New)) :- 
	add_agent_module(Agent, G1, G1_New),
	add_agent_module(Agent, G2, G2_New), !.
add_agent_module(Agent, (G1; G2), (G1_New; G2_New)) :- 
	add_agent_module(Agent, G1, G1_New),
	add_agent_module(Agent, G2, G2_New), !.
add_agent_module(Agent, (G1 -> G2), (G1_New -> G2_New)) :- 
	add_agent_module(Agent, G1, G1_New),
	add_agent_module(Agent, G2, G2_New), !.
add_agent_module(_, G, G).
	

% Is destination far from the current agent location?
% Agent location LocAgent is obtained via the percept of the agent, which is not in this module
% but in the agent "calling" module
far_away(Destination) :-
	^percepts(P),
	member(loc(LocAgent), P), !,
	distance(LocAgent, Destination, D),
	D > 10.
	

distance(L1, L2, D) :- D is abs(L1-L2).

```

will produce this clause when consulting:

```prolog
far_away(D, A) :-
	call(A:percepts(B, A)),
	member(loc(C), B), !,
	distance(C, D, E),
	E>10.

```

What I do not like here are the `add_agent_module/3` clauses to process the Body. I am sure it is not even complete!

---

<div class="post-metadata">

**Author:** ![jan](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/jan/32/4_2.png) [@jan](https://swi-prolog.discourse.group/u/jan)\
**Post date:** [April 19, 2019, 1:52pm UTC](https://swi-prolog.discourse.group/t/modules-design-for-multi-agent-modeling/557/13 "2019-04-19T13:52:22Z")

</div>

> [@ssardina](#):
>
> last solution, it’s pretty smart, although using exceptions for this sounds a bit non-natural

You not _need_ exceptions. Goal expansion in agents, wrapping all calls to the `kb` in e.g., call\_kb/1 and goal expansion in the kb module that maps all undefined predicates to call\_self/1 is enough. That might be more elegant. You probably want some declaration in the kb as to what you want to call in the agents and use that to decide on call\_self/1 or not, e.g,

```
agent_hook(percept/1).
...

```

> [@ssardina](#):
>
> maybe would be good to add something like this as example for `term_expansion/2` ?

Actually, the latter job would need to be handled by goal\_expansion. This requires some way to know the added argument. That can be done, but is quite far outside the documented (and thus supported) interface. You might get away calling expand\_goal/2 on the body from your term expansion rules. In that case you can use a global variable (b\_setval/2) to provide access to the added argument, so you get something like this

```prolog
expand((Head0 :- Body0), (Head :- Body)) :-
     expand_head(Head0, Head, ExtraArg),
     b_setval(extra_arg, ExraArg),
     expand_goal(Body0, Body),
     b_setval(extra_arg, []).

```

I’m in favor of an example, but something typical and simple rather than this things along the edges of what one may want (and context arguments should be handled differently IMO).

---

<div class="post-metadata">

**Author:** ![pmoura](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/pmoura/32/11_2.png) [@pmoura](https://swi-prolog.discourse.group/u/pmoura)\
**Post date:** [April 19, 2019, 3:52pm UTC](https://swi-prolog.discourse.group/t/modules-design-for-multi-agent-modeling/557/14 "2019-04-19T15:52:40Z")

</div>

> [@jan](#):
>
> > That’s like saying that Prolog is not more than a preprocessor for C. 🙂
> 
> Not really. Logtalk compiles to Prolog. Prolog isn’t compiled to C but to a VM that is implemented in C. Of course it is _possible_ to compile Prolog to C, as _wamcc_ does/did.

I stand corrected: That’s like saying that Prolog is not more than a preprocessor for VM instructions 🙂

P.S. For readers not familiar with wamcc, it was the predecessor of GNU Prolog.

> [@jan](#):
>
> I’m not going to argue with you that Logtalk has better support for this than plain (SWI-)Prolog. It clearly has. It is not true that you cannot do such things in plain (SWI-)Prolog though. Its module system is pretty versatile.

Logtalk is written in plain, portable, Prolog code, that runs in (among several systems) SWI-Prolog. Thus, its very existence is **proof** that you can do these things in plain (SWI)-Prolog. The **difference** is that in Logtalk the necessary work is already done. But doing it on top of the module system? All the solutions you are suggesting in this thread are arguably about working around, i.e. _fighting_ the nature of the module system. There’s nothing _fundamental_ in modules as a concept (when compared with objects) that justify that expect for the fact that there’s already an implementation in place.

> [@jan](#):
>
> The complete rewrite of the `kb` module to pass the additional argument is of course painful. Can we do better? Performance-wise probably not. If performance is less of an issue we have some alternatives. One is to use the [user:exception/3](http://www.swi-prolog.org/pldoc/doc_for?object=exception/3) hook. You can use that to trap undefined calls in the agents. Considering location/1, we let the hook create a wrapper that provides access to _self_ :
> 
> ```prolog
> location(X) :-
> context_module(Self),
> with_self(Self, kb:location(X)).
> 
> ```
> 
> And we define with\_self/2 in e.g. kb as an exported predicate like below. The `no\_lco\_ is needed to avoid last call optimization while calling Goal. We pass this Module to avoid it being garbage collected.
> 
> ```prolog
> with_self(Module, Goal) :-
> Goal,
> no_lco(Module).
> 
> no_lco(_).
> 
> ```
> 
> Now we can define call\_self/1 as
> 
> ```prolog
> call_self(Goal) :-
> prolog_current_frame(F),
> prolog_frame_attribute(F, parent_goal, kb:with_self(Module, _)),
> call(Module:Goal).
> 
> ```

I may be misunderstanding something in the semantics of `prolog_frame_attribute/3` but… you may have, say, the user making a query `agent:foo` that you are transforming into a call to `kb:foo`, only that `kb:foo` calls `kb:bar` that in turn calls `kb:baz` with is the one making the call in _self_. Will `parent_goal` work in this case?

Also, as I mentioned earlier, you are effectively making `location/1` a meta-predicate in order just ot be able to call `context_module/1`.

> [@jan](#):
>
> And all we need to do is use goal expansion in `kb` to do the call\_self. Wait, we can also use the above undefined predicate exception to define on the fly
> 
> ```prolog
> percept(P) :-
> call_self(percept(P)).
> 
> ```
> 
> Instead of doing this all on demand you can of course also add some meta description to the interface and generate all this in advance. We can also use meta-programing for the user call. There are endless options, providing trade offs between work, readability, maintainability and performance.
> 
> With Logtalk you get a well designed and documented total package. With plain Prolog you have to shape the world first, but you can keep it lightweight, make it do exactly what you want and make a trade off between all the options.

Logtalk indeed will add around ~460KB (if memory serves) to the application.

On the other hand, all Logtalk developer tools (debugger, documenting, testing, …) allow the user to work at the same abstraction level as the source code. In your suggested solution, the user will be e.g. tracing the expanded code, not the original source code.

Cheers,  
Paulo

---

<div class="post-metadata">

**Author:** ![pmoura](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/pmoura/32/11_2.png) [@pmoura](https://swi-prolog.discourse.group/u/pmoura)\
**Post date:** [April 19, 2019, 4:10pm UTC](https://swi-prolog.discourse.group/t/modules-design-for-multi-agent-modeling/557/15 "2019-04-19T16:10:54Z")

</div>

> [@ssardina](#):
>
> wow, this is indeed a very nice exchange.
> 
> I implemented a tou example using `term_expansion/2` and `goal_expansion/2` so that the KB can call to the corresponding “caller” module.
> 
> What I realized doing that is what both of you just pointed out:
> 
> > The complete rewrite of the `kb` module to pass the additional argument is of course painful.

I cannot help myself than remember here the Greenspun’s tenth rule: [Greenspun's tenth rule - Wikipedia](https://en.wikipedia.org/wiki/Greenspun%27s_tenth_rule) 😃 😃 😃

> [@ssardina](#):
>
> What makes it more complicated is that I don’t have the kb reasoning toolkit in just one module, but in _many modules_ (actions, charge, items, etc…). So, a predicate X in the actions module may call one Y in charge module, which itself will need to post a goal Z _against the original agent module_ (the one who called X in actions)! This means that the caller module should be passed across all predicates.
> 
> This makes me wonder that it is not so easy to design a mechanism, because in a sequence of goal calls, there will be many different contexts. which one we would want? the very first one? Why? not easy.

You can do this (and everything else that you have been discussing for your application) easily in Logtalk. Messages can be delegated, which preserves the original _sender_ (the agent in your case where you want "to post a goal Z "). More to the point, you can do all this using only standard Logtalk constructs, without writing a single term-expansion/goal expansion rule, while saying at a nice abstraction level.

Cheers,  
Paulo

---

<div class="post-metadata">

**Author:** ![jan](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/jan/32/4_2.png) [@jan](https://swi-prolog.discourse.group/u/jan)\
**Post date:** [April 19, 2019, 4:25pm UTC](https://swi-prolog.discourse.group/t/modules-design-for-multi-agent-modeling/557/16 "2019-04-19T16:25:12Z")

</div>

> [@pmoura](#):
>
> I may be misunderstanding something in the semantics of `prolog_frame_attribute/3` but… you may have, say, the user making a query `agent:foo` that you are transforming into a call to `kb:foo` , only that `kb:foo` calls `kb:bar` that in turn calls `kb:baz` with is the one making the call in _self_ . Will `parent_goal` work in this case?

We can emulate a notion of an execution environments with variables that are scoped to the children of a goal by

- Creating a (often) meta call that makes these environment variables available through its arguments.
- Make sure this goal stays on the stack while we execute the children (avoid LCO)
- In the child, use SWI-Prolog’s runtime query routines on stack frames to find the variable value in the parent frame.

This does some of the work you could also do with delimited continuations, but cheaper and without any interference with Prolog’s semantics. That is all in all not very different than what you do in Logtalk: add a structure that is passed between all calls. In this case we do not need to pass anything. The price we pay is one intermediate call that cannot be subject to LCO and walking the stack if we need access to this variable (and portability, although any Prolog system should be able to do the same without much trouble). If the stack contains many intermediate frames and you need frequent access deep down you pay repetitive walks along the stacks.

Given thus though, we can make a _super_ call, providing information on where we were called from and a _self_ call that uses this information. You can use SWI-Prolog’s inheritance over modules to specialise modules. Surely there is a lot more good stuff in Logtalk, but if you merely want specialization and messages to sub/super you have all you need. Depending on how you implement it, the SWI-Prolog debugger should have no problems with this trick.

I’ve used that in the past to create a lightweight object system that interacted with XPCE classes.

---

<div class="post-metadata">

**Author:** ![pmoura](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/pmoura/32/11_2.png) [@pmoura](https://swi-prolog.discourse.group/u/pmoura)\
**Post date:** [April 19, 2019, 4:37pm UTC](https://swi-prolog.discourse.group/t/modules-design-for-multi-agent-modeling/557/17 "2019-04-19T16:37:35Z")

</div>

> [@jan](#):
>
> > I may be misunderstanding something in the semantics of `prolog_frame_attribute/3` but… you may have, say, the user making a query `agent:foo` that you are transforming into a call to `kb:foo` , only that `kb:foo` calls `kb:bar` that in turn calls `kb:baz` with is the one making the call in _self_ . Will `parent_goal` work in this case?
> 
> We can emulate a notion of an execution environments with variables that are scoped to the children of a goal by  
> …

Thanks for the detailed reply. I always expected something like you describe to be possible but never looked into the details (mainly because they are system specific). That provides an interesting venue to explore for a hypothetical alternative implementation of Logtalk (asa language) that would be more “native” in SWI-Prolog. Hope we can discuss this further soon. I should pay you a visit. Or you pay me a visit 🙂

---

<div class="post-metadata">

**Author:** ![jan](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/jan/32/4_2.png) [@jan](https://swi-prolog.discourse.group/u/jan)\
**Post date:** [April 19, 2019, 4:46pm UTC](https://swi-prolog.discourse.group/t/modules-design-for-multi-agent-modeling/557/18 "2019-04-19T16:46:06Z")

</div>

> [@jan](#):
>
> I’ve used that in the past to create a lightweight object system that interacted with XPCE classes.

I’ve uploaded the old code to Github. Don’t think the application is still fully operational (although it still starts). The mini object system is in [https://github.com/JanWielemaker/triple20/blob/c271a58a53afe222df62b3dc56237e57178a02b4/src/particle.pl](https://github.com/JanWielemaker/triple20/blob/c271a58a53afe222df62b3dc56237e57178a02b4/src/particle.pl)

> [@pmoura](#):
>
> That provides an interesting venue to explore for a hypothetical alternative implementation of Logtalk

I think it does. Not unlikely we will be able to create some opportunity to explore that 🙂 Right now too busy with tabling …

---

<div class="post-metadata">

**Author:** ![anniepoo](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/anniepoo/32/12_2.png) [@anniepoo](https://swi-prolog.discourse.group/u/anniepoo)\
**Post date:** [April 19, 2019, 7:19pm UTC](https://swi-prolog.discourse.group/t/modules-design-for-multi-agent-modeling/557/19 "2019-04-19T19:19:32Z")

</div>

Another option is to reify by treating the ‘act’ as belief.

believes(bob, location(bob, paris)).  
believes(alice, location(alice, new\_york)).

then if alice has an opinion about where bob is,  
believes(alice, location(bob, chicago)). % alice incorrectly believes bob is in chicago

an alternative to passing the agent name everywhere is b\_setval(agent\_context, bob)  
then location does b\_getval(agent\_context, AgentName), percept(AgentName, P).

I’m wondering why you feel you should have a single percept for each agent, rather than  
a bunch of components

---

<div class="post-metadata">

**Author:** ![anniepoo](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/anniepoo/32/12_2.png) [@anniepoo](https://swi-prolog.discourse.group/u/anniepoo)\
**Post date:** [April 19, 2019, 7:25pm UTC](https://swi-prolog.discourse.group/t/modules-design-for-multi-agent-modeling/557/20 "2019-04-19T19:25:56Z")

</div>

video on term expansion

[Next page](https://swi-prolog.discourse.group/t/modules-design-for-multi-agent-modeling/557.md?page=2)
