18 Global objects and constants ocalknowledge is not always enouh components of a sofware system may need to access global information.It is easy to think of examples:a shared value,such as the size of available memory;an error window,to which all the components of an interactive system must be able to output messages;the gateway to a database or network In classical approaches,it is not difficult to provide for global objects;you just declare them as global variables,owned by the main program.In the modular style of design made possible by object-oriented techniques,there is neither a main program nor global variables.But even if our software texts do not include global variables our software executions may still need to share objects. Such global objects pose a challenge to the method.Object technology is all about decentralization,all about modularity,all about autonomy.It has developed from the beginning of this presentation as a war of independence for the modules,each fighting for its freedom from the excesses of central authority.In fact,there is no central authority any more.How then do we satisfy the need for common institutions?In other words,how do we allow components to share data in a simple way,without jeopardizing their autonomy, flexibility and reusability? It will not work,of course,to pass shared objects as arguments to the modules that need them.This would soon become clumsy if too many components need them.Besides, argument passing assumes that one module owns the value and then passes it on to others; in the case of a truly shared value no one module can claim ownership. To find a better answer we will start from a well-known notion,which we need in object-oriented software construction just as much as we did in more traditional approaches:constants.What is,after all,a constant such as Pi if not a simple object shared by many modules?Generalizing this notion to more complex objects will provide a first step towards fully general constant and shared objects. 18.1 CONSTANTS OF BASIC TYPES Let us start with a simple notation to denote constant values
18 Global objects and constants Local knowledge is not always enough; components of a software system may need to access global information. It is easy to think of examples: a shared value, such as the size of available memory; an error window, to which all the components of an interactive system must be able to output messages; the gateway to a database or network. In classical approaches, it is not difficult to provide for global objects; you just declare them as global variables, owned by the main program. In the modular style of design made possible by object-oriented techniques, there is neither a main program nor global variables. But even if our software texts do not include global variables our software executions may still need to share objects. Such global objects pose a challenge to the method. Object technology is all about decentralization, all about modularity, all about autonomy. It has developed from the beginning of this presentation as a war of independence for the modules, each fighting for its freedom from the excesses of central authority. In fact, there is no central authority any more. How then do we satisfy the need for common institutions? In other words, how do we allow components to share data in a simple way, without jeopardizing their autonomy, flexibility and reusability? It will not work, of course, to pass shared objects as arguments to the modules that need them. This would soon become clumsy if too many components need them. Besides, argument passing assumes that one module owns the value and then passes it on to others; in the case of a truly shared value no one module can claim ownership. To find a better answer we will start from a well-known notion, which we need in object-oriented software construction just as much as we did in more traditional approaches: constants. What is, after all, a constant such as Pi if not a simple object shared by many modules? Generalizing this notion to more complex objects will provide a first step towards fully general constant and shared objects. 18.1 CONSTANTS OF BASIC TYPES Let us start with a simple notation to denote constant values
644 GLOBAL OBJECTS AND CONSTANTS $18.1 Using symbolic constants A rule of software style,the Symbolic Constant principle,states that when an algorithm "Mamifest and sym refers to a certain value-a number,a character,a string...-it should almost never use bolic constants". it directly.Instead,a declaration should associate a name with the value,so that the page 884.See also “odular continu- algorithm can use the name(known as a symbolic constant)rather than the value(known ity"page 44. as a manifest constant).Two reasons justify this principle: Readability:someone who reads your software may not understand what the value 50 is doing in a certain algorithm;if instead you use the symbolic constant US states count everything is clear. Extendibility:in practice,with a few exceptions (such as the value of n,unlikely to change soon),the only constant thing about constants is change.To update the value of a constant it suffices,if you have been using symbolic constants,to change one declaration.This is much nicer than having to chase throughout the software for all the places that may have relied on the earlier value. The principle permits using manifest constants(hence the word"almost"above)for zero elements of various operations,as in a loop from i:=/until i>n...iterating over the elements of an array whose numbering follows the default convention of starting at 1. (But n should be symbolic,not manifest.) Although few software developers apply the Symbolic Constant principle as systematically as they should,the benefits of declaring a symbolic constant are well worth the small extra effort.So we need a clear and simple way of defining symbolic constants in an O-O framework. Constant attributes A symbolic constant,like everything else,will be defined in a class.We will simply treat a constant value as an attribute which happens to have a fixed value,the same for all instances of the class. For the syntax,we can reuse the keyword is which already serves to introduce routines;only here it will be followed by a value of the appropriate type,rather than by an algorithm.The following examples include one for each of the basic types /NTEGER, BOOLEAN,REAL and CHARACTER: Zero:INTEGER is 0 Ok:BOOLEAN is True Pi:REAL is3.1415926524 Backslash:CHARACTER is' Backslash is of type CHARACTER,its value a single character.Constants of string type, denoting character strings of arbitrary length,will be discussed below. As these examples illustrate,the recommended style convention for names of constant attributes is to start with a capital letter,with the rest in lower case. A descendant may not redefine the value of a constant attribute
644 GLOBAL OBJECTS AND CONSTANTS §18.1 Using symbolic constants A rule of software style, the Symbolic Constant principle, states that when an algorithm refers to a certain value — a number, a character, a string… — it should almost never use it directly. Instead, a declaration should associate a name with the value, so that the algorithm can use the name (known as a symbolic constant) rather than the value (known as a manifest constant). Two reasons justify this principle: • Readability: someone who reads your software may not understand what the value 50 is doing in a certain algorithm; if instead you use the symbolic constant US_states_ count everything is clear. • Extendibility: in practice, with a few exceptions (such as the value of π, unlikely to change soon), the only constant thing about constants is change. To update the value of a constant it suffices, if you have been using symbolic constants, to change one declaration. This is much nicer than having to chase throughout the software for all the places that may have relied on the earlier value. The principle permits using manifest constants (hence the word “almost” above) for zero elements of various operations, as in a loop from i := 1 until i > n … iterating over the elements of an array whose numbering follows the default convention of starting at 1. (But n should be symbolic, not manifest.) Although few software developers apply the Symbolic Constant principle as systematically as they should, the benefits of declaring a symbolic constant are well worth the small extra effort. So we need a clear and simple way of defining symbolic constants in an O-O framework. Constant attributes A symbolic constant, like everything else, will be defined in a class.We will simply treat a constant value as an attribute which happens to have a fixed value, the same for all instances of the class. For the syntax, we can reuse the keyword is which already serves to introduce routines; only here it will be followed by a value of the appropriate type, rather than by an algorithm. The following examples include one for each of the basic types INTEGER, BOOLEAN, REAL and CHARACTER: Zero: INTEGER is 0 Ok: BOOLEAN is True Pi: REAL is 3.1415926524 Backslash: CHARACTER is '\' Backslash is of type CHARACTER, its value a single character. Constants of string type, denoting character strings of arbitrary length, will be discussed below. As these examples illustrate, the recommended style convention for names of constant attributes is to start with a capital letter, with the rest in lower case. A descendant may not redefine the value of a constant attribute. “Manifest and symbolic constants”, page 884. See also “Modular continuity”, page 44
$18.2 USE OF CONSTANTS 645 Like other attributes,constant attributes are either exported or secret;if they are exported,clients of the class may access them through feature calls.So if C is the class containing the above declarations and x,declared of type C,has a non-void value,then x.Backslash denotes the backslash character. Unlike variable attributes,constant attributes do not occupy any space at run time in instances of the class.So there is no run-time penalty for adding as many constant attributes as you need. 18.2 USE OF CONSTANTS Here is an example showing how clients may use constant attributes defined in a class: class FILE feature error code:INTEGER: --Variable attribute Ok:INTEGER is 0 Open_error:INTEGER is 1 open (file name:STRING)is --Open file of name file name --and associate it with current file object do error code:=Ok if“Something went wrong”then error code:=Open error end end ..Other features... end A client may call open and compare the resulting error code to any of the constants to test how the operation went: 大FILE,… f.open if f.error code =f.Open error then “Appropriate action else end Often,however,a group of constants is needed without being attached to any particular object.For example,a system performing physics computations may use some numerical constants;or a text editor may need character constants describing the character
§18.2 USE OF CONSTANTS 645 Like other attributes, constant attributes are either exported or secret; if they are exported, clients of the class may access them through feature calls. So if C is the class containing the above declarations and x, declared of type C, has a non-void value, then x ● Backslash denotes the backslash character. Unlike variable attributes, constant attributes do not occupy any space at run time in instances of the class. So there is no run-time penalty for adding as many constant attributes as you need. 18.2 USE OF CONSTANTS Here is an example showing how clients may use constant attributes defined in a class: class FILE feature error_code: INTEGER; -- Variable attribute Ok: INTEGER is 0 Open_error: INTEGER is 1 … open (file_name: STRING) is -- Open file of name file_name -- and associate it with current file object do error_code := Ok … if “Something went wrong” then error_code := Open_error end end … Other features … end A client may call open and compare the resulting error code to any of the constants to test how the operation went: f: FILE; … f ● open if f ● error_code = f ● Open_error then “Appropriate action” else … end Often, however, a group of constants is needed without being attached to any particular object. For example, a system performing physics computations may use some numerical constants; or a text editor may need character constants describing the character
646 GLOBAL OBJECTS AND CONSTANTS $18.3 keys associated with various commands.In such a case,the constants will still be grouped in a class(where else could they be?),but there will not be any instances of that class;it is simply used as parent for the classes that need to access the constants,as in class EDITOR CONSTANTS feature Insert:CHARACTER is'i Delete:CHARACTER is'd';--etc. end elass SOME CLASS FOR THE EDITOR inherit EDITOR CONSTANTS ..Other possible parents .. feature.… ..Routines of the class have access to the constants declared in EDITOR CONSTANTS... end A class such as EDITOR CONSTANTS is used only to host a group of related See"FACILITY constants,and its role as an "abstract data type implementation"(our working definition INHERITANCE". of the notion of class)is less obvious than in earlier examples.But it definitely serves a 24.9,page847. useful purpose.We will examine its theoretical justification in a later chapter. The scheme shown would not work without multiple inheritance,since SOME CLASS FOR THE ED/TOR may need other parents,either for access to other constants or for more standard uses of inheritance. 18.3 CONSTANTS OF CLASS TYPES Symbolic constants,allowing you to use identifiers to denote certain constant values,are not just use ful for predefined types such as /NTEGER;the need also arises for types that developers have defined,through classes.Here the solution is less obvious. Manifest constants are inappropriate for class types A typical example in which you may need to define a constant for a non-basic types is that of a class describing complex numbers: class COMPLEX creation make cartesian,make polar feature x,y:REAL --Real and imaginary parts
646 GLOBAL OBJECTS AND CONSTANTS §18.3 keys associated with various commands. In such a case, the constants will still be grouped in a class (where else could they be?), but there will not be any instances of that class; it is simply used as parent for the classes that need to access the constants, as in class EDITOR_CONSTANTS feature Insert: CHARACTER is 'i' Delete: CHARACTER is 'd'; -- etc. … end class SOME_CLASS_FOR_THE_EDITOR inherit EDITOR_CONSTANTS … Other possible parents … feature … … Routines of the class have access to the constants declared in EDITOR_CONSTANTS … end A class such as EDITOR_CONSTANTS is used only to host a group of related constants, and its role as an “abstract data type implementation” (our working definition of the notion of class) is less obvious than in earlier examples. But it definitely serves a useful purpose. We will examine its theoretical justification in a later chapter. The scheme shown would not work without multiple inheritance, since SOME_ CLASS_FOR_THE_EDITOR may need other parents, either for access to other constants or for more standard uses of inheritance. 18.3 CONSTANTS OF CLASS TYPES Symbolic constants, allowing you to use identifiers to denote certain constant values, are not just useful for predefined types such as INTEGER; the need also arises for types that developers have defined, through classes. Here the solution is less obvious. Manifest constants are inappropriate for class types A typical example in which you may need to define a constant for a non-basic types is that of a class describing complex numbers: class COMPLEX creation make_cartesian, make_polar feature x, y: REAL -- Real and imaginary parts See “FACILITY INHERITANCE”, 24.9, page 847
$18.3 CONSTANTS OF CLASS TYPES 647 make cartesian (a,b:REAL)is --Initialize with real part a,imaginary part b. do x:=a;y:=b end ..Other routines (x and y are the only attributes)... end You may want to define the complex number i,with real part 0 and imaginary part 1.The first idea that comes to mind is a manifest constant notation such as i:COMPLEX is "Expression specifying the complex number (0,1)" How can you write the expression after is?For simple types,the manifest constants were self-evident:345 is a constant of type integer,'4'of type character.But no such predefined notation is available for developer-defined class types. One could imagine a notation based on the attributes of the class;something like Not a retained nota- i:COMPLEX is COMPLEX (0.1 tion.For purposes of illustration only. But such an approach (although present in some O-O languages)is incompatible with the principles of modularity which serve as the basis for object technology.It would mean requiring clients of COMPLEX to describe constants in terms of the implementation. This breaks information hiding.You could not add an attribute,even a secret one,without invalidating client code;neither could you re-implement an attribute such as x as a function(to switch internally to a polar representation). Besides,how could you make sure that such manifest constants will satisfy the class invariant if there is one? This last remark opens the way to a correct solution.An earlier chapter noted that it is the responsibility of the creation procedures to make sure that every object satisfies the invariant immediately upon creation.Creating objects in any other way (apart from the safe companion mechanism,clone)would lead to error situations.So we should look for a mechanism that,rather than manifest objects in the above style,will rely on the usual technique for object creation. Once functions We may view a constant object as a function.For example i could be defined within class COMPLEX itself as i:COMPLEX is --Complex number with real part 0 and imaginary part I do !Result.make cartesian (0,1) end
§18.3 CONSTANTS OF CLASS TYPES 647 make_cartesian (a, b: REAL) is -- Initialize with real part a, imaginary part b. do x := a; y := b end … Other routines (x and y are the only attributes) … end You may want to define the complex number i, with real part 0 and imaginary part 1. The first idea that comes to mind is a manifest constant notation such as i: COMPLEX is “Expression specifying the complex number (0, 1)” How can you write the expression after is? For simple types, the manifest constants were self-evident: 345 is a constant of type integer, 'A' of type character. But no such predefined notation is available for developer-defined class types. One could imagine a notation based on the attributes of the class; something like i: COMPLEX is COMPLEX (0, 1) But such an approach (although present in some O-O languages) is incompatible with the principles of modularity which serve as the basis for object technology. It would mean requiring clients of COMPLEX to describe constants in terms of the implementation. This breaks information hiding. You could not add an attribute, even a secret one, without invalidating client code; neither could you re-implement an attribute such as x as a function (to switch internally to a polar representation). Besides, how could you make sure that such manifest constants will satisfy the class invariant if there is one? This last remark opens the way to a correct solution. An earlier chapter noted that it is the responsibility of the creation procedures to make sure that every object satisfies the invariant immediately upon creation. Creating objects in any other way (apart from the safe companion mechanism, clone) would lead to error situations. So we should look for a mechanism that, rather than manifest objects in the above style, will rely on the usual technique for object creation. Once functions We may view a constant object as a function. For example i could be defined within class COMPLEX itself as i: COMPLEX is -- Complex number with real part 0 and imaginary part 1 do !! Result ● make_cartesian (0, 1) end Not a retained notation. For purposes of illustration only
648 GLOBAL OBJECTS AND CONSTANTS $18.4 This almost does the job,since the function will always return a reference to an object of the desired form.Since we rely on normal creation procedures,the invariant will be satisfied,so we will only produce consistent objects. The result,however,is not exactly what we need:each client use of i in the client produces a new object,identical to all the others.This is a waste of time and space: To get the proper behavior,we need a special kind of function:one which executes its body only the first time it is called.We can call this a once function.A once function is otherwise similar to a normal function;syntactically,it will be distinguished by the keyword once,replacing the usual do,to introduce the body: i:COMPLEX is --Complex num ber with real part 0 and imaginary part I once喝 The only change !Result.make cartesian (0,1) end The first time a once function is called during a system's execution,it executes its body.In the example this creates an object representing the desired complex number,and returns a reference to that object.Every subsequent call executes no instruction at all,but terminates immediately,returning the result computed the first time around. Regarding efficiency:a call to i other than the first should take only marginally longer than an attribute access. The result computed by the first call to a once function is applicable to all instances of a class,in the general sense of the word "instance"covering instances of descendants as well,except of course for any descendant that redefines the function.As a consequence you can freely redefine functions from once to non-once and conversely.Here if a descendant COMPLEXI of COMPLEX redefines i,a call to i on an instance of COMPLEY/will use the redefined version (whether once or non-once);a call on a direct instance of COMPLEX or a descendant other than COMPLEX/will use the once function, that is to say the value computed by the first such call. 18.4 APPLICATIONS OF ONCE ROUTINES The notion ofonce routine extends beyond examples such as i to more general applications: shared objects,global system parameters,initialization of common properties. Shared objects For reference types such as COMPLEX,as you may have noted,the"once"mechanism actually offers constant references,not necessarily constant objects.It guarantees that the body of the function is executed only once,to compute a result,which later calls will also return without further computation. If the function returns a value of a reference type,its body will usually contain a creation instruction,as in the example of i.All calls will return a reference to the object
648 GLOBAL OBJECTS AND CONSTANTS §18.4 This almost does the job, since the function will always return a reference to an object of the desired form. Since we rely on normal creation procedures, the invariant will be satisfied, so we will only produce consistent objects. The result, however, is not exactly what we need: each client use of i in the client produces a new object, identical to all the others. This is a waste of time and space: To get the proper behavior, we need a special kind of function: one which executes its body only the first time it is called. We can call this a once function. A once function is otherwise similar to a normal function; syntactically, it will be distinguished by the keyword once, replacing the usual do, to introduce the body: i: COMPLEX is -- Complex number with real part 0 and imaginary part 1 once !! Result ● make_cartesian (0, 1) end The first time a once function is called during a system’s execution, it executes its body. In the example this creates an object representing the desired complex number, and returns a reference to that object. Every subsequent call executes no instruction at all, but terminates immediately, returning the result computed the first time around. Regarding efficiency: a call to i other than the first should take only marginally longer than an attribute access. The result computed by the first call to a once function is applicable to all instances of a class, in the general sense of the word “instance” covering instances of descendants as well, except of course for any descendant that redefines the function. As a consequence you can freely redefine functions from once to non-once and conversely. Here if a descendant COMPLEX1 of COMPLEX redefines i, a call to i on an instance of COMPLEX1 will use the redefined version (whether once or non-once); a call on a direct instance of COMPLEX or a descendant other than COMPLEX1 will use the once function, that is to say the value computed by the first such call. 18.4 APPLICATIONS OF ONCE ROUTINES The notion of once routine extends beyond examples such as i to more general applications: shared objects, global system parameters, initialization of common properties. Shared objects For reference types such as COMPLEX, as you may have noted, the “once” mechanism actually offers constant references, not necessarily constant objects. It guarantees that the body of the function is executed only once, to compute a result, which later calls will also return without further computation. If the function returns a value of a reference type, its body will usually contain a creation instruction, as in the example of i. All calls will return a reference to the object The only change
$18.4 APPLICATIONS OF ONCE ROUTINES 649 created by the first.Although the creation will never be executed again,nothing prevents callers from modifying the object through the reference.Therefore the mechanism provides shared objects rather than constant ones. An example of a shared object,cited at the beginning of this chapter,is a window showing error messages in an interactive system.Assume we have decided that any component of the system that detects a user error may output a message to that window, through a call of the form Message_window.put_text ("Appropriate error message") Here message window is of type WINDOW,with class WINDOW declared as class WINDOW creation make feature make (...is --Create window at size and position indicated by arguments. do...end text:STRING --Text to be displayed in window put text (s:STRING)is --Make s the text to be displayed in window. do text=s end .Other features.… end--class WINDOW Obviously Message window must be the same for all components of the system This is achieved by declaring the corresponding feature as a once function: Message window:WINDOW is --Window where error messages will be output once !Result.make ("...Size and position arguments...") end In this case the message window object must be shared by all its users,but it is not a constant object:each call to pur text changes the object by putting its own chosen text in it.The best place to declare Message window is a class from which all system components needing access to the message window will inherit. In the case of a shared object that denotes a constant,such as i,you may want to disallow calls of the form i.some_procedure that might change the fields.To achieve this,simply include clauses i.x=0 and i.y=/in the class invariant
§18.4 APPLICATIONS OF ONCE ROUTINES 649 created by the first. Although the creation will never be executed again, nothing prevents callers from modifying the object through the reference. Therefore the mechanism provides shared objects rather than constant ones. An example of a shared object, cited at the beginning of this chapter, is a window showing error messages in an interactive system. Assume we have decided that any component of the system that detects a user error may output a message to that window, through a call of the form Message_window ● put_text ("Appropriate error message") Here message_window is of type WINDOW, with class WINDOW declared as class WINDOW creation make feature make (…) is -- Create window at size and position indicated by arguments. do … end text: STRING -- Text to be displayed in window put_text (s: STRING) is -- Make s the text to be displayed in window. do text := s end … Other features … end -- class WINDOW Obviously Message_window must be the same for all components of the system. This is achieved by declaring the corresponding feature as a once function: Message_window: WINDOW is -- Window where error messages will be output once !! Result ● make ("…Size and position arguments…") end In this case the message window object must be shared by all its users, but it is not a constant object: each call to put_text changes the object by putting its own chosen text in it. The best place to declare Message_window is a class from which all system components needing access to the message window will inherit. In the case of a shared object that denotes a constant, such as i, you may want to disallow calls of the form i ● some_procedure that might change the fields. To achieve this, simply include clauses i ● x = 0 and i ● y = 1 in the class invariant
650 GLOBAL OBJECTS AND CONSTANTS $18.4 Once functions returning results of basic types Another application of once functions is to represent global values-"system parameters"-used by several classes in a system.Such values will usually be constant over a given system execution;they are initially computed from user input,or from some information obtained from the environment.For example: The components of a low-level system may need to know the available memory space,obtained from the environment at initialization time. A terminal handler may start by querying the environment about the number of terminal ports:once obtained,these data elements are then used by several modules of the application. Such global values are similar to shared objects such as Message window;but in general they are values of basic types rather than class instances.You may represent them through once functions.The scheme is: Const value:Tis --A system parameter computed only once local envir param:T Any type (T or another) once "Get the value of envir param from the environment" Result:="Some value computed from envir param" end Such once functions of basic types describe dynamically computed constants. Assume the above declaration is in a class ENVIR.A class needing to use Const value See"Modular will get it simply by listing ENVIR among its parents.There is no need here for an decomposability". initialization routine as might be used in classical approaches to compute Const value, page 40. along with all other global parameters,at the beginning of system execution.As was seen in an earlier chapter,such a routine would have to access the internal details of many other modules,and hence would violate the criteria and principles of modularity: decomposability,few interfaces,information hiding etc.In contrast,classes such as ENIIR may be designed as coherent modules,each describing a set of logically related global values.The first component that requests the value of a global parameter such as Const value at execution time will trigger its computation from the environment. Although Const value is a function,components that use it may treat it as if it were a constant attribute. The introduction to this chapter mentioned that none of the modules that use a shared value has more claim to own it than any of the others.This is especially true in the cases just seen:if,depending on the order of events in each execution of the system,any one among a set of modules may trigger the computation of the value,it would be improper to designate any single one among them as the owner.The modular structure reflects this
650 GLOBAL OBJECTS AND CONSTANTS §18.4 Once functions returning results of basic types Another application of once functions is to represent global values — “system parameters” — used by several classes in a system. Such values will usually be constant over a given system execution; they are initially computed from user input, or from some information obtained from the environment. For example: • The components of a low-level system may need to know the available memory space, obtained from the environment at initialization time. • A terminal handler may start by querying the environment about the number of terminal ports: once obtained, these data elements are then used by several modules of the application. Such global values are similar to shared objects such as Message_window; but in general they are values of basic types rather than class instances. You may represent them through once functions. The scheme is: Const_value: T is -- A system parameter computed only once local envir_param: T ' -- Any type (T or another) once “Get the value of envir_param from the environment” Result := “Some value computed from envir_param” end Such once functions of basic types describe dynamically computed constants. Assume the above declaration is in a class ENVIR. A class needing to use Const_value will get it simply by listing ENVIR among its parents. There is no need here for an initialization routine as might be used in classical approaches to compute Const_value, along with all other global parameters, at the beginning of system execution. As was seen in an earlier chapter, such a routine would have to access the internal details of many other modules, and hence would violate the criteria and principles of modularity: decomposability, few interfaces, information hiding etc. In contrast, classes such as ENVIR may be designed as coherent modules, each describing a set of logically related global values. The first component that requests the value of a global parameter such as Const_value at execution time will trigger its computation from the environment. Although Const_value is a function, components that use it may treat it as if it were a constant attribute. The introduction to this chapter mentioned that none of the modules that use a shared value has more claim to own it than any of the others. This is especially true in the cases just seen: if, depending on the order of events in each execution of the system, any one among a set of modules may trigger the computation of the value, it would be improper to designate any single one among them as the owner. The modular structure reflects this. See “Modular decomposability”, page 40
$18.4 APPLICATIONS OF ONCE ROUTINES 651 Once procedures The function close should only be called once.We recommend using a global variable in your application to check that close is not called more than once. (From the manual for a commercial C library.) The "once"mechanism is interesting not just for functions but for procedures as well A once procedure is appropriate when some facility used on a system-wide basis must be initialized,but it is not known in advance which system component will be the first to use the facility.It is like having a rule that whoever comes in first in the morning should turn on the heating A simple example is a graphics library providing a number of display routines, where the first display routine called in any system execution must set up the terminal.The library author could of course require every client to perform a setup call before the first display call.This is a nuisance for clients and does not really solve the problem anyway: to deal properly with errors,any routine should be able to detect that it has been called without proper setup;but if it is smart enough to detect this case,the routine might just as well do the setup and avoid bothering the client! Once procedures provide a better solution: check setup is --Perform terminal setup if not done yet. once terminal setup --Actual setup action end Thenevery display routine in the library should begin with a call to check setup.The first call will do the setup;subsequent ones will do nothing.Note that check setup does not have to be exported;client authors do not need to know about it. This is an important technique to improve the usability of any library or other software package.Any time you can remove a usage rule-such as "Always call procedure xy=before the first operation"-and instead take care of the needed operations automatically and silently,you have made the software better. Arguments Like other routines,once routines-procedures and functions-can have arguments But because of the definition of the mechanism,these arguments are only useful in the call that gets executed first. In the earlier analogy,imagine a thermostat dial which anyone coming into the building may turn to any marking,but such that only the first person to do so will set the temperature:subsequent attempts have no effect
§18.4 APPLICATIONS OF ONCE ROUTINES 651 Once procedures The function close should only be called once. We recommend using a global variable in your application to check that close is not called more than once. (From the manual for a commercial C library.) The “once” mechanism is interesting not just for functions but for procedures as well. A once procedure is appropriate when some facility used on a system-wide basis must be initialized, but it is not known in advance which system component will be the first to use the facility. It is like having a rule that whoever comes in first in the morning should turn on the heating. A simple example is a graphics library providing a number of display routines, where the first display routine called in any system execution must set up the terminal. The library author could of course require every client to perform a setup call before the first display call. This is a nuisance for clients and does not really solve the problem anyway: to deal properly with errors, any routine should be able to detect that it has been called without proper setup; but if it is smart enough to detect this case, the routine might just as well do the setup and avoid bothering the client! Once procedures provide a better solution: check_setup is -- Perform terminal setup if not done yet. once terminal_setup -- Actual setup action end Then every display routine in the library should begin with a call to check_setup. The first call will do the setup; subsequent ones will do nothing. Note that check_setup does not have to be exported; client authors do not need to know about it. This is an important technique to improve the usability of any library or other software package. Any time you can remove a usage rule — such as “Always call procedure xyz before the first operation” — and instead take care of the needed operations automatically and silently, you have made the software better. Arguments Like other routines, once routines — procedures and functions — can have arguments. But because of the definition of the mechanism, these arguments are only useful in the call that gets executed first. In the earlier analogy, imagine a thermostat dial which anyone coming into the building may turn to any marking, but such that only the first person to do so will set the temperature: subsequent attempts have no effect
652 GLOBAL OBJECTS AND CONSTANTS $18.4 Once functions,anchoring and genericity (This section addresses a specific technical point and may be skipped on first reading.) Once functions of class types carry a potential incompatibility with anchored types and genericity. Let us start with genericity.In a generic class EXAMPLE [G]assume a once function returning a value whose type is the formal generic parameter: f:G is once...end Warning:not valid. See below. and consider a possible use: character_example:EXAMPLE [CHARACTER] print (character example.f) So far so good.But you also try to do something with another generic derivation: integer_example:EXAMPLE [INTEGER] print (integer example.f+1) The last instruction adds two integer values.Unfortunately,the first of them,the result of calling /has already been computed since is a once function;and it is a character,not an integer.The addition is not valid. The problem is that we are sharing a value between different generic derivations which expect the type of that value to depend on the actual generic parameter. A similar issue arises with anchored types.Assume a class B which adds an attribute to the features of its parent A: class B inherit A feature attribute of B:INTEGER end Assume that 4 had a once function /returning a result of anchored type: f:like Current is once !Result.make end Warning:not valid. See below. and that the first evaluation of is in a2:=al.f with a/and a2 of type 4.The evaluation of/creates a direct instance ofA,and attaches it to entity a2,also of type 4.Fine.But assume now that a subsequent use of/is b2:=bl.f where b/and b2 are of type B.If/were a non-once function,this would not cause any problem,since the call would now produce and return a direct instance of B.Since here we have a once function,the result has already been computed through the first call;and that result is a direct instance of A,not B.So an instruction such as print (b2.attribute of B)
652 GLOBAL OBJECTS AND CONSTANTS §18.4 Once functions, anchoring and genericity (This section addresses a specific technical point and may be skipped on first reading.) Once functions of class types carry a potential incompatibility with anchored types and genericity. Let us start with genericity. In a generic class EXAMPLE [G] assume a once function returning a value whose type is the formal generic parameter: f: G is once … end and consider a possible use: character_example: EXAMPLE [CHARACTER] … print (character_example ● f) So far so good. But you also try to do something with another generic derivation: integer_example: EXAMPLE [INTEGER] … print (integer_example ● f + 1) The last instruction adds two integer values. Unfortunately, the first of them, the result of calling f, has already been computed since f is a once function; and it is a character, not an integer. The addition is not valid. The problem is that we are sharing a value between different generic derivations which expect the type of that value to depend on the actual generic parameter. A similar issue arises with anchored types. Assume a class B which adds an attribute to the features of its parent A: class B inherit A feature attribute_of_B: INTEGER end Assume that A had a once function f, returning a result of anchored type: f: like Current is once !! Result ● make end and that the first evaluation of f is in a2 := a1 ● f with a1 and a2 of type A. The evaluation of f creates a direct instance of A, and attaches it to entity a2, also of type A. Fine. But assume now that a subsequent use of f is b2 := b1 ● f where b1 and b2 are of type B. If f were a non-once function, this would not cause any problem, since the call would now produce and return a direct instance of B. Since here we have a once function, the result has already been computed through the first call; and that result is a direct instance of A, not B. So an instruction such as print (b2 ● attribute_of_B) Warning: not valid. See below. Warning: not valid. See below