10.3.5. Contextualisation of actions
Actions consist of statements. Some statement types mention a role- or context type to create:
create role RoleType [in <contextExpression>] bind <binding> to RoleType [in <contextExpression>] unbind <binding> [from RoleType] create context ContextType bound to RoleType in <contextExpression> create_ context ContextType bound to <roleExpression>
In these statements, RoleType refers to some role that should be defined for the current context, or the type of context that results from <contextExpression>.
We have two kinds of Action: those defined in lexical positions with a current object (where the action will be applied to that object), and without a current object (these actions will be applied to the current context). We call the former kind perspective actions and latter kind context actions.
Both kinds must be contextualised, when the user role with access to the actions is used as an aspect for a specialised user role, or is included as is in a context. The point is that the RoleType (and ContextType) may be specialised in the contextualising context, too, in which case this specialisation should be substituted for the original in the assignment statements.
Returning to the Driver-Pilot example: let’s assume for the sake of the explanation that the Driver can create the Vehicle role. Clearly, we want the Pilot to create a Plane, rather than a Vehicle. So we substitute Plane for Vehicle in the actions of the Pilot.
10.3.5.1. The consequences of compile time contextualisation
Skipping the details of how to contextualise an action (more about this later), we ask ourselves: where can we store the contextualised actions?
A contextualised context action cannot be tied to its user role’s representation, since we can incorporate an aspect user role as is in as many contexts as we like. Nevertheless, we’d have to contextualise the action for each such context. Obviously, a contextualised action is specific to the combination of a user role and a context. We can save Actions directly in the DomeinFile in a map with keys constructed from context- and user role types.
What about contextualised perspective actions? Currently (version v0.18.0) we store perspective actions in the perspectives. We could contextualise the perspective holding contextualised actions, but then store perspectives in the DomeinFile, again under a key constructed from context- and user role type. In other words, if we incorporate a(n aspect) user role into a context, we create a specialised version of the perspectives of that user role.
This requires a major change to the implementation, however. This is because the above implies we conceive of a perspective as a relation between two role-in-a-context combinations, rather than between two roles. On the perspective object side, we’ve already tackled that issue (because of query inversion). The abstract data types that we use to describe query domains and ranges are in terms of combinations of role and context. However, on the perspective subject side it means a complete overhaul of many modules.
Compile time contextualisation of actions requires a major refactoring.
10.3.5.2. How to contextualise an Action
Before deciding on compile time versus runtime contextualisation of actions, we explore how to contextualise individual statements.
Create role
The create operator in conjunction with the role keyword:
create role RoleType [in <contextExpression>]
mentions a RoleType that must be contextualised. Let us work with a different example to create some intuition: we have an Aspect context Meeting with a role Organizer and a role Participant. Now, we create a MedicalAppointment with:
-
a role Physician and a role Patient, both having aspect Participant
-
a role Assistant, having aspect Organizer.
Organizer can create Participant role instances and has an action to create one. What happens when Assistant executes that action? As there are two specialisations of Participant, two instances will be created: a Physician and a Patient.
Contextualisation of this action in compile time would result in two statements in the action:
create role Physician [in <contextExpression>] create role Patient [in <contextExpression>]
However, we have a variant of this assignment operator that lets us save the result in a letA variable. This introduces a dilemma: should we create an ad hoc new variable? Or can we assume that the variable can hold multiple values, and will semantics of the rest of the action be conserved, under this change?
Contextualisation of this action in run time would require an adaptation of the code that is compiled from the CreateRole datastructure. We would have to look up the specialisations of Participant in MedicalAppointment, keep only those that the user executing the action has a sufficient perspective on and then iterate over those specialisations.
Both approaches need the same lookup (of local specialisations of the RoleType). While compile time contextualisation is not quite clear, run time contextualisation is conceptually simple.
Bind
The bind operator:
bind <binding> to RoleType [in <contextExpression>]
needs to be handled just like the create role operator. It, too, creates a role instance.
Unbind
The unbind operator:
unbind <filler> [from RoleType]
works by clearing those roles filled by filler that have type RoleType. However, no roles will be cleared that have RoleType as a supertype (an aspect of their type). In terms of our example: if the instruction is to unbind from Drivers, no Pilot roles will lose their filler. In compile time, we should replace RoleType with the specialised role type to contextualise the action. Again, if multiple role types are specialised, we’d have to duplicate the statement for each of them.
However, we can handle it in runtime, too, relying on the alias administration in the filler role instances described above for the filled role step. Each time we fill a Pilot role instance with some role instance R, we’d have to record on R that Pilot is an alias for Driver. Then, when unbind is carried out, we look up Driver in the alias administration of R, find Pilot, then read from R what role instances are recorded as filled under the key Pilot. Finally, we’d clear the filler from those instances.
Create context
The create operator can also be used in conjunction with the context keyword:
create context ContextType bound to RoleType in <contextExpression>
The RoleType must be contextualised just like in the create role situation. The ContextType must be replaced, too: by the type of context that may be bound to the substitution of RoleType.
To extend our Meeting and MedicalAppointment example, let’s assume there is a Calendar context with a role Meetings that holds contexts of type Meeting. And let’s assume that Calendar is added as an aspect to HospitalCalendar, with a role HospitalMeetings that has aspect Meetings. It, however, restricts its fillers to MedicalAppointment. Now, some user in HospitalCalendar with an aspect role that is allowed to create Meetings, should create an HospitalMeetings role instance rather than a Meetings instance. And it should fill it with a MedicalAppointment, instead of a Meeting. To accomplish this, the runtime first finds that HospitalMeetings is the local substitution for Meetings and then discovers MedicalAppointment as its filler restriction, being a specialisation of Meeting.
Create_ context
The create_ operator is a variant of the create operator, in that it omits the RoleType to create and instead retrieves an existing role instance that should be filled with a new context:
create_ context ContextType bound to <roleExpression>
Looking up the ContextType goes exactly in the same way as with create.
10.3.5.3. Wrapping up: contextualising Actions
Compile time contextualisation of actions requires a major refactoring. In contrast, run time contextualisation requires just part of the mechanisms that are required for compile time contextualisation (the lookup of type substitutions for statements with the operators create, create_, bind and unbind.