====== The Implementation relation ====== When types are created using either ''deftype'' or ''defchan'', they are defined as the implementation of a type. This is also possible for processes defined using ''defproc''. ==== Implementation ==== The implementation relation is used to specify the precise implementation of a data or channel type. The most straightforward mechanism to specify a channel is to say that it is the implementation of a built-in data type. In the example in the [[language:types2|previous section]], the channel ''e1of2'' is defined to be an implementation of a ''chan(bool)''. The implementation has additional port parameters, and it specifies the communication protocols for a send and receive action. The syntax that is used to say that we have an implementation is the ''<:'' symbol between the name of the type and the parent type that it is related to. When a type implements another one, the new type can be used in places the old type was used. While this is superficially similar to subtyping in programming languages, it is better viewed as an implementation relationship. So, if type ''tA'' implements type ''tB'', then ''tA'' has a more detailed description of the implementation compared to ''tB''. Type ''tB'' is the //parent type// for type ''tA''. As seen earlier, each data type or channel type can have a method body that specifies all the operations that can be performed using the type. These method bodies relate operations on the type to operations on the parent type (through ''self''). Different implementations of the same type are not equivalent or interchangeable. For example, one can imagine two different implementations of an ''int<2>'': two dual-rail codes, or a one-of-four code. Both are implementations, but they are not equivalent to each other. A more interesting option is to use the implementation relationship between two user-defined types. This option is used to create related versions of an existing type. If type ''foo'' implements ''bar'', then the basic rule is that one can use ''foo'' instances in all the same contexts where ''bar'' instances can be used. The meaning of defining one type to be an implementation of another is discussed in detail below. ==== Aspects of the implementation relation ==== When a user defined type implements another, there are several items to consider: (i) the template parameters (if any); (ii) the port parameters; and the body of the new type. Since the new type is supposed to implement the old one, ACT defines the template parameters and port parameters for the new type using the old type as a starting point. The port list of the new type consists of the original port list in the base type plus the additional ports specified in the type definition---in other words, the new type //extends// the original port list. As an illustration, consider: defproc type1 (bool a, b) { ... } defproc type2 <: type1 (bool c) { ... } In this case ''type2'' would have ports ''a'', ''b'', and ''c'', since it is an implementation of ''type1''. Template parameters are handled in a similar fashion, except there is a complication. In the example below, ''type2'' would have two template parameters ''N'' and ''M'', as might be expected from the port list example above. template defproc type1 (bool a, b) { ... } template defproc type2 <: type1 (bool c) { ... } However, we can also define ''type2'' in the following manner: template defproc type1 (bool a, b) { ... } template defproc type2 <: type1<4> (bool c) { ... } In this version, the template parameter from ''type1'' has been specified in the type definition for ''type2''. To permit this feature, template parameters for the new type are defined in the following manner: * Template parameters are categorized as //definable parameters// versus //pre-specified parameters//. Fresh template parameters are always definable. * Pre-specified parameters are not part of the parameter set that can be specified when a type is created. * The value of the pre-specified parameter is computed based on the type signature. * The list of parameters accessible in the body of the type and outside the type is the combination of the ones defined by the new type and the parent type. * The order of template parameters in the new type corresponds to the new parameters first (in the order specified), followed by any definable parameters left in the parent type. So in the example above, we could create an instance of ''type2'' as follows: type2<5> x; // x.N, x.M are both accessible! The parameter ''N'' for ''x'' will be ''4'', since that has been pre-specified in the type definition for ''type2''. If instead we had specified ''type2 <: type1'' as in the earlier example, then the instance type2<5,7> x; // x.M=5, x.N=7 would set ''M'' (the new template parameter) to ''5'', and ''N'' (the parent, still definable parameter) to ''7''. Finally, the following would be an error: template defproc type1 (bool a, b) { ... } template defproc type2 <: type1 (bool c) { ... } -[ERROR]-> Duplicate meta-parameter name in port list: `N' Conflict occurs due to parent type: type1 since the name of the template parameter is repeated. The body of the new type is the union of the original type definition and the new body. As an example to illustrate how this might be used, consider the following example: defproc buffer (e1of2? l, e1of2! r) { bool x; chp { *[ l?x;r!x ] } } ''buffer'' specifies the CHP description for a one-place FIFO. This can be implemented using a variety of circuits, and hence we could define a specific implementation as follows: defproc wchb <: buffer () { prs { ... } } The process ''wchb'' is a specific implementation of the buffer (without any extra ports) that contains a particular production-rule implementation of the same buffer. ==== Overrides ==== Consider the buffer example above. A better and more abstract specification for a buffer at the CHP level of abstraction would be: defproc buffer (chan?(bool) l, chan!(bool) r) { bool x; chp { *[ l?x;r!x ] } } The production rules for this buffer use the fact that the Boolean channel for ''l'' and ''r'' is in fact an ''e1of2'' channel. Because an ''e1of2'' channel implements a ''chan(bool)'', ACT provides a mechanism to say that an implementation of a process also replaces existing types with new implementations of the same type. This mechanism is called an //override//, because old types can be //overridden// with new ones that implement them. Variables in the port list as well as the body of a type can be overridden. For this to be sound, the new type must be an implementation of the original. The syntax for this is shown below: defproc wchb <: buffer() +{ e1of2 l, r; } // override block { prs { ... } } In this version, the original process implements a buffer with channels, and the ''wchb'' specifies that the channel is a ''e1of2''. The first abstract buffer can be used with different handshake protocols on channels, or with different circuits. The ''+{ ... }'' indicates the override block. The override block uses the instantiation syntax to specify override types. The only syntax permited is of the form within an override block is +{ type list-of-ids; ... } The identifiers cannot contain array specifiers, and the types cannot have any direction flags. Furthermore, the types must also be user-defined types. The identifier names must match names in the parent type (either in the port list or in the body of the type). If the original identifier was declared as an array, the entire array will be overridden. Direction flags from the parent type will be inherited by the overridden type. Implementation relations introduced should be handled with care. For example, suppose one has two definitions of a buffer: one using the CHP language, and the other using the PRS language (as above), and consider the variable ''x''. Now this internal variable which is in the CHP language may not be part of the PRS for the WCHB reshuffling. In other words, the ''wchb'' process will not have any internal variable that corresponds to ''x'' in the CHP language! Similarly, the ''wchb'' PRS language will contain additional variables that are not present in the CHP language. The relation between such variables is unspecified by the implementation relationship. An additional complication arises if the process ''wchb'' ends up introducing a state variable ''x'' in the PRS language that does correspond to the ''bool x'' in the CHP language. In this case, one should override ''x'' as well with the appropriate detailed representation of the Boolean variable. === Overriding conditional instances and templated types === An instance created in a type definition may be created only under certain circumstances. This can occur in templated types, where template parameters can affect the instances within the type. The override block has the simple syntax shown above. However, to account for conditional instances, the override block directives implicitly check if the instance exists before applying the override. (Note that ACT does not permit different types for the same instance name under different conditions.) When a type is being overridden by another, the new implementation may have additional template parameters. The override syntax above requires the complete list of template parameters to be specified. Instead, we also provide a single extension override syntax +{ type+ id; // single ID only ... } The ''+'' indicates that these are //extra// template parameter(s) that should be added to the existing ones that have already been specified for the instance ''id'' in the original type definition.