Complicated program functions can be built from simpler program functions via composition. Further, one can reuse program components throughout a program, or even in different programs. These approaches are made possible by the ability to reference one program function from another.
TML allows models to reference other models. The referenced model is an included model. This allows complex models to be built in terms of simpler components and allows libraries of common model components to be developed.
Note TML does not have an "include" or "import" keyword; when an included model is required, it is loaded by searching a specified model repository. This is typically declared through either the command-line options of tools (the -M option), or through the MODELPATH environment variable. See documentation on TML-related tools for more information.
An arc's destination can be either another state in the same model, or an included model. In the latter case, one gives the included model name as the destination for the arc. This connects the arc to the [Enter] state of the included model.
When the included model's [Exit] state is reached, flow must return to some state of the enclosing model. This is specified by giving the return state after the name of the included model.
As an example Consider the model from Figure 1 of the previous section and assume that whenever the user saves the work, one of three formats must be chosen. Since the "Save" event appears in the model twice, this is a good candidate for a new model.
The new model, SaveFormat, is given below.
model SaveFormat [Enter] "Text Format" [Exit] "Graphic Format" [Exit] "Normal Format" [Exit] end
Figure 1: SaveFormat model
The destination of the "Save" arcs can be changed to the SaveFormat model, resulting in the following changes.
[Work Saved] "Save" SaveFormat [Work Saved] "Close" [No Work] "Exit" [Exit] [Work Unsaved] "Save" SaveFormat [Work Saved] "Close" [Confirm Close] "Exit" [Confirm Exit]
Figure 2: Using the SaveFormat model
The above says, informally: On the "Save" event go to the [Enter] state of the SaveFormat model. Upon returning from the SaveFormat model, go to the [Work Saved] state.
Consider the model from Figure 1 of the previous section. There are two "confirm" cases, from which one must choose either "OK" or "Cancel" before continuing. This repeated structure is a good candidate for a new model.
The new model, Confirm, is given below.
model Confirm [Enter] "OK" [Exit] "Cancel" [Exit] end
Figure 3: Confirm model
Here it is necessary to know what arc is taken in the model to know where to go next in the enclosing model. This is done by using an exit selector.
Continuing the analogy to programming, the idea is to return a value from the model. In TML, the value returned is the last stimulus encountered in the model, the stimulus to [Exit]. In the model there are two possibilities: "OK" and "Cancel".
An exit selector is just a list of arc declarations, where the stimuli are the exit stimuli of the included model. The selector is given right after the included model name, and is introduced with the keyword select. The selector is terminated with the keyword end. In many ways, the selector is like the case statement of a programming language.
Here's how the Confirm model would be included.
model BigModelByStacy [Enter] "Begin" [No Work] [No Work] "New" [Work Unsaved] "Open" [Work Saved] "Exit" [Exit] [Work Saved] "Save" [Work Saved] "Close" [No Work] "Exit" [Exit] [Work Unsaved] "Save" [Work Saved] "Close" Confirm select "OK" [No Work] "Cancel" [Work Unsaved] end "Exit" Confirm select "OK" [No Work] "Cancel" [Work Unsaved] end end
Figure 4: Using the Confirm submodel
If an included model has an arc to exit, but the corresponding stimulus is not mentioned in the exit selector, then an error will occur. However, there may be cases in which some exit stimuli have special destinations, but all others have the same destination. This is similar to the default selection of a case statement, and TML provides an analogous construct; the default selection.
Consider a case in which a submodel performs a complex task, which may result in either normal operation or one of four distinct error cases. The error status is passed back as the final stimulus, and trapped by the exit selector. In all error cases the destination is an error handler state.
The following two exit selectors are equivalent.
// First try; catch each exit stimulus separately. [Normal Operation] "Do something complicated" ComplexSubmodel select "Normal exit" [Continue Normal Operation] "Error exit type 1" [Error Handler] "Error exit type 2" [Error Handler] "Error exit type 3" [Error Handler] "Error exit type 4" [Error Handler] end // Second try; use a default selection. [Normal Operation] "Do something complicated" ComplexSubmodel select "Normal exit" [Continue Normal Operation] default [Error Handler] end
Figure 5: Using a default selection
In fact, all references to included models use an exit selector, even if it isn't obvious. The following two model inclusions are equivalent (during compilation, the first is expanded to the second by the TML compiler).
// First case: give the return state explicitly. [Work Saved] "Save" SaveFormat [Work Saved] // Second case: given the return state as default selection. [Work Saved] "Save" SaveFormat select default [Work Saved] end
Figure 6: Equivalence of default selections
Using exit selectors allows one to obtain information from included models. It is also possible to send information into an included model, again in the form of stimuli. All information is passed to and from included models in the form of stimuli.
The input to an included model is an ordered sequence of stimuli called an input trajectory. This trajectory must point to a unique state in the included model, starting from [Enter]. The use of the included model then begins at this state.
Each stimulus in the input trajectory is appended to the included model name with a period, in order. Thus if one wished to pass the stimulus sequence "A" "B" "C" to model BigModelByStacy, one would write BigModelByStacy."A"."B"."C".
To see why this is useful, consider the model from Figure 4 above and how one might try to reuse it. Several examples are given below.
// Here are some input trajectory examples, and also // some examples of different formatting styles. // Use the model, starting with unsaved work. [A state] "Import document" BigModelByStacy."Begin"."New" [Return State] // Use the model, but avoid the "Begin" stimulus. [A state] "Open previous document" BigModelByStacy."Begin" select "Exit" [Did Not Save Work] "OK" [Saved Work] end // Trajectories can go into included models. This // trajectory goes to the [Enter] state of the Confirm // model. [A state] "Go to confirm" BigModelByStacy."Begin"."New"."Exit" [Return State] // This trajectory is not legal. [A state] "Bad idea" BigModelByStacy."Open"."Cancel" [Return State]
Figure 7: Reusing the model with an input trajectory