|
1 | 1 | :prev_state: False |
2 | 2 |
|
3 | | -.. include:: ../../global.txt |
4 | | - |
5 | 3 | Introduction |
6 | 4 | ============ |
| 5 | + |
| 6 | +.. include:: ../../global.txt |
| 7 | + |
| 8 | +This course describes how to implement selected programming idioms in the Ada |
| 9 | +language. |
| 10 | + |
| 11 | +What is an idiom? Some would say that an idiom is a workaround for an |
| 12 | +expressive deficiency in a programming language. That is not what we mean. |
| 13 | + |
| 14 | +For example, we have in mind solution techniques and designs that are widely |
| 15 | +recognized as most appropriate for certain programming situations, across |
| 16 | +multiple programming languages. For instance, *reference counting* is a |
| 17 | +well-known approach to tracking and managing the storage for objects and is |
| 18 | +conceptually independent of the programming language. The implementation varies |
| 19 | +with the language, of course, but the concept is the same. Other examples might |
| 20 | +include |
| 21 | +:wikipedia:`Resource Acquisition Is Allocation (RAII) <Resource_acquisition_is_initialization>`, |
| 22 | +as well as :wikipedia:`type punning <Type_punning>` and inheritance idioms such |
| 23 | +as |
| 24 | +:wikipedia:`interface inheritance <Subtyping>` and |
| 25 | +:wikipedia:`implementation inheritance <Implementation_inheritance>`. |
| 26 | + |
| 27 | +We also have in mind the best way in Ada to express fundamental, commonly known |
| 28 | +concepts such as Abstract Data Types, something the Ada language directly |
| 29 | +supports but using *building blocks* instead of a single construct. These |
| 30 | +concepts are included because they are fundamental to good design and because |
| 31 | +the building blocks require some explanation. |
| 32 | + |
| 33 | +In contrast, we also include idioms for programming situations specific to the |
| 34 | +Ada language. These are not deficiency workarounds, but rather, |
| 35 | +*best practices* in situations that arise given the capabilities and semantics |
| 36 | +in the language. For example, Ada directly supports tasks (threads). There is |
| 37 | +an idiom for how best to associate a task with an enclosing object so that the |
| 38 | +task has visibility to the object's other components. |
| 39 | + |
| 40 | +Some would equate idioms with design patterns. That's not unreasonable but is |
| 41 | +more narrow than we intend. This is not a document dedicated to describing how |
| 42 | +to use Ada to implement various design patterns. But note that we may refer to |
| 43 | +a design pattern to illustrate an idiom's purpose and implementation. For |
| 44 | +example, in the idiom for controlling object creation and initialization, the |
| 45 | +implementation approach happens to be the same as for expressing a |
| 46 | +Singleton [1]_. Some idiom entries might even be dedicated to implementing |
| 47 | +design patterns, but the intent is beyond just that. |
| 48 | + |
| 49 | +Perhaps instead of *idiom* we should have used the term *cookbook,* but |
| 50 | +although descriptive for some entries, that term didn't completely convey the |
| 51 | +intent either. |
| 52 | + |
| 53 | + |
| 54 | +Assumptions |
| 55 | +----------- |
| 56 | + |
| 57 | +We assume the reader knows Ada to some degree, including some advanced topics. |
| 58 | +For those lacking significant familiarity, we hope these solutions will at |
| 59 | +least give a sense for how to apply the language. We direct such readers to the |
| 60 | +:ref:`online Learn courses dedicated to the Ada language itself <Advanced_Ada_Course_Index>`. |
| 61 | + |
| 62 | + |
| 63 | +Definitions |
| 64 | +----------- |
| 65 | + |
| 66 | +For the sake of avoiding duplication in the idiom entries, the following terms |
| 67 | +are defined here. Note that the Ada Language Manual includes a glossary in |
| 68 | +:arm22:`Section 1.3 <1-3>` (located in Annex N prior to Ada 2022). Some of the |
| 69 | +following expand on the definitions found there. |
| 70 | + |
| 71 | +Suppliers and Clients |
| 72 | +~~~~~~~~~~~~~~~~~~~~~ |
| 73 | + |
| 74 | +*Suppliers* are software units that provide programming entities to other |
| 75 | +software units, the users. These users are the *clients* of the supplied units. |
| 76 | +The concept is simple and intuitive, but by defining these terms we can convey |
| 77 | +these roles quickly in the idioms' discussions. |
| 78 | + |
| 79 | +For example, a unit that defines a type and associated operations would be a |
| 80 | +supplier. Client units could use that type to declare objects, and/or apply the |
| 81 | +operations to such objects. The language-defined package :ada:`Ada.Text_IO` is |
| 82 | +an example of a supplier. Similarly, the unit that defines a library, such as a |
| 83 | +math library, is a supplier. Callers to the math library routines are the |
| 84 | +clients. The generic package |
| 85 | +:ada:`Ada.Numerics.Generic_Complex_Elementary_Functions`, once instantiated, |
| 86 | +would be an example supplier. (Arguably, the generic package itself would be a |
| 87 | +supplier to the client that instantiates it, but instantiation is the only |
| 88 | +possibility in that narrow case. Only the routines in the instances can be |
| 89 | +called.) |
| 90 | + |
| 91 | +Betrand Meyer's book on OOP [2]_ limits these terms specifically to the case of |
| 92 | +a type used in an object declaration. Our definitions cover that case but |
| 93 | +others as well. |
| 94 | + |
| 95 | +Units can be both suppliers and clients, because a given supplier's facility, |
| 96 | +i.e., the interface and/or implementation, may be built upon the facilities |
| 97 | +defined by other suppliers. |
| 98 | + |
| 99 | +Compile-time Visibility |
| 100 | +~~~~~~~~~~~~~~~~~~~~~~~ |
| 101 | + |
| 102 | +In the definitions of supplier and client above, we gave an example in which a |
| 103 | +supplier's type was used by clients to declare objects of the type. For the |
| 104 | +client to legally do so |mdash| that is, for the compiler to accept this usage |
| 105 | +and process the code |mdash| the use of the supplier's type has to satisfy the |
| 106 | +scope and visibility rules of the programming language. |
| 107 | + |
| 108 | +Good implementations harness these visibility rules to adhere to the software |
| 109 | +engineering principles of information hiding and abstraction, both of which |
| 110 | +require that nothing of the implementation be made visible to clients unless |
| 111 | +necessary. Compiler enforcement ensures rigorous adherence to those principles. |
| 112 | + |
| 113 | +Therefore, modern languages provide some way to express this control. For |
| 114 | +example, in Ada, a package can have both a *public* part and a *private* part. |
| 115 | +Clients have no compile-time visibility to the private part, nor to the package |
| 116 | +body, as both parts contain implementation artifacts. In class-oriented |
| 117 | +languages, parts of the class can be marked as *public,* *private*, and |
| 118 | +*protected* (the details depend on the specific language). |
| 119 | + |
| 120 | +The idioms explored in |
| 121 | +:ref:`Fundamental Packages <Ada_Idioms_Essential_Design_Idioms_For_Packages>` |
| 122 | +are largely variations on expressing this control in Ada. More details on the |
| 123 | +topic are provided in those idioms. |
| 124 | + |
| 125 | +Views |
| 126 | +~~~~~ |
| 127 | + |
| 128 | +In Ada, a *view* of an entity defines what the developer can legally do with |
| 129 | +that entity. For example, the declaration of an object defines a view of that |
| 130 | +object. The operations allowed by that view are determined by the type used to |
| 131 | +declare the object: a signed integer type would allow signed integer numeric |
| 132 | +operations, but not, say, bit-level operations, nor array indexing, and so on. |
| 133 | +Furthermore, the view includes whether the object is a constant. |
| 134 | + |
| 135 | +An entity can have more than one view, depending on where in the text of the |
| 136 | +source code a view of that entity is considered. For example, let's say that |
| 137 | +the integer object introduced above is in fact a variable. Within the scope of |
| 138 | +that variable, we can refer to it by that name and update the value using |
| 139 | +assignment statements. However, if we pass that variable as the argument to a |
| 140 | +procedure call, within that subprogram (for that call) the view specifies a |
| 141 | +different name for the argument, i.e., the formal parameter name. Moreover, if |
| 142 | +that formal parameter is a mode-in parameter, within that procedure body the |
| 143 | +view of the actual parameter is as if it is a constant rather than a variable. |
| 144 | +No assignments via the formal parameter name are allowed because the view at |
| 145 | +that point in the text |mdash| within that procedure body |mdash| doesn't allow |
| 146 | +them, unlike the view outside the body. |
| 147 | + |
| 148 | +As another example, consider a tagged type named :ada:`Parent`, and a type |
| 149 | +derived from it via type extension, named :ada:`Child`. It is common for a |
| 150 | +derived type to have either additional components, or additional operations, or |
| 151 | +both. For a given object of the :ada:`Child` type, the view via type |
| 152 | +:ada:`Child` allows the developer to refer to the extended components and/or |
| 153 | +operations. But we can convert the :ada:`Child` object to a value of the |
| 154 | +:ada:`Parent` type using what is known as a *view conversion*. With that |
| 155 | +:ada:`Parent` view of the :ada:`Child` object, we can only refer to those |
| 156 | +components and operations defined for the :ada:`Parent` type. The compiler |
| 157 | +enforces this temporary view. |
| 158 | + |
| 159 | +For further details about view conversions, please refer to that |
| 160 | +:ref:`specific section of the Advanced Ada course <Adv_Ada_View_Conversion>`. |
| 161 | + |
| 162 | +Views are a fundamental concept in Ada. Understanding them will greatly |
| 163 | +facilitate understanding the rules of the language in general. |
| 164 | + |
| 165 | + |
| 166 | +Partial and Full Views |
| 167 | +~~~~~~~~~~~~~~~~~~~~~~ |
| 168 | + |
| 169 | +Like objects, types also can have more than one view, again determined by the |
| 170 | +place in the program text that a view is considered. These views can be used to |
| 171 | +apply information hiding and abstraction. |
| 172 | + |
| 173 | +The declaration of a private type defines a *partial view* of a type that |
| 174 | +reveals only some of its properties: the type name, primarily, but in |
| 175 | +particular not the type's representation. For example: |
| 176 | + |
| 177 | +.. code-block:: ada |
| 178 | +
|
| 179 | + type Rotary_Encoder is private; |
| 180 | +
|
| 181 | +Private type declarations must occur in the *public part* of a package |
| 182 | +declaration. Anything declared there is compile-time visible to clients of the |
| 183 | +package so the type's name is visible, and potentially some other properties as |
| 184 | +well. Clients can therefore declare objects of the type name, for example, but |
| 185 | +must adhere to their partial view's affect on what is compile-time visible. |
| 186 | + |
| 187 | +The private type's full representation must be specified within the |
| 188 | +*private part* of that same package declaration. For example: |
| 189 | + |
| 190 | +.. code-block:: ada |
| 191 | +
|
| 192 | + type Rotary_Encoder is record ... end record; |
| 193 | +
|
| 194 | +Therefore, within that package private part and within the package body the |
| 195 | +*full view* is available because full representation information is |
| 196 | +compile-time visible in those regions. (Parts of child units have the full view |
| 197 | +as well.) This view is necessary in those two regions of the package because |
| 198 | +the representation details are required in order to implement the corresponding |
| 199 | +operations, among other possibilities. |
| 200 | + |
| 201 | +Because the clients only have the partial view they do not have compile-time |
| 202 | +visibility to the type's internal representation. Consequently, the compiler |
| 203 | +will not allow representation-specific references or operations in client code. |
| 204 | +The resulting benefit is that clients are independent of the type's |
| 205 | +representation and, therefore, it can be changed without requiring coding |
| 206 | +changes in the clients. Clients need only be recompiled in that case. |
| 207 | + |
| 208 | +This application of information hiding has real-world cost benefits because |
| 209 | +changing client code can be prohibitively expensive. That's one reason why the |
| 210 | +maintenance phase of a project is by far the most expensive phase. Another |
| 211 | +reason is that *maintenance* is often a euphemism for new development. Either |
| 212 | +way, change is involved. |
| 213 | + |
| 214 | +As a result, when defining types, developers should use private types by |
| 215 | +default, only avoiding them when they are not appropriate. Not using them |
| 216 | +should be an explicit design choice, a line item in code reviews. Not defining |
| 217 | +a major abstraction as a private type should be suspect, just as using a |
| 218 | +:c:`struct` rather than a :c:`class` in C++ should be suspect in that case. (In |
| 219 | +C++ anything a :c:`struct` contains is compile-time visible to clients by |
| 220 | +default.) |
| 221 | + |
| 222 | +For further details about type views, please refer to that |
| 223 | +:ref:`specific section of the Advanced Ada course <Adv_Ada_Type_View>`. |
| 224 | + |
| 225 | + |
| 226 | +Bibliography |
| 227 | +------------ |
| 228 | + |
| 229 | +.. [1] Gamma, E., R. Helm, et al. (1995), pp. 127. Design Patterns: Elements of |
| 230 | + Reusable Object-Oriented Software. Reading, MA, Addison-Wesley |
| 231 | + Publishing Company. |
| 232 | +
|
| 233 | +.. [2] Meyer, B. (1997), pp. 182. Object-Oriented Software Construction, |
| 234 | + Prentice-Hall. |
0 commit comments