14.3.6. Refining understanding of resource removal

Reconsider these three important rules of the execution model:

  1. Statements are executed in order of appearance in the model source text;

  2. Statements in an on exit clause are executed before the resource is actually removed from the structure of contexts and roles.

  3. When a resource changes state during an execution stage, the effects of this state change will only occur in the next stage.

These three rules are not compatible, as we will illustrate with this example:

thing SomeRole
  on entry
    do
      remove role currentcontext >> AnotherRole
      create role YetAnotherRole in currentcontext
thing AnotherRole
  on exit
    do
      create role TheThirdRole in currentcontext

Clearly, rule 1 dictates that the AnotherRole instance must be gone by the time that the instance of YetAnotherRole is created. However, rule 3 says that the action on exit of the instance of AnotherRole can only be executed in the next phase (that is, after both statements have been executed). And rule 2 states that these actions must be completed while the AnotherRole instance is still there.

14.3.6.1. Solution: monotonic inference first

We solve this problem by, in effect postponing the actual removal of resources until the very last moment. This means that, seen per on entry or on exit clause, removal statements always come last. So the first part of our example is equivalent to (and should be written as):

thing SomeRole
  on entry
    do
      create role YetAnotherRole in currentcontext
      remove role currentcontext >> AnotherRole

Removal comes last. This holds recursively, for ‘nested’ automatic actions. As a consequence, execution of automatic actions is divided in two steps:

  1. First, all additions to the structure are made, recursively, all the while postponing any removal encountered, while yet executing all on exit clauses of resources that are to be removed (and, obviously, any actions on entry as well);

  2. Then, in one fell sweep, all resources marked for removal are detached from the network.

This may, of course, trigger fresh state changes in some resources, so then the entire process begins again.

Complicated though this may seem, it actually has a desirable characteristic: to understand the execution of automatic actions in a model, you can try to understand the additions independently from the removals. Both can be understood as monotonic inferences from the state of all resources. This is good, because it means we can analyse what happens in terms of ordinary mathematical logic.

In other words: you can use logical inference to determine from a given overall state:

  • what will be added to the structure, and, independently,

  • what will be taken away from the structure;

Then consider the new state that arises when new things are first added and then some others are removed.

Actually, it does not matter whether we first add and then remove the results, or the other way round. This is because the system is robust enough not to fail if we try to add a role instance to a context that does not exist. But it’s certainly more elegant and efficient to first add and then remove.
14.3.6.2. Caveat: when a condition result depends on a resource to be removed

Imagine a state condition that checks whether an instance of a functional role exists. Clearly, the outcome of that query would be affected by removing the instance! But in the above, we’ve written that resources are only actually removed when all other work has been done - including evaluating state conditions! So what would happen is that, given that a role instance exists, the state query would return true - but then the role instance would be removed, clearly invalidating the condition. But it would be too late to notice!

We have a partial remedy against this problem. If we detect a state condition result that depends on a removed resource, we re-evaluate that state again after all resources have been removed. Such conditions will then be triggered. However, the consequence is that any actions or notifications that are in their lexical scope, will be performed in the state that results after removal of resources.

It may be easy to see where that will lead to problems, as in this example:

context C
  state SomeRoleExists = exists SomeRole
    on exit
      notify U
        "The role {SomeRole >> Name} no longer exists."

  thing SomeRole
    property Name (String)

  user U
    perspective on SomeRole
      props (Name) verbs (Consult)

By the time we establish that, indeed, state SomeRoleExists no longer holds for an instance of context C, it is too late to read its Name. This doesn’t cause an error but the notification is less informative than intended.

It may not always be as obvious that a condition depends on removal. Keep the following in mind:

  • a state condition may be written in terms of a type that is calculated and the calculation may depend on a resource that will be removed. As the resource type will not appear as is in the state condition, such a condition may escape your notice;

  • the resources used in the predicate of a filtered role or property also affect the outcome and so such states are re-evaluated after resource removal, too.