10 Genericity omthe merging of module and types concepts,we have been able to develop a powerful notion of class,which serves as the basis of the object-oriented method and can already,as it stands,enable us to do much.But to achieve our goals of extendibility, reusability and reliability we must make the class construct more flexible,an effort that will proceed in two directions.One,vertical in the figure below,represents abstraction and specialization;it will give rise to the study of inheritance in subsequent chapters.The present chapter studies the other dimension,horizontal in the figure:type parameterization, also known as genericity. Dimensions of Abstraction generalization SET OF BOOKS Type parameterization Type parameterization LIST OF LIST OF LIST OF PEOPLE BOOKS JOURNALS LINKED LIST OF BOOKS Specialization 10.1 HORIZONTAL AND VERTICAL TYPE GENERALIZATION With the mechanisms studied so far we have all that we need to write the class at the center of the figure,LIST_OF_BOOKS,of which an instance represents a list of book objects. We know what kinds of feature it would have:put to add an element,remove to delete an
10 Genericity From the merging of module and types concepts, we have been able to develop a powerful notion of class, which serves as the basis of the object-oriented method and can already, as it stands, enable us to do much. But to achieve our goals of extendibility, reusability and reliability we must make the class construct more flexible, an effort that will proceed in two directions. One, vertical in the figure below, represents abstraction and specialization; it will give rise to the study of inheritance in subsequent chapters. The present chapter studies the other dimension, horizontal in the figure: type parameterization, also known as genericity. 10.1 HORIZONTAL AND VERTICAL TYPE GENERALIZATION With the mechanisms studied so far we have all that we need to write the class at the center of the figure, LIST_OF_BOOKS, of which an instance represents a list of book objects. We know what kinds of feature it would have: put to add an element, remove to delete an Dimensions of generalization LIST_OF_ PEOPLE LIST_OF_ BOOKS LIST_OF_ JOURNALS SET_OF_ BOOKS LINKED_LIST_ OF_BOOKS Abstraction Specialization Type parameterization Type parameterization
318 GENERICITY $10.2 element,count to find out how many elements are present and so on.But it is easy to see two ways of generalizing the notion of LIST OF BOOKS: Lists are a special case of"container"structure,of which other examples (among many)include trees,stacks and arrays.A more abstract variant might be described by a class SET OF BOOKS.A more specialized variant,covering a particular choice of list representation,might be described by a class LINKED LIST OF BOOK.S.This is the vertical dimension of our figure-the dimension of inheritance. Lists of books are a special case of lists of objects of any particular kind,of which other examples (among many)include lists of journals,lists of people,lists of integers.This is the horizontal dimension of our figure -the dimension of genericity,our topic for the rest of this chapter.By giving classes parameters representing arbitrary types,we will avoid the need to write many quasi-identical classes-such as LIST OF BOOKS and LIST OF PEOPLE-without sacrificing the safety afforded by static typing. The relation between these two mechanisms is an elusive question for students of Appendix B. object-oriented concepts.Should inheritance and genericity be viewed as comrades or competitors in the rush towards more flexible software?That question is the subject of an appendix.In the present chapter we concentrate on genericity;this will also enable us to take a closer look at one of the most common examples of generic structure:arrays. 10.2 THE NEED FOR TYPE PARAMETERIZATION Genericity is not really a new concept in this discussion,although we have not yet seen it See"Genericin". applied to classes.We encountered the idea a first time when reviewing traditional page 96,and again approaches to reusability;and when we studied the mathematical model-abstract data "Genericity'”,page 131. types-we saw the need to define an ADT as parameterized by types. Generic abstract data types Our working ADT example,STACK,was declared as STACK [G],meaning that any actual use requires you to specify an"actual generic parameter"representing the type of the objects stored in a particular stack.The name G as used in the ADT's specification stands for any possible type that these stack elements may have;it is called the formal generic parameter of the class.With this approach you can use a single specification for all possible stacks;the alternative,hard to accept,would be to have a class INTEGER STACK,a class REAL STACK and so on. Any ADT describing "container"structures-data structures such as sets,lists, trees,matrices,arrays and many others that serve to keep objects of various possible types -will be similarly generic. The same concerns,applied to the container classes of our software systems rather than to the container ADTs of our mathematical models,will yield a similar solution
318 GENERICITY §10.2 element, count to find out how many elements are present and so on. But it is easy to see two ways of generalizing the notion of LIST_OF_BOOKS: • Lists are a special case of “container” structure, of which other examples (among many) include trees, stacks and arrays. A more abstract variant might be described by a class SET_OF_BOOKS. A more specialized variant, covering a particular choice of list representation, might be described by a class LINKED_LIST_OF_ BOOKS. This is the vertical dimension of our figure — the dimension of inheritance. • Lists of books are a special case of lists of objects of any particular kind, of which other examples (among many) include lists of journals, lists of people, lists of integers. This is the horizontal dimension of our figure — the dimension of genericity, our topic for the rest of this chapter. By giving classes parameters representing arbitrary types, we will avoid the need to write many quasi-identical classes — such as LIST_OF_BOOKS and LIST_OF_PEOPLE — without sacrificing the safety afforded by static typing. The relation between these two mechanisms is an elusive question for students of object-oriented concepts. Should inheritance and genericity be viewed as comrades or competitors in the rush towards more flexible software? That question is the subject of an appendix. In the present chapter we concentrate on genericity; this will also enable us to take a closer look at one of the most common examples of generic structure: arrays. 10.2 THE NEED FOR TYPE PARAMETERIZATION Genericity is not really a new concept in this discussion, although we have not yet seen it applied to classes. We encountered the idea a first time when reviewing traditional approaches to reusability; and when we studied the mathematical model — abstract data types — we saw the need to define an ADT as parameterized by types. Generic abstract data types Our working ADT example, STACK, was declared as STACK [G], meaning that any actual use requires you to specify an “actual generic parameter” representing the type of the objects stored in a particular stack. The name G as used in the ADT’s specification stands for any possible type that these stack elements may have; it is called the formal generic parameter of the class. With this approach you can use a single specification for all possible stacks; the alternative, hard to accept, would be to have a class INTEGER_ STACK, a class REAL_STACK and so on. Any ADT describing “container” structures — data structures such as sets, lists, trees, matrices, arrays and many others that serve to keep objects of various possible types — will be similarly generic. The same concerns, applied to the container classes of our software systems rather than to the container ADTs of our mathematical models, will yield a similar solution. Appendix B. See “Genericity”, page 96, and again “Genericity”, page 131
$10.2 THE NEED FOR TYPE PARAMETERIZATION 319 The issue Let us keep the stack example,no longer as a mathematical ADT but as a software class. We know how to write a class INTEGER STACK describing the notion of stack of integers.Features will include cout(number ofelements),put(push a new element),item (top element),remove (pop the top element),empry (is this stack empty?). Type /NTEGER will be used frequently in this class.For example it is the type of the argument of put and of the result of item: put (element:INTEGER)is --Push element on top. do...end item:INTEGER is --Item at top do...end These appearances of type INTEGER follow from the rule of explicit declaration that we have used in developing the notation:any time you introduce an entity,denoting possible run-time objects,you must write an explicit type declaration for it,such as element: INTEGER.Here this means that you must specify a type for the query item,for the argument element of procedure put,and for other entities denoting possible stack elements. But as a consequence you must write a different class for every sort of stack: INTEGER STACK,REAL STACK,POINT STACK,BOOK STACK...All such stack classes will be identical except for the type declarations of item,element and a few other entities:since the basic operations on a stack are the same regardless of the type of stack elements,nothing in the bodies of the various routines depends on the choice of INTEGER,REAL,POINT or BOOK as the type of stack element.For anyone concerned with reusability,this is not attractive. The issue,then,is the contradiction that container classes seem to cause between two of the fundamental quality goals introduced at the beginning of this book: Reliability:retaining the benefits of type safety through explicit type declarations. Reusability:being able to write a single software element covering variants of a given notion. The role of typing Chapter 17. Why insist on explicit type declarations(the first of the two requirements)?This is part of the general question of typing,to which an entire chapter is devoted later in this book.It is not too early to note the two basic reasons why an O-O notation should be statically typed: The readability reason:explicit declarations tell the reader,loud and clear,about the intended use of every element.This is precious to whoever-the original author,or someone else-needs to understand the element,for example to debug or extend it
§10.2 THE NEED FOR TYPE PARAMETERIZATION 319 The issue Let us keep the stack example, no longer as a mathematical ADT but as a software class. We know how to write a class INTEGER_STACK describing the notion of stack of integers. Features will include count (number of elements), put (push a new element), item (top element), remove (pop the top element), empty (is this stack empty?). Type INTEGER will be used frequently in this class. For example it is the type of the argument of put and of the result of item: put (element: INTEGER) is -- Push element on top. do … end item: INTEGER is -- Item at top do … end These appearances of type INTEGER follow from the rule of explicit declaration that we have used in developing the notation: any time you introduce an entity, denoting possible run-time objects, you must write an explicit type declaration for it, such as element: INTEGER. Here this means that you must specify a type for the query item, for the argument element of procedure put, and for other entities denoting possible stack elements. But as a consequence you must write a different class for every sort of stack: INTEGER_STACK, REAL_STACK, POINT_STACK, BOOK_STACK… All such stack classes will be identical except for the type declarations of item, element and a few other entities: since the basic operations on a stack are the same regardless of the type of stack elements, nothing in the bodies of the various routines depends on the choice of INTEGER, REAL, POINT or BOOK as the type of stack element. For anyone concerned with reusability, this is not attractive. The issue, then, is the contradiction that container classes seem to cause between two of the fundamental quality goals introduced at the beginning of this book: • Reliability: retaining the benefits of type safety through explicit type declarations. • Reusability: being able to write a single software element covering variants of a given notion. The role of typing Why insist on explicit type declarations (the first of the two requirements)? This is part of the general question of typing, to which an entire chapter is devoted later in this book. It is not too early to note the two basic reasons why an O-O notation should be statically typed: • The readability reason: explicit declarations tell the reader, loud and clear, about the intended use of every element. This is precious to whoever — the original author, or someone else — needs to understand the element, for example to debug or extend it. Chapter 17
320 GENERICITY $10.3 The reliability reason:thanks to explicit type declarations,a compiler will be able to detect erroneous operations before they have had a chance to strike.In the fundamental operations of object-oriented computation,feature calls of the general formx.(a....),wherex is of some type TX,the potential for mischief is manyfold: the class corresponding to TX might not have a feature called /the feature might exist but be secret;the number of arguments might not coincide with what has been declared for fin the class;the type for a or another argument might not be compatible with what /expects.In all such cases,letting the software text go through unopposed -as in a language without static typechecking-would usually mean nasty consequences at run time,such as the program crashing with a diagnostic of the form "Message not understood"(the typical outcome in Smalltalk,a non-statically-typed O-O language).With explicit typing,the compiler will not let the erroneous construct through. The key to software reliability,as was pointed out in the discussion of that notion,is prevention more than cure.Many studies have found that the cost of correcting an error grows astronomically when the time of detection is delayed.Static typing,which enables the early detection of type errors,is a fundamental tool in the quest for reliability Without these considerations we would not need explicit declarations,and so we would not need genericity.As a consequence the rest of this chapter only applies to statically typed languages,that is to say languages which require all entities to be declared and enforce rules enabling compilers to detect type inconsistencies prior to execution.In a non-statically-typed language such as Smalltalk,there is no role for genericity;this removes a language construct,but also removes any protection against schemes such as my_stack.put (my circle) my account:=my stack.item my_account.withdraw (5000) where an element is retrieved from the top of the stack and treated as if it were a bank account even though it is in reality (because of the first instruction)a circle,so that the software ends up trying to withdraw five thousand dollars from a circle on the screen. Static typing protects us against such mishaps;combining it with the reusability requirement implies that we develop a mechanism for genericity. 10.3 GENERIC CLASSES Reconciling static typing with the requirement of reusability for classes describing container structures means,as illustrated by the stack example,that we want both to: Declare a type for every entity appearing in the text of a stack class,including entities representing stack elements. Write the class so that it does not give out any clue about the elements'type,and hence that it can be used to build stacks of arbitrary elements
320 GENERICITY §10.3 • The reliability reason: thanks to explicit type declarations, a compiler will be able to detect erroneous operations before they have had a chance to strike. In the fundamental operations of object-oriented computation, feature calls of the general form x ● f (a, …), where x is of some type TX, the potential for mischief is manyfold: the class corresponding to TX might not have a feature called f; the feature might exist but be secret; the number of arguments might not coincide with what has been declared for f in the class; the type for a or another argument might not be compatible with what f expects. In all such cases, letting the software text go through unopposed — as in a language without static typechecking — would usually mean nasty consequences at run time, such as the program crashing with a diagnostic of the form “Message not understood” (the typical outcome in Smalltalk, a non-statically-typed O-O language). With explicit typing, the compiler will not let the erroneous construct through. The key to software reliability, as was pointed out in the discussion of that notion, is prevention more than cure. Many studies have found that the cost of correcting an error grows astronomically when the time of detection is delayed. Static typing, which enables the early detection of type errors, is a fundamental tool in the quest for reliability. Without these considerations we would not need explicit declarations, and so we would not need genericity. As a consequence the rest of this chapter only applies to statically typed languages, that is to say languages which require all entities to be declared and enforce rules enabling compilers to detect type inconsistencies prior to execution. In a non-statically-typed language such as Smalltalk, there is no role for genericity; this removes a language construct, but also removes any protection against schemes such as my_stack ● put (my_circle) my_account := my_stack ● item my_account ● withdraw (5000) where an element is retrieved from the top of the stack and treated as if it were a bank account even though it is in reality (because of the first instruction) a circle, so that the software ends up trying to withdraw five thousand dollars from a circle on the screen. Static typing protects us against such mishaps; combining it with the reusability requirement implies that we develop a mechanism for genericity. 10.3 GENERIC CLASSES Reconciling static typing with the requirement of reusability for classes describing container structures means, as illustrated by the stack example, that we want both to: • Declare a type for every entity appearing in the text of a stack class, including entities representing stack elements. • Write the class so that it does not give out any clue about the elements’ type, and hence that it can be used to build stacks of arbitrary elements
$10.3 GENERIC CLASSES 321 At first sight these requirements seem irreconcilable but they are not.The first one commands us to declare a type;it does not assume that the declaration is exact!As soon as we have provided a type name,we will have pacified the type checking mechanism. ("Name your fear,and it will go away".)Hence the idea of genericity:to obtain a type- parameterized class,equip it with the name of a fictitious type,called the formal generic parameter. Declaring a generic class By convention the generic parameter will use the name G for Generic;this is a style recommendation,not a formal rule.If we need more generic parameters they will be called H.and so on. The syntax will include the formal generic parameters in square brackets after the class name,as with generic ADTs in a previous chapter.Here is an example: indexing description:"Stacks of elements of an arbitrary type G" class STACK [G]feature count:INTEGER --Number of elements in stack empty:BOOLEAN is --Are there no items? do...end full:BOOLEAN is --Is representation full? do...end item:G is --Top element do...end put (x:G)is --Add x on top. do...end remove is --Remove top element. do...end end--class STACK In the class,you may use a formal generic parameter such as G in declarations:not only for function results (as in item)and formal arguments ofroutines (as in put),but also for attributes and local entities
§10.3 GENERIC CLASSES 321 At first sight these requirements seem irreconcilable but they are not. The first one commands us to declare a type; it does not assume that the declaration is exact! As soon as we have provided a type name, we will have pacified the type checking mechanism. (“Name your fear, and it will go away”.) Hence the idea of genericity: to obtain a typeparameterized class, equip it with the name of a fictitious type, called the formal generic parameter. Declaring a generic class By convention the generic parameter will use the name G for Generic; this is a style recommendation, not a formal rule. If we need more generic parameters they will be called H, I and so on. The syntax will include the formal generic parameters in square brackets after the class name, as with generic ADTs in a previous chapter. Here is an example: indexing description: "Stacks of elements of an arbitrary type G" class STACK [G] feature count: INTEGER -- Number of elements in stack empty: BOOLEAN is --Are there no items? do … end full: BOOLEAN is -- Is representation full? do … end item: G is -- Top element do … end put (x: G) is -- Add x on top. do … end remove is -- Remove top element. do … end end -- class STACK In the class, you may use a formal generic parameter such as G in declarations: not only for function results (as in item) and formal arguments of routines (as in put), but also for attributes and local entities
322 GENERICITY $10.3 Using a generic class A client may use a generic class to declare entities of its own,such as an entity representing a stack.In such a case,the declaration must provide types,called actual generic parameters-as many as the class has formal generic parameters,here just one: sp:STACK [POINT] Providing an actual generic parameter to a generic class so as to produce a type,as here,is called a generic derivation,and the resulting type,such as STACK [POINT],is said to be generically derived. A generic derivation both produces and requires a type: The result of the derivation,STACK [POINT]in this example,is a type To produce this result,you need an existing type to serve as actual generic parameter, PO/NT in the example. The actual generic parameter is an arbitrary type.Nothing prevents us,in particular, from choosing a type that is itself generically derived;assuming another generic class LIST [G],we can define a stack of lists of points: slp:STACK [LIST [POINT]] or even,using STACK [POINT]itself as the actual generic parameter,a stack of stacks of points: ssp:STACK [STACK [POINT]] There is no limit-other than suggested by the usual guideline that software texts should remain simple-to the depth of such nesting. Terminology To discuss genericity,we need to be precise about the terms that we use: To produce a type such as STACK [POINT]by providing a type,here POINT,as actual generic parameter for a generic class,here STACK,is to perform a generic derivation.You may encounter the term"generic instantiation"for that process,but it is confusing because "instantiation"normally denotes a run-time event,the production of an object-an instance-from its mold (a class).Generic derivation is a static mechanism,affecting the text of the software,not its execution.So it is better to use completely different terms. This book uses the term "parameter"exclusively to denote the types that parameterize generic classes,never to denote the values that a routine call may pass to that routine,called arguments.In traditional software parlance "parameter"and "argument"are synonymous.Although the decision of which term to use for routines and which for generic classes is a matter of convention,it is desirable to stick to a consistent rule to avoid any confusion
322 GENERICITY §10.3 Using a generic class A client may use a generic class to declare entities of its own, such as an entity representing a stack. In such a case, the declaration must provide types, called actual generic parameters — as many as the class has formal generic parameters, here just one: sp: STACK [POINT] Providing an actual generic parameter to a generic class so as to produce a type, as here, is called a generic derivation, and the resulting type, such as STACK [POINT], is said to be generically derived. A generic derivation both produces and requires a type: • The result of the derivation, STACK [POINT] in this example, is a type. • To produce this result, you need an existing type to serve as actual generic parameter, POINT in the example. The actual generic parameter is an arbitrary type. Nothing prevents us, in particular, from choosing a type that is itself generically derived; assuming another generic class LIST [G], we can define a stack of lists of points: slp: STACK [LIST [POINT]] or even, using STACK [POINT ] itself as the actual generic parameter, a stack of stacks of points: ssp: STACK [STACK [POINT ]] There is no limit — other than suggested by the usual guideline that software texts should remain simple — to the depth of such nesting. Terminology To discuss genericity, we need to be precise about the terms that we use: • To produce a type such as STACK [POINT ] by providing a type, here POINT, as actual generic parameter for a generic class, here STACK, is to perform a generic derivation. You may encounter the term “generic instantiation” for that process, but it is confusing because “instantiation” normally denotes a run-time event, the production of an object — an instance — from its mold (a class). Generic derivation is a static mechanism, affecting the text of the software, not its execution. So it is better to use completely different terms. • This book uses the term “parameter” exclusively to denote the types that parameterize generic classes, never to denote the values that a routine call may pass to that routine, called arguments. In traditional software parlance “parameter” and “argument” are synonymous. Although the decision of which term to use for routines and which for generic classes is a matter of convention, it is desirable to stick to a consistent rule to avoid any confusion
$10.3 GENERIC CLASSES 323 Type checking Using genericity,you can guarantee that a data structure will only contain elements of a single type.Assuming a class contains the declarations sc:STACK [CIRCLE];sa:STACK [ACCOUNT];c:CIRCLE;a:ACCOUNT then the following are valid instructions in routines of that class: sc.put(c) --Push a circle onto a stack of circles sa,p(a) --Push an account onto a stack of accounts c=sc.item --Assign to a circle entity the top of a stack of circles but each of the following is invalid and will be rejected: sc.put (a), --Attempt to push an account onto a stack of circles sa.put (c); --Attempt to push a circle onto a stack of accounts c=sa.item --Attempt to access as a circle the top of a stack of accounts This will rule out erroneous operations of the kind described earlier,such as attempting to withdraw money from a circle. The type rule The type rule that makes the first set of examples valid and the second invalid is intuitively clear but let us make it precise. First the basic non-generic rule.Consider a feature declared as follows,with no use of any formal generic parameter,in a non-generic class C f(a:T):Uis... This will be the Fea- Then a call of the formxf(d),appearing in an arbitrary class B wherex is of type ture Application C,will be typewise correct if and only if:f is available to B-that is to say,generally rule,page 473. exported,or exported selectively to a set of classes including B;and d is of type T.(When we bring inheritance into the picture we will also accept d if its type is based on a descendant of T.)The result of the call-there is a result since the example assumes that is a function-is of type U. Now assume that C is generic,with G as formal generic parameter,and has a feature h (a:G):G is .. A call to h will be of the form y.h(e)for some entity y that has been declared,for some type /as :C[叮 The counterpart of the non-generic rule is that e must now be of type /(or a compatible type in the sense of inheritance),since the corresponding formal argument a is declared as being of type G,the formal generic parameter,and in the case ofy we may consider G,wherever it appears in class C,as a placeholder for I.Similarly,the result of the call will be of type V.The earlier examples all follow this model:a call of the form s.put (=requires an argument=of type POINT if s is of type STACK [POINT],INTEGER
§10.3 GENERIC CLASSES 323 Type checking Using genericity, you can guarantee that a data structure will only contain elements of a single type. Assuming a class contains the declarations sc: STACK [CIRCLE]; sa: STACK [ACCOUNT]; c: CIRCLE; a: ACCOUNT then the following are valid instructions in routines of that class: sc ● put (c) -- Push a circle onto a stack of circles sa ● put (a) -- Push an account onto a stack of accounts c := sc ● item -- Assign to a circle entity the top of a stack of circles but each of the following is invalid and will be rejected: sc ● put (a); -- Attempt to push an account onto a stack of circles sa ● put (c); -- Attempt to push a circle onto a stack of accounts c := sa ● item -- Attempt to access as a circle the top of a stack of accounts This will rule out erroneous operations of the kind described earlier, such as attempting to withdraw money from a circle. The type rule The type rule that makes the first set of examples valid and the second invalid is intuitively clear but let us make it precise. First the basic non-generic rule. Consider a feature declared as follows, with no use of any formal generic parameter, in a non-generic class C f (a: T ): U is … Then a call of the form x ● f (d ), appearing in an arbitrary class B where x is of type C, will be typewise correct if and only if: f is available to B — that is to say, generally exported, or exported selectively to a set of classes including B; and d is of type T. (When we bring inheritance into the picture we will also accept d if its type is based on a descendant of T.) The result of the call — there is a result since the example assumes that f is a function — is of type U. Now assume that C is generic, with G as formal generic parameter, and has a feature h (a: G): G is … A call to h will be of the form y ● h (e) for some entity y that has been declared, for some type V, as y: C [V] The counterpart of the non-generic rule is that e must now be of type V (or a compatible type in the sense of inheritance), since the corresponding formal argument a is declared as being of type G, the formal generic parameter, and in the case of y we may consider G, wherever it appears in class C, as a placeholder for V. Similarly, the result of the call will be of type V. The earlier examples all follow this model: a call of the form s ● put (z) requires an argument z of type POINT if s is of type STACK [POINT], INTEGER This will be the Feature Application rule, page 473
324 GENERICITY $10.3 if s is of type STACK [INTEGER];and s.item returns a result of type POINT in the first case and INTEGER in the second. These examples involve features with zero or one argument,but the rule immediately extends to an arbitrary number of arguments. Operations on entities of generic types In a generic class C[G.H....]consider an entity whose type is one of the formal generic parameters,for examplex oftype G.When the class is used by a client to declare entities, G may ultimately represent any type.So any operation that the routines of C perform on x must be applicable to all types.This leaves only five kinds of operation: Uses of entities of a formal generic type The valid uses for an entity x whose type G is a formal generic parameter are the following: GI.Use of x as left-hand side in an assignment,x=y,where the right- hand side expression y is also of type G. G2.Use ofx as right-hand side of an assignment y:=x,where the left- hand side entity y is also of type G. G3 Use ofx in a boolean expression of the form x=y or x/=y,where y is also of type G. G4.Use ofx as actual argument in a routine call corresponding to a formal argument declared of type G,or of type ANY. G5.Use as target of a call to a feature of ANY. In particular,a creation instruction of the form !!x is illegal,since we know nothing about the creation procedures,if any,defined for possible actual generic parameters corresponding to G. Cases G4 and G5 refer to class ANY.Mentioned a few times already,this class See "THE GLOBAL contains features that all classes will inherit.So you can be assured that whatever actual INHERITANCE type G represents in a particular generic derivation will have access to them.Among the STRUCTURE”, I6.2,page580. features of ANY are all the basic operations for copying and comparing objects:clone, copy,equal,copy,deep clone,deep_equal and others.This means it is all right,for x and y of a formal generic type G,to use instructions such as x.copy (y) x:=clone (y) if equal (x,y)then .. To check creek- Ignoring ANY,case G4 permits a call a.f(x)in a generic class C [G]if f takes a clarity do exercise formal argument of type G.In particular a could be of type D[G],where D is another E10.3,page331. generic class,declared as D [G]with a feature that takes an argument of type G,here
324 GENERICITY §10.3 if s is of type STACK [INTEGER]; and s ● item returns a result of type POINT in the first case and INTEGER in the second. These examples involve features with zero or one argument, but the rule immediately extends to an arbitrary number of arguments. Operations on entities of generic types In a generic class C [G, H, …] consider an entity whose type is one of the formal generic parameters, for example x of type G. When the class is used by a client to declare entities, G may ultimately represent any type. So any operation that the routines of C perform on x must be applicable to all types. This leaves only five kinds of operation: In particular, a creation instruction of the form !! x is illegal, since we know nothing about the creation procedures, if any, defined for possible actual generic parameters corresponding to G. Cases G4 and G5 refer to class ANY. Mentioned a few times already, this class contains features that all classes will inherit. So you can be assured that whatever actual type G represents in a particular generic derivation will have access to them. Among the features of ANY are all the basic operations for copying and comparing objects: clone, copy, equal, copy, deep_clone, deep_equal and others. This means it is all right, for x and y of a formal generic type G, to use instructions such as x ● copy (y) x := clone (y) if equal (x, y) then … Ignoring ANY, case G4 permits a call a ● f (x) in a generic class C [G] if f takes a formal argument of type G. In particular a could be of type D [G], where D is another generic class, declared as D [G] with a feature f that takes an argument of type G, here Uses of entities of a formal generic type The valid uses for an entity x whose type G is a formal generic parameter are the following: G1 • Use of x as left-hand side in an assignment, x := y, where the righthand side expression y is also of type G. G2 • Use of x as right-hand side of an assignment y := x, where the lefthand side entity y is also of type G. G3 • Use of x in a boolean expression of the form x = y or x /= y, where y is also of type G. G4 • Use of x as actual argument in a routine call corresponding to a formal argument declared of type G, or of type ANY. G5 • Use as target of a call to a feature of ANY. See “THE GLOBAL INHERITANCE STRUCTURE”, 16.2, page 580. To check creekclarity do exercise E10.3, page 331
$10.4 ARRAYS 325 denoting D's own formal generic parameter.(If the preceding sentence does not immediately make sense,please read it once more and it will,I hope,soon seem as clear as a mountain creek!) Types and classes We have learned to view the class,the central notion in object technology,as the product of the corporate merger between the module and type concepts.Until we had genericity, we could say that every class is a module and is also a type. With genericity,the second of these statements is not literally true any more, although the nuance will be small.A generic class declared as C [G]is,rather than a type, a type pattern covering an infinite set of possible types;you can obtain any one of these by providing an actual generic parameter-itself a type-corresponding to G. This yields a more general and flexible notion.But for what we gain in power we have to pay a small price in simplicity:only through a small abuse of language can we continue talking,if x is declared oftype 7,about"the features ofT"or"the clients of T"; other than a class,T may now be a generically derived type C[]for some generic class C and some type U.Of course there is still a class involved-class C-,which is why the abuse of language is acceptable. When we need to be rigorous the terminology is the following.Any type T is associated with a class,the base class of 7,so that it is always correct to talk about the features or clients of T's base class.If T is a non-generic class,then it is its own base class If T is a generic derivation of the form C[,...1,then the base class of T is C. “ANCHORED The notion of base class will again be useful when we introduce yet another kind of type, DECLARATION”, also(like all others in the O-O approach)based on classes,but indirectly:anchored types. 167,page598. 10.4 ARRAYS As a conclusion to this discussion it is useful to take a look at a very useful example of container class:ARRAY,which represents one-dimensional arrays. Arrays as objects The notion of array is usually part of a programming language's definition.But with object technology we do not need to burden the notation with special predefined constructs:an array is just a container object,an instance of a class which we may call ARRAY. A better version of the ARRAY is a good example of generic class.Here is a first outline: class,refving on assertions,appears in indexing “Arrays revisited”, description:"Sequences ofvalues,all of the same type or of a conforming one, page 373. %accessible through integer indices in a contiguous interval" class ARRAY [G]creation make
§10.4 ARRAYS 325 denoting D’s own formal generic parameter. (If the preceding sentence does not immediately make sense, please read it once more and it will, I hope, soon seem as clear as a mountain creek!) Types and classes We have learned to view the class, the central notion in object technology, as the product of the corporate merger between the module and type concepts. Until we had genericity, we could say that every class is a module and is also a type. With genericity, the second of these statements is not literally true any more, although the nuance will be small. A generic class declared as C [G] is, rather than a type, a type pattern covering an infinite set of possible types; you can obtain any one of these by providing an actual generic parameter — itself a type — corresponding to G. This yields a more general and flexible notion. But for what we gain in power we have to pay a small price in simplicity: only through a small abuse of language can we continue talking, if x is declared of type T, about “the features of T ” or “the clients of T”; other than a class, T may now be a generically derived type C [U] for some generic class C and some type U. Of course there is still a class involved — class C —, which is why the abuse of language is acceptable. When we need to be rigorous the terminology is the following. Any type T is associated with a class, the base class of T, so that it is always correct to talk about the features or clients of T’s base class. If T is a non-generic class, then it is its own base class. If T is a generic derivation of the form C [U, …], then the base class of T is C. The notion of base class will again be useful when we introduce yet another kind of type, also (like all others in the O-O approach) based on classes, but indirectly: anchored types. 10.4 ARRAYS As a conclusion to this discussion it is useful to take a look at a very useful example of container class: ARRAY, which represents one-dimensional arrays. Arrays as objects The notion of array is usually part of a programming language’s definition. But with object technology we do not need to burden the notation with special predefined constructs: an array is just a container object, an instance of a class which we may call ARRAY. ARRAY is a good example of generic class. Here is a first outline: indexing description: "Sequences of values, all of the same type or of a conforming one, % %accessible through integer indices in a contiguous interval" class ARRAY [G] creation make “ANCHORED DECLARATION”, 16.7, page 598. A better version of the class, relying on assertions, appears in “Arrays revisited”, page 373
326 GENERICITY $10.4 feature make (minindex,maxindex:INTEGER)is --Allocate array with bounds minindex and maxindex -(empty if minindex maxindex) do...end lower,upper,count:INTEGER --Minimum and maximum legal index;array size. put (v:G;i:INTEGER)is -Assign v to the entry of index i do...end infix "@"item (i:INTEGER):G is --Entry of index i do...end end -class ARRAY To create an array of bounds m and n,with a declared of type ARRAY [T]for some type 7,you will execute the creation instruction !a.make (m,n) To set the value of an array element you will use procedure put:the call a.put (x,i) sets the value of the i-th element to x.To access the value of an element you will use function item (the synonym infix "@will be explained shortly),as in x :a.item (i) Here is a sketch of how you might use the class from a client: pa:ARRAY [POINT];pl:POINT;i,j:INTEGER !pa.make (-32,101) --Allocate array with the bounds shown. pa-put (pl,i) --Assign p/to entry of index i. pl:=pa.item (j) --Assign to pl the value of entry of index j. In conventional (say Pascal)notation,you would write pa[川:=pl for pa.put (i,pl) pl=pa[川] for pl:=pa.item (i) Array properties A few observations on the preceding class: Similar classes exist for arrays with more dimensions:ARRAY2 etc. Feature count may be implemented as either an attribute or a function,since it satisfies count upper-lower+1.This is expressed in the actual class by an invariant,as explained in the next chapter
326 GENERICITY §10.4 feature make (minindex, maxindex: INTEGER) is -- Allocate array with bounds minindex and maxindex -- (empty if minindex > maxindex) do … end lower, upper, count: INTEGER -- Minimum and maximum legal index; array size. put (v: G; i: INTEGER) is -- Assign v to the entry of index i do … end infix "@", item (i: INTEGER): G is -- Entry of index i do … end end -- class ARRAY To create an array of bounds m and n, with a declared of type ARRAY [T] for some type T, you will execute the creation instruction !! a ● make (m, n) To set the value of an array element you will use procedure put: the call a ● put (x, i) sets the value of the i-th element to x. To access the value of an element you will use function item (the synonym infix "@" will be explained shortly), as in x := a ● item (i) Here is a sketch of how you might use the class from a client: pa: ARRAY [POINT]; p1: POINT; i, j: INTEGER … !! pa ● make (–32, 101) -- Allocate array with the bounds shown. pa ● put (p1, i) -- Assign p1 to entry of index i. … p1 := pa ● item (j) -- Assign to p1 the value of entry of index j. In conventional (say Pascal) notation, you would write pa [i] := p1 for pa ● put (i, p1) p1 := pa [i] for p1 := pa ● item (i) Array properties A few observations on the preceding class: • Similar classes exist for arrays with more dimensions: ARRAY2 etc. • Feature count may be implemented as either an attribute or a function, since it satisfies count = upper – lower+1. This is expressed in the actual class by an invariant, as explained in the next chapter