-
Notifications
You must be signed in to change notification settings - Fork 3
Reference Actions Tech Note
mint.sm-meta.action.tn.4 / Leon Starr / Version 0.2.0
This technical note explores the primitive actions necessary to create, update and delete references to instances on class model relationships.
Andrew Mangogna has written an excellent paper laying the foundations for this work. We will begin from these principles and define the necessary actions to incorporate into our action language metamodel.
The most fundamental principle is that Shlaer-Mellor class models are an application of relational theory and relational theory is a branch of mathematics [MATHDP].
To create an instance of a class you must supply a value for each of its attributes. In relational theory the concept of a 'null value' is an oxymoron. Null values result in a thing called Three Valued Logic (3VL) which is problematic.
The net of all this is that if nulls are present, then we’re certainly not talking about the relational model (I don’t know what we are talking about, but it’s not the relational model); the entire edifice crumbles, and all bets are off. – C.J. Date
In practice, this means, among other things, that we cannot create an instance of a class that references an instance of some other class if that referenced instances does not exist.
When we talk about flowing a set of instance references what we actually mean is that we are flowing a populated table (relation) whose header matches an identifier of the associated class. In other words the header consists of a column for each attribute in the identifier. Each row of the table then refers to some instance of that class.
Pointers, handles, and such are programming implementation concepts and do not exist in relational theory. You can reference an instance via an identifier value only.
LS: There is a disagreement in terminology between Andrew and myself. Andrew defines an instance reference as a relation with a set of rows and thus potential references to multiple instances of the same class. I use the term 'instance reference set' for this concept and reserve 'instance reference' to refer to a single instance. I believe this is just a dispute over terminology and will stick to my definition in construction of the action language metamodel (unless convinced otherwise!)
The concept of creating 'links' between instances is not valid. All you can do is set the values of referential attributes in such a way as to refer to some other instance.
Now let's move on to the defining our primitive actions.
We'll refer to an association relationship that is not formalized by an association class as a 'simple association'.
In the example below we must assign each Control Zone
to some On Duty Controller
. This means that if we create a new instance of Control Zone
it must be assigned to an On Duty Controller
immediately.
To accomplish this we don't need a separate create and link action. The create action suffices since it must initialize all attributes of a class and this includes any referential attributes. Consequently, it is impossible to create an instance of Control Zone
without simultaneously instantiating a reference on R2
. This is a great way to preserve model integrity and one of the many features of the relational model of data.
Speaking of integrity, we do need to be careful when setting the value of that referential attribute. A referential attribute is constrained to hold a value that exists in a referenced instance. So ATC7
must correspond to an instance of On Duty Controller
with the same value in its ID
attribute.
Rather than requiring that the user manipulate the value directly, we will need another primitive action that takes the association name R2
and the referenced instance as input to yield that referential attribute value.
We can knit the two together like so:
The Scrall for this could be:
*Control Zone( Name: cz name, Traffic: traffic quantity ) &R2 assigned controller
The action language above brings in the non-referential attributes via input scalar flows (variables) cz name
and traffic quantity
. (Scrall forbids you from putting literal values in your action language) The *
symbol means 'create' and the &R2
clause specifies both the association name and the target instance reference (as named instance set flow).
Right, but what if we want to hand off a Control Zone
to a different On Duty Controller
? Do we need a re-link action? No we do not.
We just need to update the referential attribute value which suggests a primitive update action.
This is a common usage, so we propose a composite action:
The Scrall being:
handoff czone &R2 new controller
The 'Control Zone' to hand off is related to a different 'On Duty Controller'
There is no need to unlink and relink. We simply update the value of a referential attribute and we're safely done,
On a simple association, deleting a reference is achieved by deleting the referring instance. So all we need is a simple delete instance action.
And the Scrall could be:
!* deactivated cz // Delete this control zone
The !*
symbol means 'delete' and it is applied to the deactivated cz
instance flow.
We'll call an association formalized by an association class an associative relationship
. Andrew Mangogna came up with this term and it's a little less confusing than calling it an `association class association'!
The same principles apply, but we need to manage the references a bit differently. Let's use this example going forward:
Let's say that a Vehicle
decides to change lanes. A target Lane
is selected and now we want to relate them together. We do this by creating an instance of a Lane Change
. Again, no special link action is required. But we don't want to do a simple create since we need to constrain our referential attribute values properly as we did with the simple association example. We can't use the Get reference primitive because it only takes one instance reference and we need two, one for each side, for an associative relationship. So let's define that primitive:
To create an instance of the association class we'll need a reference to each side of the association. It is helpful to have a way to refer to these sides and I have adopted the convention of calling them T and P since the Southpark characters Terrance and Phillip are similarly separate, yet indistinct.
Now we can couple this primitive with the create action...
...and then, let's go one step further and box them up into a useful composite action:
In Scrall we could write:
lc .= my vehicle &R5 target lane, *Lane Change( Initiation: Date.Now hms() )
In the action language above we use an assignment statement to capture an instance reference to the newly created instance of Lane Change
.
Alternatively, we could fire off an asynchronous creation event from a local Vehicle instance with similar information:
Initiate lane change( Initiation: Date.Now hms() ) -> *Lane Change &R5 me, target lane
In the example Scrall above me
is a keyword that provides an instance reference to the local vehicle instance.
In both cases, we provide the same information and leave it up to the composite action to implicitly set the association class referential attributes correctly.
Continuing with our example, let's say that we change our minds and decide to enter a different lane. We can handle this two ways. We could simply delete the association class instance and then create a different one. This is a good choice if we need to go back to an initial state (when the association class has a lifecycle) or if we need to gather a new set of non-referential attribute values. Otherwise, we could leave the association class instance intact and simply update the target lane reference. The choice is up to the modeler so we need to accommodate both possibilities in our metamodel.
We can use two of our existing primitive actions to update the reference to the target lane. It turns out that our simple association primitives work since we are still referring from one class to another, with the only difference being that our referring class is an association class.
The Scrall for this example could be:
my vehicle &R5 alternate lane, lc
Here we relate the vehicle, my vehicle
to the new choice of lane, alternate lane
using the same association class instance, lc
.
And we already have a delete primitive if we want to delete the association class, so we don't need anything further. So we could do it with a statement like this in Scrall:
!*lc // deletes the lane change instance
Updating a reference within a generalization is handled by the migration actions described in the Data Flow Semantics Tech Note.
That just leaves the problems of creating and deleting a generalization.
Let's start by creating an instance of an Off Duty Controller
in our Air Traffic Controller
generalization.
We don't need any new primitives to create super and subclass instances. But we must take care to create the superclass first and then flow a reference to it to the subclass so that we have all our attribute values ready for each create action.
We could write Scrall for this like so:
off atc .= *Off Duty Controller( Last shift ended: end of shift time ) &R1 \
*On Duty Controller( Name: atc name, Rating: atc rating )
The execution order in either representation is driven by data dependencies, so the On Duty Controller
instance must be created first since all of its data is initially available, followed by the Get reference
action followed by the subclass create action. The instance flow output for the subclass is assigned in the Scrall snippet since it is closest to the assignment operator, in case you were wondering.
Now let's delete the very same instance:
Here we combine the Navigate and Delete primitives with some necessary sequencing. It is critical that we first navigate to the superclass from the subclass instance. Having both instances to delete, we first delete the subclass since it holds a reference to the superclass. Only then can we safely delete the superclass. This sequence maintains referential attribute integrity.
And the Scrall can be:
atc super .= off atc/R1
!* off atc <1>
<1> !* atc super
This is a bit awkward and we don't normally do it this way in Scrall, but it is helpful to visualize a direct text representation of our exploration thus far.
But what happens when we have a multi level generalization? Worse yet, what happens when we have a class participating as a subclass in more than one generalization?
We want to ensure that when an instance of a class participating in one or more generalizations is deleted, it is the modeler's responsibility to ensure that all related super and subclasses through all connected generalizations are removed.
That said, we can make use of our combined class and action language metamodels to ensure that everything that needs to be deleted is deleted and in the proper order. So if the modeler does not delete all related generalization instances, the error can be flagged. Also, the modeler can in some cases leave it to the metamodel behind the scenes to work out the correct order of deletion so that the awkward DFD and scrall above can be simplified.
For example, the actual Scrall might look like this instead:
!* off atc // Superclass is automatically deleted
alternately:
!* super atc // Subclass is automatically deleted
Since the modeler has not specified otherwise, the resultant DFD in both examples above will correctly delete first the subclass and then the related superclass instances.
Often the modeler will want to coordinate class specific activities on each instance as part of the deletion which will require more explicit action language invoking methods, sending asynchronous delete signals and so forth.
Copyright © 2021-2023 Leon Starr