diff --git a/documentation/current/community/index.wiki b/documentation/current/community/index.wiki
index 389d5854..62c6d8da 100644
--- a/documentation/current/community/index.wiki
+++ b/documentation/current/community/index.wiki
@@ -1,6 +1,20 @@
[[Property:title|Community]]
[[Property:weight|2]]
[[Property:uuid|75c75712-7c3e-2757-51b7-77b404471e2e]]
-The Community book is a vehicle through which community members will be able to submit and/or revise documentation content.
+EiffelStudio community:
+:'''You can become a contributor!'''
+EiffelStudio documentation is available in a convenient Wiki format. This makes it possible to improve the documentation continuously and make sure it is always up to date.
+
+Community input is welcome. If you are interested in improving and developing EiffelStudio documentation, you can become:
+
+* A contributor: authorized to edit existing pages to any book, and to add pages to the "Community contributions" book.
+**"Community contributions" book pages can be added to other books by an editor.
+
+* An editor: authorized to modify and add pages in any book.
+
+To become a contributor, you should be proficient in Eiffel technology and have good written English skills. To become an editor, you should already be a contributor and have contributed significant improvements or additions to the documentation.
+
+If you are interested in becoming a contributor, please send an email to info@eiffel.com with a short description of your Eiffel experience and any other relevant background.
+
diff --git a/documentation/current/method/eiffel-tutorial-et/et-design-contract-tm-assertions-and-exceptions.wiki b/documentation/current/method/eiffel-tutorial-et/et-design-contract-tm-assertions-and-exceptions.wiki
index 1d4b62ca..861310c4 100644
--- a/documentation/current/method/eiffel-tutorial-et/et-design-contract-tm-assertions-and-exceptions.wiki
+++ b/documentation/current/method/eiffel-tutorial-et/et-design-contract-tm-assertions-and-exceptions.wiki
@@ -9,6 +9,8 @@ Eiffel directly implements the ideas of Design by Contract , which enhance
A system -- a software system in particular, but the ideas are more general -- is made of a number of cooperating components. Design by Contract states that their cooperation should be based on precise specifications -- contracts -- describing each party's expectations and guarantees.
An Eiffel contract is similar to a real-life contract between two people or two companies, which it is convenient to express in the form of tables listing the expectations and guarantees. Here for example is how we could sketch the contract between a homeowner and the telephone company:
+
+
{| border="1"
|-
| style="width=10%" |provide telephone service
@@ -28,6 +30,7 @@ Provide telephone service.
No need to provide anything if bill not paid.
|}
+
Note how the obligation for each of the parties maps onto a benefit for the other. This will be a general pattern.
The client's obligation, which protects the supplier, is called a '''precondition'''. It states what the client must satisfy before requesting a certain service. The client's benefit, which describes what the supplier must do (assuming the precondition was satisfied), is called a '''postcondition'''.
@@ -36,50 +39,50 @@ In addition to preconditions and postconditions, contract clauses include '''cla
==Expressing assertions==
-Eiffel provides syntax for expressing preconditions ( require), postconditions ( ensure) and class invariants ( invariant), as well as other assertion constructs studied later (see [[10 Other Mechanisms|"Instructions", page 84]] ): loop invariants and variants, check instructions.
+Eiffel provides syntax for expressing preconditions (require), postconditions (ensure) and class invariants (invariant), as well as other assertion constructs studied later (see [[10 Other Mechanisms#Instructions|"Instructions"]] ): loop invariants and variants, check instructions.
Here is a partial update of class ACCOUNT with more assertions:
indexing
- description: "Simple bank accounts"
+ description: "Simple bank accounts"
class
- ACCOUNT
+ ACCOUNT
feature -- Access
- balance: INTEGER
- -- Current balance
+ balance: INTEGER
+ -- Current balance
- deposit_count: INTEGER is
- -- Number of deposits made since opening
- do
- ... As before ...
- end
+ deposit_count: INTEGER
+ -- Number of deposits made since opening
+ do
+ ... As before ...
+ end
feature -- Element change
- deposit (sum: INTEGER) is
- -- Add `sum' to account.
- require
- non_negative: sum >= 0
- do
- ... As before ...
- ensure
- one_more_deposit: deposit_count = old deposit_count + 1
- updated: balance = old balance + sum
- end
+ deposit (sum: INTEGER)
+ -- Add `sum' to account.
+ require
+ non_negative: sum >= 0
+ do
+ ... As before ...
+ ensure
+ one_more_deposit: deposit_count = old deposit_count + 1
+ updated: balance = old balance + sum
+ end
feature {NONE} -- Implementation
- all_deposits: DEPOSIT_LIST
- -- List of deposits since account's opening.
+ all_deposits: DEPOSIT_LIST
+ -- List of deposits since account's opening.
invariant
- consistent_balance: (all_deposits /= Void) implies
- (balance = all_deposits . total)
- zero_if_no_deposits: (all_deposits = Void) implies
- (balance = 0)
+ consistent_balance: (all_deposits /= Void) implies
+ (balance = all_deposits . total)
+ zero_if_no_deposits: (all_deposits = Void) implies
+ (balance = 0)
end -- class ACCOUNT
@@ -93,6 +96,8 @@ The precondition of a routine expresses conditions that the routine is imposing
The postcondition of a routine expresses what the routine guaranteed to its clients for calls satisfying the precondition. The notation old expression, valid in postconditions ( ensure clauses) only, denotes the value that expression had on entry to the routine.
The precondition and postcondition state the terms of the contract between the routine and its clients, similar to the earlier example of a human contract:
+
+
{| border="1"
|-
| style="width=10%" |deposit
@@ -112,18 +117,21 @@ Update deposits list and balance.
No need to handle negative arguments.
|}
+
The class invariant, as noted, applies to all features. It must be satisfied on exit by any creation procedure, and is implicitly added to both the precondition and postcondition of every exported routine. In this respect it is both good news and bad news for the routine implementer: good news because it guarantees that the object will initially be in a stable state, averting the need in the example to check that the total of all_deposits is compatible with the balance; bad news because, in addition to its official contract as expressed by its specific postcondition, every routine must take care of restoring the invariant on exit.
A requirement on meaningful contracts is that they should be in good faith: satisfiable by an honest partner. This implies a consistency rule: if a routine is exported to a client (either generally or selectively), any feature appearing in its precondition must also be available to that client. Otherwise -- for example if the precondition included require n > 0, where n is a secret attribute -- the supplier would be making demands that a good-faith client cannot possibly check for.
Note in this respect that guaranteeing a precondition does not necessarily mean, for the client, testing for it. Assuming n is exported, a call may test for the precondition
-if x.n > 0 then x.r end
+ if x.n > 0 then
+ x.r
+ end
possibly with an else part. But if the context of the call, in the client's code, implies that n is positive -- perhaps because some preceding call set it to the sum of two squares -- then there is no need for an if or similar construct.
-{{note|In such a case, a check instruction as introduced later ( [[10 Other Mechanisms|"Instructions", page 84]] ) is recommended if the reason for omitting the test is non-trivial. }}
+{{note|In such a case, a check instruction as introduced later ( [[10 Other Mechanisms#Instructions|"Instructions"]] ) is recommended if the reason for omitting the test is non-trivial. }}
==Using contracts for built-in reliability==
@@ -137,28 +145,27 @@ Contracts in Eiffel are not just wishful thinking. They can be monitored at run
It should be clear from the preceding discussion that contracts are not a mechanism to test for special conditions, for example erroneous user input. For that purpose, the usual control structures ( if deposit_sum > 0 then ...) are available, complemented in applicable cases by the exception handling mechanism reviewed next. An assertion is instead a '''correctness condition''' governing the relationship between two software modules (not a software module and a human, or a software module and an external device). If sum is negative on entry to deposit, violating the precondition, the culprit is some other software element, whose author was not careful enough to observe the terms of the deal. Bluntly:
-{{note| '''Assertion Violation rule '''A run-time assertion violation is the manifestation of a bug. }}
+{{rule|name=Assertion Violation|text=A run-time assertion violation is the manifestation of a bug. }}
To be more precise:
* A precondition violation signals a bug in the client, which did not observe its part of the deal.
* A postcondition (or invariant) violation signals a bug in the supplier -- the routine -- which did not do its job.
-
That violations indicate bugs explains why it is legitimate to enable or disable assertion monitoring through mere compilation options: for a correct system -- one without bugs -- assertions will always hold, so the compilation option makes no difference to the semantics of the system.
But of course for an incorrect system the best way to find out where the bug is -- or just that there is a bug -- is often to monitor the assertions during development and testing. Hence the presence of the compilation options, which EiffelStudio lets you set separately for each class, with defaults at the system and cluster levels:
* no : assertions have no run-time effect.
* require : monitor preconditions only, on routine entry.
* ensure : preconditions on entry, postconditions on exit.
-* invariant : like ensure, plus class invariant on both entry and exit for qualified calls.
-* all : like invariant, plus check instructions, loop invariants and loop variants ( [[10 Other Mechanisms|"Exception handling", page 46]] ).
+* invariant : same as ensure, plus class invariant on both entry and exit for qualified calls.
+* all : same as invariant, plus check instructions, loop invariants and loop variants.
-An assertion violation, if detected at run time under one of these options other than the first, will cause an exception ( [[8 Design by Contract (tm), Assertions and Exceptions|"Instructions", page 84]] ). Unless the software has an explicit "retry" plan as explained in the discussion of exceptions, the violation will cause produce an exception trace and cause termination (or, in EiffelStudio, a return to the environment's browsing and debugging facilities at the point of failure). If present, the label of the violated sub clause will be displayed, to help identify the problem.
+An assertion violation, if detected at run time under one of these options other than the first, will cause an exception ( [[8 Design by Contract (tm), Assertions and Exceptions#exception_handling|"Exception handling"]] ). Unless the software has an explicit "retry" plan as explained in the discussion of exceptions, the violation will cause produce an exception trace and cause termination (or, in EiffelStudio, a return to the environment's browsing and debugging facilities at the point of failure). If present, the label of the violated sub clause will be displayed, to help identify the problem.
The default is require. This is particularly interesting in connection with the Eiffel method's insistence on reuse: with libraries such as EiffelBase, richly equipped with preconditions expressing terms of use, an error in the '''client software''' will often lead, for example through an incorrect argument, to violating one of these preconditions. A somewhat paradoxical consequence is that even an application developer who does not apply the method too well (out of carelessness, haste, indifference or ignorance) will still benefit from the presence of contracts in someone else's library code.
-During development and testing, assertion monitoring should be turned on at the highest possible level. Combined with static typing and the immediate feedback of compilation techniques such as the Melting Ice Technology, this permits the development process mentioned in the section [[3 The Software Process in Eiffel|"Quality and functionality", page 10]] , where errors are exterminated at birth. No one who has not practiced the method in a real project can imagine how many mistakes are found in this way; surprisingly often, a violation will turn out to affect an assertion that was just included for goodness' sake, the developer being convinced that it could never "possibly" fail to be satisfied.
+During development and testing, assertion monitoring should be turned on at the highest possible level. Combined with static typing and the immediate feedback of compilation techniques such as the Melting Ice Technology, this permits the development process mentioned in the section [[3 The Software Process in Eiffel#Quality_and_functionality|"Quality and functionality"]] , where errors are exterminated at birth. No one who has not practiced the method in a real project can imagine how many mistakes are found in this way; surprisingly often, a violation will turn out to affect an assertion that was just included for goodness' sake, the developer being convinced that it could never "possibly" fail to be satisfied.
By providing a precise reference (the description of what the software is supposed to do) against which to assess the reality (what the software actually does), Design by Contract profoundly transforms the activities of debugging, testing and quality assurance.
@@ -169,31 +176,31 @@ When releasing the final version of a system, it is usually appropriate to turn
Another application of assertions governs documentation. Environment mechanisms, such as clicking the Form Contract icon in Eiffelstudio, will produce, from a class text, an abstracted version which only includes the information relevant for client authors. Here is the contract form of class ACCOUNT in the latest version given:
indexing
- description: "Simple bank accounts"
+ description: "Simple bank accounts"
class interface
- ACCOUNT
+ ACCOUNT
feature -- Access
- balance: INTEGER
- -- Current balance
+ balance: INTEGER
+ -- Current balance
- deposit_count: INTEGER
- -- Number of deposits made since opening
+ deposit_count: INTEGER
+ -- Number of deposits made since opening
feature -- Element change
- deposit (sum: INTEGER)
- -- Add `sum' to account.
- require
- non_negative: sum >= 0
- ensure
- one_more_deposit: deposit_count = old deposit_count + 1
- updated: balance = old balance + sum
+ deposit (sum: INTEGER)
+ -- Add `sum' to account.
+ require
+ non_negative: sum >= 0
+ ensure
+ one_more_deposit: deposit_count = old deposit_count + 1
+ updated: balance = old balance + sum
invariant
- consistent_balance: balance = all_deposits.total
+ consistent_balance: balance = all_deposits.total
end -- class interface ACCOUNT
@@ -202,25 +209,25 @@ The words interface class are used instead of just class
* Names and signatures (argument and result type information) for exported features.
-* Header comments of these features, which carry informal descriptions of their purpose. (Hence the importance, mentioned in section [[4 Hello World|4]] , of always including such comments and writing them carefully.)
+* Header comments of these features, which carry informal descriptions of their purpose. (Hence the importance, mentioned in section [[4 Hello World|"Hello World"]], of always including such comments and writing them carefully.)
* Preconditions and postconditions of these features (at least the subclauses involving only exported features).
* Class invariant (same observation).
-The following elements, however, are not in the Contract Form: any information about non-exported features; all the routine bodies ( do clauses, or the external and once variants seen in [[5 The Static Picture: System Organization|"External software", page 16]] above and [[10 Other Mechanisms|"Once routines and shared objects", page 82]] below); assertion subclauses involving non-exported features; and some keywords not useful in the documentation, such as is for a routine.
+The following elements, however, are not in the Contract Form: any information about non-exported features; all the routine bodies (do clauses, or the external and once variants seen in [[5 The Static Picture: System Organization#External_software|"External software"]] above and [[10 Other Mechanisms#Once_routines_and_shared_objects|"Once routines and shared objects"]] below); assertion subclauses involving non-exported features; and some keywords not useful in the documentation.
-In accordance with the Uniform Access principle (page [[6 The Dynamic Structure: Execution Model|19]] ), the Contract Form does not distinguish between attributes and argument-less queries. In the above example, balance could be one or the other, as it makes no difference to clients, except possibly for performance.
+In accordance with the Uniform Access principle (described in [[6 The Dynamic Structure: Execution Model|"Objects, fields, values, and references"]] ), the Contract Form does not distinguish between attributes and argument-less queries. In the above example, balance could be one or the other, as it makes no difference to clients, except possibly for performance.
The Contract Form is the fundamental tool for using supplier classes in the Eiffel method. It enables client authors to reuse software elements without having to read their source code. This is a crucial requirement in large-scale industrial developments.
The Contract Form satisfies two key requirements of good software documentation:
* It is truly abstract, free from the implementation details of what it describes and concentrating instead on its functionality.
-* Rather than being developed separately -- an unrealistic requirement, hard to impose on developers initially and becoming impossible in practice if we expect the documentation to remain up to date as the software evolves -- the documentation is extracted from the software itself. It is not a separate product but a different view of the same product. This prolongs the '''Single Product''' principle that lies at the basis of Eiffel's seamless development model (section [[3 The Software Process in Eiffel|3]] ).
+* Rather than being developed separately -- an unrealistic requirement, hard to impose on developers initially and becoming impossible in practice if we expect the documentation to remain up to date as the software evolves -- the documentation is extracted from the software itself. It is not a separate product but a different view of the same product. This prolongs the '''Single Product''' principle that lies at the basis of Eiffel's seamless development model (shown in [[3 The Software Process in Eiffel|"The Software Process in Eiffel"]] ).
The Contract Form is only one of the relevant views. EiffelStudio, for example, generates graphical representations of system structures, to show classes and their relations -- client, inheritance -- according to the conventions of BON (the Business Object Notation). In accordance with the principles of seamlessness and reversibility, EiffelStudio lets you both work on the text, producing the graphics on the fly, or work on the graphics, updating the text on the fly; you can alternate as you wish between these two modes. The resulting process is quite different from more traditional approaches based on separate tools: an analysis and CASE workbench, often based on UML, to deal with an initial "bubble-and-arrow" description; and a separate programming environment, to deal with implementation aspects only. In Eiffel the environment provides consistent, seamless support from beginning to end.
-The Contract Form -- or its variant the Flat-Contract Form, which takes account of inheritance ( [[9 Inheritance|"Flat and Flat-Contract Forms", page 72]] ) are the standard form of library documentation, used extensively, for example, in the book [http://www.eiffel.com/doc/page.html Reusable Software] (see bibliography). Assertions play a central role in such documentation by expressing the terms of the contract. As demonstrated a contrario by the widely publicized $500-million crash of the Ariane-5 rocket launcher in June of 1996, due to the incorrect reuse of a software module from the Ariane-4 project, '''reuse without a contract documentation''' is the path to disaster. Non-reuse would, in fact, be preferable.
+The Contract Form -- or its variant the Flat-Contract Form, which takes account of inheritance ( [[9 Inheritance#Flat_and_Flat-Contract_Forms|"Flat and Flat-Contract Forms"]] ) are the standard form of library documentation, used extensively, for example, in the book [http://www.eiffel.com/doc/page.html Reusable Software] (see bibliography). Assertions play a central role in such documentation by expressing the terms of the contract. As demonstrated a contrario by the widely publicized $500-million crash of the Ariane-5 rocket launcher in June of 1996, due to the incorrect reuse of a software module from the Ariane-4 project, '''reuse without a contract documentation''' is the path to disaster. Non-reuse would, in fact, be preferable.
==Exception handling==
@@ -232,27 +239,28 @@ Another application of Design by Contract governs the handling of unexpect
* As a result the routine may fail too -- causing an exception in its own caller.
+
Note the precise definitions of the two key concepts, failure and exception. Although failure is the more basic one -- since it is defined for atomic, non-routine operations -- the definitions are mutually recursive, since an exception may cause a failure of the recipient routine, and a routine's failure causes an exception in its own caller.
Why state that an exception "may" cause a failure? It is indeed possible to "rescue" a routine from failure in the case of an exception, by equipping it with a clause labeled rescue, as in:
-read_next_character (f: FILE) is
- -- Make next character available in last_character.
- -- If impossible, set failed to True.
- require
- readable: file.readable
- local
- impossible: BOOLEAN
- do
- if impossible then
- failed := True
- else
- last_character := low_level_read_function (f)
- end
- rescue
- impossible := True
- retry
- end
+ read_next_character (f: FILE)
+ -- Make next character available in last_character.
+ -- If impossible, set failed to True.
+ require
+ readable: file.readable
+ local
+ impossible: BOOLEAN
+ do
+ if impossible then
+ failed := True
+ else
+ last_character := low_level_read_function (f)
+ end
+ rescue
+ impossible := True
+ retry
+ end
This example includes the only two constructs needed for exception handling: rescue and retry. A retry instruction is only permitted in a rescue clause; its effect is to start again the execution of the routine, without repeating the initialization of local entities (such as impossible in the example, which was initialized to False on first entry). Features failed and last_character are assumed to be attributes of the enclosing class.
@@ -261,42 +269,42 @@ This example is typical of the use of exceptions: as a last resort, for situatio
A variant would be
-local
- attempts: INTEGER
-do
- if attempts < Max_attempts then
- last_character := low_level_read_function (f)
- else
- failed := True
- end
-rescue
- attempts := attempts + 1
- retry
-end
+ local
+ attempts: INTEGER
+ do
+ if attempts < Max_attempts then
+ last_character := low_level_read_function (f)
+ else
+ failed := True
+ end
+ rescue
+ attempts := attempts + 1
+ retry
+ end
which would try again up to Max_attempts times before giving up.
The above routine, in either variant, never fails: it always fulfills its contract, which states that it should either read a character or set failed to record its inability to do so. In contrast, consider the new variant
-local
- attempts: INTEGER
-do
- last_character := low_level_read_function (f)
-rescue
- attempts := attempts + 1
- if attempts < Max_attempts then
- retry
- end
-end
+ local
+ attempts: INTEGER
+ do
+ last_character := low_level_read_function (f)
+ rescue
+ attempts := attempts + 1
+ if attempts < Max_attempts then
+ retry
+ end
+ end
with no more role for failed. In this case, after Max_attempts unsuccessful attempts, the routine will execute its rescue clause to the end, with no retry (the if having no else clause). This is how a routine '''fails'''. It will, as noted, pass on the exception to its caller.
Such a rescue clause should, before terminating, restore the invariant of the class so that the caller and possible subsequent retryattempts from higher up find the objects in a consistent state. As a result, the rule for an absent rescue clause -- the case for the vast majority of routines in most systems -- is that it is equivalent to
-rescue
- default_rescue
+ rescue
+ default_rescue
where procedure default_rescue comes from ANY, where it is defined to do nothing; in a system built for robustness, classes subject to non-explicitly-rescued exceptions should redefine default_rescue (perhaps using a creation procedure, which is bound by the same formal requirement) so that it will always restore the invariant.
@@ -311,13 +319,13 @@ Concretely, exceptions may result from the following events:
* Operating system signal:arithmetic overfolow; no memory available for a requested creation or clone -- even after garbage collection has rummaged everything to find some space. (But no C/C++-like "wrong pointer address", which cannot occur thanks to the statically typed nature of Eiffel.)
-It is sometimes useful, when handling exceptions in rescue clauses, to ascertain the exact nature of the exception that got the execution there. For this it is suffices to inherit from the Kernel Library class EXCEPTIONS, which provides queries such as exception, giving the code for the last exception, and symbolic names ( [[10 Other Mechanisms|"Constant and unique attributes", page 83]] ) for all such codes, such as No_more_memory. You can then process different exceptions differently by testing exception against various possibilities. The method strongly suggests, however, that exception handling code should remain simple; a complicated algorithm in a rescue clause is usually a sign that the mechanism is being misused. Class EXCEPTIONS also provides various facilities for fine-tuning the exception facilities, such as a procedure raise that will explicitly trigger a "developer exception" with a code than can then be detected and processed. Exception handling helps produce Eiffel software that is not just correct but robust, by planning for cases that should not normally arise, but might out of Murphy's law, and ensuring they do not affect the software's basic safety and simplicity.
+It is sometimes useful, when handling exceptions in rescue clauses, to ascertain the exact nature of the exception that got the execution there. For this it is suffices to inherit from the Kernel Library class EXCEPTIONS, which provides queries such as exception, giving the code for the last exception, and symbolic names ( [[10 Other Mechanisms#Constant_and_unique_attributes|"Constant and unique attributes"]] ) for all such codes, such as No_more_memory. You can then process different exceptions differently by testing exception against various possibilities. The method strongly suggests, however, that exception handling code should remain simple; a complicated algorithm in a rescue clause is usually a sign that the mechanism is being misused. Class EXCEPTIONS also provides various facilities for fine-tuning the exception facilities, such as a procedure raise that will explicitly trigger a "developer exception" with a code than can then be detected and processed. Exception handling helps produce Eiffel software that is not just correct but robust, by planning for cases that should not normally arise, but might out of Murphy's law, and ensuring they do not affect the software's basic safety and simplicity.
==Other applications of Design by Contract==
The Design by Contract ideas pervade the Eiffel method. In addition to the applications just mentioned, they have two particularly important consequences:
-* They make it possible to use Eiffel for analysis and design. At a high level of abstraction, it is necessary to be precise too. With the exception of BON, object-oriented analysis and design methods tend to favor abstraction over precision. Thanks to assertions, it is possible to express precise properties of a system ("At what speed should the alarm start sounding?") without making any commitment to implementation. The discussion of deferred classes ( [[9 Inheritance|"Applications of deferred classes", page 60]] ) will show how to write a purely descriptive, non-software model in Eiffel, using contracts to describe the essential properties of a system without any computer or software aspect.
-* Assertions also serve to control the power of inheritance-related mechanisms -- redeclaration, polymorphism, dynamic binding -- and channel them to correct uses by assigning the proper semantic limits. See [[9 Inheritance|"Inheritance and contracts", page 66]] .
+* They make it possible to use Eiffel for analysis and design. At a high level of abstraction, it is necessary to be precise too. With the exception of BON, object-oriented analysis and design methods tend to favor abstraction over precision. Thanks to assertions, it is possible to express precise properties of a system ("At what speed should the alarm start sounding?") without making any commitment to implementation. The discussion of deferred classes ( [[9 Inheritance#Applications_of_deferred_classes|"Applications of deferred classes"]] ) will show how to write a purely descriptive, non-software model in Eiffel, using contracts to describe the essential properties of a system without any computer or software aspect.
+* Assertions also serve to control the power of inheritance-related mechanisms -- redeclaration, polymorphism, dynamic binding -- and channel them to correct uses by assigning the proper semantic limits. See [[9 Inheritance#Inheritance_and_contracts|"Inheritance and contracts"]] .
diff --git a/documentation/current/method/eiffel-tutorial-et/et-inheritance.wiki b/documentation/current/method/eiffel-tutorial-et/et-inheritance.wiki
index da7ca51a..851a1ea3 100644
--- a/documentation/current/method/eiffel-tutorial-et/et-inheritance.wiki
+++ b/documentation/current/method/eiffel-tutorial-et/et-inheritance.wiki
@@ -6,234 +6,260 @@ Inheritance is a powerful and attractive technique. A look at either the practic
==Basic inheritance structure==
-To make a class inherit from another, simply use an inherit clause:
+To make a class inherit from another, simply use an inherit clause:
indexing ...
class
- D
+ D
inherit
- A
- B
- ...
+ A
+ B
+ ...
feature
- ...
+ ...
-This makes D an heir of A , B and any other class listed. Eiffel supports '''multiple''' inheritance: a class may have as many parents as it needs. Later sections ( [[9 Inheritance|"Multiple inheritance and renaming", page 64]] and [[9 Inheritance|"Repeated inheritance and selection", page 73]] ) will explain how to handle possible conflicts between parent features.
+This makes D an heir of A, B and any other class listed. Eiffel supports '''multiple''' inheritance: a class may have as many parents as it needs. Later sections ( [[9 Inheritance#Multiple_inheritance_and_renaming|"Multiple inheritance and renaming"]] and [[9 Inheritance#Repeated_inheritance_and_selection|"Repeated inheritance and selection"]] ) will explain how to handle possible conflicts between parent features.
-{{note|This discussion will rely on the terminology introduced on page [[5 The Static Picture: System Organization|14]] : descendants of a class are the class itself, its heirs, the heirs of its heirs and so on. Proper descendants exclude the class itself. The reverse notions are ancestors and proper ancestors. }}
+{{note|This discussion will rely on the terminology introduced in [[5 The Static Picture: System Organization|The Static Picture: System Organization]]: descendants of a class are the class itself, its heirs, the heirs of its heirs and so on. Proper descendants exclude the class itself. The reverse notions are ancestors and proper ancestors. }}
-By default D will simply include all the original features of A , B , ..., to which it may add its own through its feature clauses if any. But the inheritance mechanism is more flexible, allowing D to adapt the inherited features in many ways. Each parent name -- A , B , ... in the example -- can be followed by a Feature Adaptation clause, with subclauses, all optional, introduced by keywords rename , export , undefine , redefine and select , enabling the author of A to make the best use of the inheritance mechanism by tuning the inherited features to the precise needs of D . This makes inheritance a principal tool in the Eiffel process, mentioned earlier, of carefully crafting each individual class, like a machine, for the benefit of its clients. The next sections review the various Feature Adaptation subclauses.
+By default D will simply include all the original features of A, B, ..., to which it may add its own through its feature clauses if any. But the inheritance mechanism is more flexible, allowing D to adapt the inherited features in many ways. Each parent name -- A, B, ... in the example -- can be followed by a Feature Adaptation clause, with subclauses, all optional, introduced by keywords rename, export, undefine, redefine and select, enabling the author of A to make the best use of the inheritance mechanism by tuning the inherited features to the precise needs of D. This makes inheritance a principal tool in the Eiffel process, mentioned earlier, of carefully crafting each individual class, like a machine, for the benefit of its clients. The next sections review the various Feature Adaptation subclauses.
==Redefinition==
The first form of feature adaptation is the ability to change the implementation of an inherited feature.
-Assume a class SAVINGS_ACCOUNT that specializes the notion of account. It is probably appropriate to define it as an heir to class ACCOUNT , to benefit from all the features of ACCOUNT still applicable to savings accounts, and to reflect the conceptual relationship between the two types: every savings account, apart from its own specific properties, also "is" an account. But we may need to produce a different effect for procedure deposit which, besides recording the deposit and updating the balance, may also need, for example, to update the interest.
+Assume a class SAVINGS_ACCOUNT that specializes the notion of account. It is probably appropriate to define it as an heir to class ACCOUNT, to benefit from all the features of ACCOUNT still applicable to savings accounts, and to reflect the conceptual relationship between the two types: every savings account, apart from its own specific properties, also "is" an account. But we may need to produce a different effect for procedure deposit which, besides recording the deposit and updating the balance, may also need, for example, to update the interest.
This example is typical of the form of reuse promoted by inheritance and crucial to effective reusability in software: the case of reuse with adaptation. Traditional forms of reuse are all-or-nothing: either you take a component exactly as it is, or you build your own. Inheritance will get us out of this "reuse or redo" dilemma by allowing us to reuse and redo. The mechanism is feature redefinition:
indexing
- description: "Savings accounts"
+ description: "Savings accounts"
class
- SAVINGS_ACCOUNT
+ SAVINGS_ACCOUNT
inherit
- ACCOUNT
- redefine
- deposit
- end
+ ACCOUNT
+ redefine
+ deposit
+ end
feature -- Element change
- deposit (sum: INTEGER) is
- -- Add sum to account.
- do
- ... New implementation (see below) ...
- end
+ deposit (sum: INTEGER)
+ -- Add sum to account.
+ do
+ ... New implementation (see below) ...
+ end
- ... Other features ...
+ ... Other features ...
end -- class SAVINGS_ACCOUNT
+
+
+
+
+
+
+
+
+
+
-Without the redefine subclause, the declaration of deposit would be invalid, yielding two features of the same name, the inherited one and the new one. The subclause makes this valid by specifying that the new declaration will override the old one.
+Without the redefine subclause, the declaration of deposit would be invalid, yielding two features of the same name, the inherited one and the new one. The subclause makes this valid by specifying that the new declaration will override the old one.
-In a redefinition, the original version -- such as the ACCOUNT implementation of deposit in this example -- is called the precursor of the new version. It is common for a redefinition to rely on the precursor's algorithm and add some other actions; the reserved word Precursor helps achieve this goal simply. Permitted only in a routine redefinition, it denotes the parent routine being redefined. So here he body of the new deposit (called "implementation New" above) could be of the form
+In a redefinition, the original version -- such as the ACCOUNT implementation of deposit in this example -- is called the precursor of the new version. It is common for a redefinition to rely on the precursor's algorithm and add some other actions; the reserved word Precursor helps achieve this goal simply. Permitted only in a routine redefinition, it denotes the parent routine being redefined. So here he body of the new deposit (called "implementation New" above) could be of the form
-Precursor (sum)
- -- Apply ACCOUNT's version of deposit
-... Instructions to update the interest ...
+ Precursor (sum)
+ -- Apply ACCOUNT's version of deposit
+ ... Instructions to update the interest ...
-Besides changing the implementation of a routine, a redefinition can turn an argument-less function into an attribute; for example a proper descendant of ACCOUNT could redefine deposits_count , originally a function, as an attribute. The Uniform Access Principle (page [[6 The Dynamic Structure: Execution Model|19]] ) guarantees that the redefinition makes no change for clients, which will continue to use the feature under the form acc.deposits_count .
+Besides changing the implementation of a routine, a redefinition can turn an argument-less function into an attribute; for example a proper descendant of ACCOUNT could redefine deposits_count, originally a function, as an attribute. The Uniform Access Principle (introduced in [[6 The Dynamic Structure: Execution Model|The Dynamic Structure: Execution Model]] ) guarantees that the redefinition makes no change for clients, which will continue to use the feature under the form
+
+ acc.deposits_count
+
==Polymorphism==
-The inheritance mechanism is relevant to both roles of classes: module and type. Its application as a mechanism to reuse, adapt and extend features from one class to another, as just seen, covers its role as a '''module extension''' mechanism. But it's also a '''subtyping''' mechanism. To say that D is an heir of A , or more generally a descendant of A , is to expresses that instances of D can be viewed as instances of A .
+The inheritance mechanism is relevant to both roles of classes: module and type. Its application as a mechanism to reuse, adapt and extend features from one class to another, as just seen, covers its role as a '''module extension''' mechanism. But it's also a '''subtyping''' mechanism. To say that D is an heir of A, or more generally a descendant of A, is to expresses that instances of D can be viewed as instances of A.
-'''Polymorphic assignment''' supports this second role. In an assignment x := y , the types of x and y do not have, with inheritance, to be identical; the rule is that the type of y must simply '''conform''' to the type of x . A class D conforms to a class A if and only if it is a descendant of A (which includes the case in which A and D are the same class); if these classes are generic, conformance of D [ U ] to C [ T ] requires in addition that type U conform to type T (through the recursive application of the same rules).
+'''Polymorphic assignment''' supports this second role. In an assignment x := y, the types of x and y do not have, with inheritance, to be identical; the rule is that the type of y must simply '''conform''' to the type of x. A class D conforms to a class A if and only if it is a descendant of A (which includes the case in which A and D are the same class); if these classes are generic, conformance of D [ U ] to C [ T ] requires in addition that type U conform to type T (through the recursive application of the same rules).
-{{note|In addition, it will be shown in the discussion of tuples ([[10 Other Mechanisms|"Tuple types"]]), that TUPLE [X] conforms to TUPLE , TUPLE [X, Y] to TUPLE [X] and so on. }}
+{{note|In addition, it will be shown in the discussion of tuples ([[10 Other Mechanisms#Tuple_types|"Tuple types"]]), that TUPLE [X] conforms to TUPLE, TUPLE [X, Y] to TUPLE [X] and so on. }}
So with the inheritance structure that we have seen, the declarations
-acc: ACCOUNT
-sav: SAVINGS_ACCOUNT
+ acc: ACCOUNT
+ sav: SAVINGS_ACCOUNT
make it valid to write the assignment
-acc := sav
+ acc := sav
-which will assign to acc a reference attached (if not void) to a direct instance of type SAVINGS_ACCOUNT , not ACCOUNT .
+which will assign to acc a reference attached (if not void) to a direct instance of type SAVINGS_ACCOUNT, not ACCOUNT.
-Such an assignment, where the source and target types are different, is said to be polymorphic. An entity such as acc , which as a result of such assignments may become attached at run time to objects of types other than the one declared for it, is itself called a polymorphic entity.
+Such an assignment, where the source and target types are different, is said to be polymorphic. An entity such as acc, which as a result of such assignments may become attached at run time to objects of types other than the one declared for it, is itself called a polymorphic entity.
-For polymorphism to respect the reliability requirements of Eiffel, it must be controlled by the type system and enable static type checking. We certainly do not want an entity of type ACCOUNT to become attached to an object of type DEPOSIT . Hence the second typing rule:
+For polymorphism to respect the reliability requirements of Eiffel, it must be controlled by the type system and enable static type checking. We certainly do not want an entity of type ACCOUNT to become attached to an object of type DEPOSIT. Hence the second typing rule:
{{rule|name=Type Conformance|text=An assignment x := y, or the use of y as actual argument corresponding to the formal argument x in a routine call, is only valid if the type of y conforms to the the type of x. }}
-The second case listed in the rule is a call such as target.routine(..., y, ...) where the routine declaration is of the form routine (..., x: SOME_TYPE) . The relationship between y , the actual argument in the call, and the corresponding formal argument x , is exactly the same as in an assignment x := y : not just the type rule, as expressed by Type Conformance (the type of y must conform to SOME_TYPE ), but also the actual run-time effect which, as for assignments, will be either a reference attachment or, for expanded types, a copy.
+The second case listed in the rule is a call such as target.routine(..., y, ...) where the routine declaration is of the form routine (..., x: SOME_TYPE). The relationship between y, the actual argument in the call, and the corresponding formal argument x, is exactly the same as in an assignment x := y: not just the type rule, as expressed by Type Conformance (the type of y must conform to SOME_TYPE), but also the actual run-time effect which, as for assignments, will be either a reference attachment or, for expanded types, a copy.
-The ability to accept the assignment x := Void for x of any reference type ( [[6 The Dynamic Structure: Execution Model|"Basic operations", page 28]] ) is a consequence of the Type Conformance rule, since Void is of type NONE which by construction ( [[5 The Static Picture: System Organization|"The global inheritance structure", page 15]] ) conforms to all types.
+The ability to accept the assignment x := Void for x of any reference type (see [[6 The Dynamic Structure: Execution Model#Basic_operations|"Basic operations"]] ) is a consequence of the Type Conformance rule, since Void is of type NONE which by construction ([[5 The Static Picture: System Organization#The_global_inheritance_structure|"The global inheritance structure"]] ) conforms to all types.
-Polymorphism also yields a more precise definition of "instance". A '''direct instance''' of a type A is an object created from the exact pattern defined by the declaration of A 's base class, with one field for each of the class attributes; you will obtain it through a creation instruction of the form create x ..., for x of type A , or by cloning an existing direct instance. An '''instance''' of A is a direct instance of any type conforming to A : A itself, but also any type based on descendant classes. So an instance of SAVINGS_ACCOUNT is also an instance, although not a direct instance, of ACCOUNT .
+Polymorphism also yields a more precise definition of "instance". A '''direct instance''' of a type A is an object created from the exact pattern defined by the declaration of A 's base class, with one field for each of the class attributes; you will obtain it through a creation instruction of the form create x ..., for x of type A, or by cloning an existing direct instance. An '''instance''' of A is a direct instance of any type conforming to A: A itself, but also any type based on descendant classes. So an instance of SAVINGS_ACCOUNT is also an instance, although not a direct instance, of ACCOUNT.
A consequence of polymorphism is the ability to define '''polymorphic data structures'''. With a declaration such as
-accounts: LIST [ACCOUNT]
+ accounts: LIST [ACCOUNT]
-the procedure call accounts.extend ( acc ), because it uses a procedure extend which in this case expects an argument of any type conforming to ACCOUNT , will be valid not only if acc is of type ACCOUNT but also if it is of a descendant type such as SAVINGS_ACCOUNT . Successive calls of this kind make it possible to construct a data structure that, at run-time, might contain objects of several types, all conforming to ACCOUNT :
- [[Image:tutorial-10]] [[Image:tutorial-11]]
-Such polymorphic data structures combine the flexibility and safety of genericity and inheritance. You can make them more or less general by choosing for the actual generic parameter, here ACCOUNT , a type higher or lower in the inheritance hierarchy. Static typing is again essential here, prohibiting for example a mistaken insertion of the form accounts.extend (dep) where dep is of type DEPOSIT , which does not conform to ACCOUNT .
+the procedure call accounts.extend ( acc), because it uses a procedure extendwhich in this case expects an argument of any type conforming to ACCOUNT, will be valid not only if acc is of type ACCOUNT but also if it is of a descendant type such as SAVINGS_ACCOUNT. Successive calls of this kind make it possible to construct a data structure that, at run-time, might contain objects of several types, all conforming to ACCOUNT:
-At the higher (most abstract) end of the spectrum, you can produce an unrestrictedly polymorphic data structure general_list: LIST [ANY] which makes the call general_list.extend (x) valid for any x . The price to pay is that retrieving an element from such a structure will yield an object on which the only known applicable operations are the most general ones, valid for all types: assignment, copy, clone, equality comparison and others from ANY . Assignment attempt, studied below, will make it possible to apply more specific operations after checking dynamically that a retrieved object is of the appropriate type.
+[[Image:tutorial-10]] [[Image:tutorial-11]]
+
+Such polymorphic data structures combine the flexibility and safety of genericity and inheritance. You can make them more or less general by choosing for the actual generic parameter, here ACCOUNT, a type higher or lower in the inheritance hierarchy. Static typing is again essential here, prohibiting for example a mistaken insertion of the form accounts.extend (dep) where depis of type DEPOSIT, which does not conform to ACCOUNT.
+
+At the higher (most abstract) end of the spectrum, you can produce an unrestrictedly polymorphic data structure general_list: LIST [ANY] which makes the call general_list.extend (x) valid for any x. The price to pay is that retrieving an element from such a structure will yield an object on which the only known applicable operations are the most general ones, valid for all types: assignment, copy, twin, equality comparison and others from ANY. Assignment attempt, studied below, will make it possible to apply more specific operations after checking dynamically that a retrieved object is of the appropriate type.
==Dynamic binding==
The complement of polymorphism is dynamic binding, the answer to the question "What version of a feature will be applied in a call whose target is polymorphic?".
-Consider acc is of type ACCOUNT . Thanks to polymorphism, an object attached to acc may be a direct instance not just of ACCOUNT but also of SAVINGS_ACCOUNT or other descendants. Some of these descendants, indeed SAVINGS_ACCOUNT among them, redefine features such as deposit . Then we have to ask what the effect will be for a call of the form
+Consider acc is of type ACCOUNT. Thanks to polymorphism, an object attached to acc may be a direct instance not just of ACCOUNT but also of SAVINGS_ACCOUNT or other descendants. Some of these descendants, indeed SAVINGS_ACCOUNT among them, redefine features such as deposit. Then we have to ask what the effect will be for a call of the form
-acc.deposit (some_value)
+ acc.deposit (some_value)
-Dynamic binding is the clearly correct answer: the call will execute the version of deposit from the generating class of the object attached to acc at run time. If acc is attached to a direct instance of ACCOUNT , execution will use the original ACCOUNT version; if acc is attached to a direct instance of SAVINGS_ACCOUNT , the call will execute the version redefined in that class.
+Dynamic binding is the clearly correct answer: the call will execute the version of deposit from the generating class of the object attached to acc at run time. If acc is attached to a direct instance of ACCOUNT, execution will use the original ACCOUNT version; if acc is attached to a direct instance of SAVINGS_ACCOUNT, the call will execute the version redefined in that class.
-This is a clear correctness requirement. A policy of static binding (as available for example in C++ or Delphi, for non-virtual functions) would take the declaration of acc as an ACCOUNT literally. But that declaration is only meant to ensure generality, to enable the use of a single entity acc in many different cases: what counts at execution time is the object that acc represents. Applying the ACCOUNT version to a SAVINGS_ACCOUNT object would be wrong, possibly leading in particular to objects that violate the invariant of their own generating class (since there is no reason a routine of ACCOUNT will preserve the specific invariant of a proper descendant such as SAVINGS_ACCOUNT , which it does not even know about).
+This is a clear correctness requirement. A policy of static binding (as available for example in C++ or Delphi, for non-virtual functions) would take the declaration of acc as an ACCOUNT literally. But that declaration is only meant to ensure generality, to enable the use of a single entity acc in many different cases: what counts at execution time is the object that acc represents. Applying the ACCOUNT version to a SAVINGS_ACCOUNT object would be wrong, possibly leading in particular to objects that violate the invariant of their own generating class (since there is no reason a routine of ACCOUNT will preserve the specific invariant of a proper descendant such as SAVINGS_ACCOUNT, which it does not even know about).
In some cases, the choice between static and dynamic binding does not matter: this is the case for example if a call's target is not polymorphic, or if the feature of the call is redefined nowhere in the system. In such cases the use of static binding permits slightly faster calls (since the feature is known at compile time). This application of static binding should, however, be treated as a '''compiler optimization'''. The EiffelStudio compiler, under its "finalization" mode, which performs extensive optimization, will detect some of these cases and process them accordingly -- unlike approaches that make developers responsible for specifying what should be static and what dynamic (a tedious and error-prone task, especially delicate because a minute change in the software can make a static call, in a far-away module of a large system, suddenly become dynamic). Eiffel programmers don't need to worry about such aspects; they can rely on the semantics of dynamic binding in all cases, with the knowledge that the compiler will apply static binding when safe and desirable.
Even in cases that require dynamic binding, the design of Eiffel, in particular the typing rules, enable compilers to make the penalty over the static-binding calls of traditional approaches very small and, most importantly, '''constant-bounded''' : it does not grow with the depth or complexity of the inheritance structure. The discovery in 1985 of a technique for constant-time dynamic binding calls, even in the presence of multiple and repeated inheritance, was the event that gave the green light to the development of Eiffel.
-Dynamic binding is particularly interesting for polymorphic data structures. If you iterate over the list of accounts of various kinds, accounts: LIST [ACCOUNT] , illustrated in the last figure, and at each step let acc represent the current list element, you can repeatedly apply
+Dynamic binding is particularly interesting for polymorphic data structures. If you iterate over the list of accounts of various kinds, accounts: LIST [ACCOUNT], illustrated in the last figure, and at each step let acc represent the current list element, you can repeatedly apply
-acc.deposit (...)
+ acc.deposit (...)
-to have the appropriate variant of the deposit operation triggered for each element.
+to have the appropriate variant of the deposit operation triggered for each element.
-The benefit of such techniques appears clearly if we compare them with the traditional way to address such needs: using multi-branch discriminating instructions of the form if "Account is a savings account " then ... elseif "It is a money market account" then ... and so on, or the corresponding case ... of ..., switch or inspect instructions. Apart from their heaviness and complexity, such solutions cause many components of a software system to rely on the knowledge of the exact set of variants available for a certain notion, such as bank account. Then any addition, change or removal of variants can cause a ripple of changes throughout the architecture. This is one of the majors obstacles to extendibility and reusability in traditional approaches. In contrast, using the combination of inheritance, redefinition, polymorphism and dynamic binding makes it possible to have a '''point of single choice''' -- a unique location in the system which knows the exhaustive list of variants. Every client then manipulates entities of the most general type, ACCOUNT , through dynamically bound calls of the form acc.some_account_feature (...) .
+The benefit of such techniques appears clearly if we compare them with the traditional way to address such needs: using multi-branch discriminating instructions of the form
+
+ if "Account is a savings account " then
+ ...
+ elseif "It is a money market account" then
+ ...
+ elseif ...
+
+and so on, or the corresponding case ... of ..., switch or inspect instructions. Apart from their heaviness and complexity, such solutions cause many components of a software system to rely on the knowledge of the exact set of variants available for a certain notion, such as bank account. Then any addition, change or removal of variants can cause a ripple of changes throughout the architecture. This is one of the majors obstacles to extendibility and reusability in traditional approaches. In contrast, using the combination of inheritance, redefinition, polymorphism and dynamic binding makes it possible to have a '''point of single choice''' -- a unique location in the system which knows the exhaustive list of variants. Every client then manipulates entities of the most general type, ACCOUNT, through dynamically bound calls of the form
+
+ acc.some_account_feature (...)
+
These observations make dynamic binding appear for what it is: not an implementation mechanism, but an '''architectural technique''' that plays a key role (along with information hiding, which it extends, and Design by Contract, to which it is linked through the assertion redefinition rules seen below) in providing the modular system architectures of Eiffel, the basis for the method's approach to reusability and extendibility. These properties apply as early as analysis and modeling, and continue to be useful throughout the subsequent steps.
==Deferred features and classes==
-The examples of dynamic binding seen so far assumed that all classes were fully implemented, and dynamically bound features had a version in every relevant class, including the most general ones such as ACCOUNT .
+The examples of dynamic binding seen so far assumed that all classes were fully implemented, and dynamically bound features had a version in every relevant class, including the most general ones such as ACCOUNT.
-It is also useful to define classes that leave the implementation of some of their features entirely to proper descendants. Such an abstract class is known as deferred ; so are its unimplemented features. The reverse of deferred is effective, meaning fully implemented.
+It is also useful to define classes that leave the implementation of some of their features entirely to proper descendants. Such an abstract class is known as deferred; so are its unimplemented features. The reverse of deferred is effective, meaning fully implemented.
- LIST is a typical example of deferred class. As it describes the general notion of list, it should not favor any particular implementation; that will be the task of its effective descendants, such as LINKED_LIST (linked implementation), TWO_WAY_LIST (linked both ways) ARRAYED_LIST, (implementation by an array), all effective, and all indeed to be found in EiffelBase.
+LIST is a typical example of deferred class. As it describes the general notion of list, it should not favor any particular implementation; that will be the task of its effective descendants, such as LINKED_LIST (linked implementation), TWO_WAY_LIST (linked both ways) ARRAYED_LIST, (implementation by an array), all effective, and all indeed to be found in EiffelBase.
-At the level of the deferred class LIST , some features such as extend (add an item at the end of the list) will have no implementation and hence will be declared as deferred. Here is the corresponding form, illustrating the syntax for both deferred classes and their deferred features:
+At the level of the deferred class LIST, some features such as extend (add an item at the end of the list) will have no implementation and hence will be declared as deferred. Here is the corresponding form, illustrating the syntax for both deferred classes and their deferred features:
indexing
- description: "[
- Sequential finite lists, without a commitment
- to a representation."
+ description: "[
+ Sequential finite lists, without a commitment
+ to a representation."
deferred class
- LIST [G]
+ LIST [G]
feature -- Access
- count: INTEGER is
- -- Number of items in list
- do
- ... See below; this feature can be effective ...
- end
+ count: INTEGER
+ -- Number of items in list
+ do
+ ... See below; this feature can be effective ...
+ end
feature -- Element change
- extend (x: G) is
- -- Add `x' at end of list.
- require
- space_available: not full
- deferred
- ensure
- one_more: count = old count + 1
- end
+ extend (x: G)
+ -- Add `x' at end of list.
+ require
+ space_available: not full
+ deferred
+ ensure
+ one_more: count = old count + 1
+ end
- ... Other feature declarations and invariants ...
+ ... Other feature declarations and invariants ...
end -- class LIST
-A deferred feature (considered to be a routine, although it can yield an attribute in a proper descendant) has the single keyword deferred in lieu of the do ''Instructions'' clause of an effective routine. A deferred class -- defined as a class that has at least one deferred feature -- must be introduced by class deferred instead of just class .
+A deferred feature (considered to be a routine, although it can yield an attribute in a proper descendant) has the single keyword deferred in lieu of the do ''Instructions'' clause of an effective routine. A deferred class -- defined as a class that has at least one deferred feature -- must be introduced by class deferred instead of just class.
-As the example of extend shows, a deferred feature, although it has no implementation, can be equipped with assertions. They will be binding on implementations in descendants, in a way to be explained below.
+As the example of extend shows, a deferred feature, although it has no implementation, can be equipped with assertions. They will be binding on implementations in descendants, in a way to be explained below.
-Deferred classes do not have to be fully deferred. They may contain some effective features along with their deferred ones. Here, for example, we may express count as a function:
+Deferred classes do not have to be fully deferred. They may contain some effective features along with their deferred ones. Here, for example, we may express count as a function:
-count: INTEGER is
- -- Number of items in list
- do
- from
- start
- until
- after
- loop
- Result := Result + 1
- forth
- end
- end
+ count: INTEGER
+ -- Number of items in list
+ do
+ from
+ start
+ until
+ after
+ loop
+ Result := Result + 1
+ forth
+ end
+ end
-This implementation relies on the loop construct described below ( from introduces the loop initialization) and on a set of deferred features of the class which allow traversal of a list based on moving a fictitious cursor: start to bring the cursor to the first element if any, after to find out whether all relevant elements have been seen, and forth (with precondition not after ) to advance the cursor to the next element. Procedure forth itself appears as
+This implementation relies on the loop construct described below ( from introduces the loop initialization) and on a set of deferred features of the class which allow traversal of a list based on moving a fictitious cursor: start to bring the cursor to the first element if any, after to find out whether all relevant elements have been seen, and forth (with precondition not after) to advance the cursor to the next element. Procedure forth itself appears as
-forth is
- -- Advance cursor by one position
- require
- not_after: not after
- deferred
- ensure
- moved_right: index = old index + 1
- end
+ forth
+ -- Advance cursor by one position
+ require
+ not_after: not after
+ deferred
+ ensure
+ moved_right: index = old index + 1
+ end
-where index -- another deferred feature -- is the integer position of the cursor.
+where index -- another deferred feature -- is the integer position of the cursor.
-Although the above version of feature count is time-consuming -- it implies a whole traversal just for the purpose of determining the number of elements -- it has the advantage of being applicable to all variants, without any commitment to a choice of implementation, as would follow for example if we decided to treat count as an attribute. Proper descendants can always redefine count for more efficiency.
+Although the above version of feature count is time-consuming -- it implies a whole traversal just for the purpose of determining the number of elements -- it has the advantage of being applicable to all variants, without any commitment to a choice of implementation, as would follow for example if we decided to treat count as an attribute. Proper descendants can always redefine count for more efficiency.
-Function count illustrates one of the most important contributions of the method to reusability: the ability to define '''behavior classes''' that capture common behaviors (such as count) while leaving the details of the behaviors (such as start , after , forth ) open to many variants. As noted earlier, traditional approaches to reusability provide closed reusable components. A component such as LIST , although equipped with directly usable behaviors such as count, is open to many variations, to be provided by proper descendants.
+Function count illustrates one of the most important contributions of the method to reusability: the ability to define '''behavior classes''' that capture common behaviors (such as count) while leaving the details of the behaviors (such as start, after, forth) open to many variants. As noted earlier, traditional approaches to reusability provide closed reusable components. A component such as LIST, although equipped with directly usable behaviors such as count, is open to many variations, to be provided by proper descendants.
{{note|Some O-O languages support only the two extremes: fully effective classes, and fully deferred "interfaces", but not classes with a mix of effective and deferred features. This is an unacceptable limitation, negating the object-oriented method's support for a seamless, continuous spectrum from the most abstract to the most concrete. }}
-A class B inheriting from a deferred class A may provide implementations -- effective declarations -- for the features inherited in deferred form. In this case there is no need for a redefine subclause; the effective versions simply replace the inherited versions. The class is said to '''effect''' the corresponding features. If after this process there remain any deferred features, B is still considered deferred, even if it introduces no deferred features of its own, and must be declared as class deferred .
+A class B inheriting from a deferred class A may provide implementations -- effective declarations -- for the features inherited in deferred form. In this case there is no need for a redefine subclause; the effective versions simply replace the inherited versions. The class is said to '''effect''' the corresponding features. If after this process there remain any deferred features, B is still considered deferred, even if it introduces no deferred features of its own, and must be declared as class deferred.
-In the example, classes such as LINKED_LIST and ARRAYED_LIST will effect all the deferred features they inherit from LIST -- extend , start etc. -- and hence will be effective.
+In the example, classes such as LINKED_LIST and ARRAYED_LIST will effect all the deferred features they inherit from LIST -- extend, start etc. -- and hence will be effective.
-Except in some applications restricted to pure system modeling -- as discussed next -- the main benefit of deferred classes and features comes from polymorphism and dynamic binding. Because extend has no implementation in class LIST , a call of the form my_list.extend(...) with my_list of type LIST [T] for some T can only be executed if my_list is attached to a direct instance of an effective proper descendant of LIST , such as LINKED_LIST ; then it will use the corresponding version of extend . Static binding would not even make sense here.
+Except in some applications restricted to pure system modeling -- as discussed next -- the main benefit of deferred classes and features comes from polymorphism and dynamic binding. Because extend has no implementation in class LIST, a call of the form my_list.extend(...) with my_list of type LIST [T] for some T can only be executed if my_list is attached to a direct instance of an effective proper descendant of LIST, such as LINKED_LIST; then it will use the corresponding version of extend. Static binding would not even make sense here.
-Even an effective feature of LIST such as count may depend on deferred features (start and so on), so that a call of the form my_list.count can only be executed in the context of an effective descendant.
+Even an effective feature of LIST such as count may depend on deferred features (start and so on), so that a call of the form my_list.count can only be executed in the context of an effective descendant.
-All this indicates that a deferred class must have '''no direct instance'''. (It will have instances, the direct instances of its effective descendants.) If it had any, we could call deferred features on them, leading to execution-time impossibility. The rule that achieves this goal is simple: if the base type of x is a deferred class, no creation instruction of target x , of the form create x... , is permitted.
+All this indicates that a deferred class must have '''no direct instance'''. (It will have instances, the direct instances of its effective descendants.) If it had any, we could call deferred features on them, leading to execution-time impossibility. The rule that achieves this goal is simple: if the base type of x is a deferred class, no creation instruction of target x, of the form create x..., is permitted.
==Applications of deferred classes==
@@ -246,65 +272,65 @@ Deferred classes cover abstract notions with many possible variants. They are wi
These applications make deferred classes a central tool of the Eiffel method's support for seamlessness and reversibility. The last one in particular uses deferred classes and features to model objects from an application domain, without any commitment to implementation, design, or even software (and computers). Deferred classes are the ideal tool here: they express the properties of the domain's abstractions, without any temptation of implementation bias, yet with the precision afforded by type declarations, inheritance structures (to record classifications of the domain concepts), and contracts to express the abstract properties of the objects being described.
-Rather than using a separate method and notation for analysis and design, this apprroach integrates seamlessly with the subsequent phases (assuming the decision is indeed taken to develop a software system): it suffices to refine the deferred classes progressively by introducing effective elements, either by modifying the classes themselves, or by introducing design- and implementation-oriented descendants. In the resulting system, the classes that played an important role for analysis, and are the most meaningful for customers, will remain important; as we have seen ( [[3 The Software Process in Eiffel|"Seamlessness and reversibility", page 9]] ) this direct mapping property is a great help for extendibility.
+Rather than using a separate method and notation for analysis and design, this approach integrates seamlessly with the subsequent phases (assuming the decision is indeed taken to develop a software system): it suffices to refine the deferred classes progressively by introducing effective elements, either by modifying the classes themselves, or by introducing design- and implementation-oriented descendants. In the resulting system, the classes that played an important role for analysis, and are the most meaningful for customers, will remain important; as we have seen ( [[3 The Software Process in Eiffel#Seamlessness_and_reversibility|"Seamlessness and reversibility"]] ) this direct mapping property is a great help for extendibility.
-The following sketch (from the book [http://eiffel.com/doc/oosc/ Object-Oriented Software Construction] ) illustrates these ideas on theexample of scheduling the programs of a TV station. This is pure modeling of an application domain; no computers or software are involved yet. The class describes the notion of program segment.
+The following sketch (from the book [http://eiffel.com/doc/oosc/ Object-Oriented Software Construction] ) illustrates these ideas on the example of scheduling the programs of a TV station. This is pure modeling of an application domain; no computers or software are involved yet. The class describes the notion of program segment.
Note the use of assertions to define semantic properties of the class, its instances and its features. Although often presented as high-level, most object-oriented analysis methods (with the exception of Walden's and Nerson's Business Object Notation) have no support for the expression of such properties, limiting themselves instead to the description of broad structural relationships.
indexing
- description: "Individual fragments of a broadcasting schedule"
+ description: "Individual fragments of a broadcasting schedule"
deferred class
- SEGMENT
+ SEGMENT
feature -- Access
- schedule: SCHEDULE is deferred end
- -- Schedule to which segment belongs
+ schedule: SCHEDULE deferred end
+ -- Schedule to which segment belongs
- index: INTEGER is deferred end
- -- Position of segment in its schedule
+ index: INTEGER deferred end
+ -- Position of segment in its schedule
- starting_time, ending_time: INTEGER is deferred end
- -- Beginning and end of scheduled air time
+ starting_time, ending_time: INTEGER deferred end
+ -- Beginning and end of scheduled air time
- next: SEGMENT is deferred end
- -- Segment to be played next, if any
+ next: SEGMENT deferred end
+ -- Segment to be played next, if any
- sponsor: COMPANY is deferred end
- -- Segment's principal sponsor
+ sponsor: COMPANY deferred end
+ -- Segment's principal sponsor
- rating: INTEGER is deferred end
- -- Segment's rating (for children's viewing etc.)
+ rating: INTEGER deferred end
+ -- Segment's rating (for children's viewing etc.)
- Minimum_duration: INTEGER is 30
- -- Minimum length of segments, in seconds
+ Minimum_duration: INTEGER = 30
+ -- Minimum length of segments, in seconds
- Maximum_interval: INTEGER is 2
- -- Maximum time (seconds) between successive segments
+ Maximum_interval: INTEGER = 2
+ -- Maximum time (seconds) between successive segments
feature -- Element change
- set_sponsor (s: SPONSOR) is
- require
- not_void: s /= Void
- deferred
- ensure
- sponsor_set: sponsor = s
- end
+ set_sponsor (s: SPONSOR)
+ require
+ not_void: s /= Void
+ deferred
+ ensure
+ sponsor_set: sponsor = s
+ end
- ... change_next, set_rating omitted ...
+ ... change_next, set_rating omitted ...
invariant
- in_list: (1 <= index) and (index <= schedule.segments.count)
- in_schedule: schedule.segments.item (index) = Current
- next_in_list: (next /= Void) implies (schedule.segments.item (index + 1) = next)
- no_next_if_last: (next = Void) = (index = schedule.segments.count)
- non_negative_rating: rating >= 0
- positive times: (starting_time > 0) and (ending_time > 0)
- sufficient_duration: ending_time - starting_time >= Minimum_duration
- decent_interval: (next.starting_time) - ending_time <= Maximum_interval
+ in_list: (1 <= index) and (index <= schedule.segments.count)
+ in_schedule: schedule.segments.item (index) = Current
+ next_in_list: (next /= Void) implies (schedule.segments.item (index + 1) = next)
+ no_next_if_last: (next = Void) = (index = schedule.segments.count)
+ non_negative_rating: rating >= 0
+ positive times: (starting_time > 0) and (ending_time > 0)
+ sufficient_duration: ending_time - starting_time >= Minimum_duration
+ decent_interval: (next.starting_time) - ending_time <= Maximum_interval
end
@@ -313,45 +339,45 @@ end
Some deferred classes describe a structural property, useful to the description of many other classes. Typical examples are classes of the Kernel Library in EiffelBase:
- NUMERIC describes objects on which arithmetic operations +, -, *, / are available, with the properties of a ring (associativity, distributivity, zero elements etc.). Kernel Library classes such as INTEGER and REAL -- but not, for example, STRING -- are descendants of NUMERIC . An application that defines a class MATRIX may also make it a descendant of NUMERIC .
+NUMERIC describes objects on which arithmetic operations +, -, *, / are available, with the properties of a ring (associativity, distributivity, zero elements etc.). Kernel Library classes such as INTEGER and REAL -- but not, for example, STRING -- are descendants of NUMERIC. An application that defines a class MATRIX may also make it a descendant of NUMERIC.
- COMPARABLE describes objects on which comparison operations <, <=, >, >= are available, with the properties of a total preorder (transitivity, irreflexivity). Kernel Library classes such as CHARACTER , STRING and INTEGER -- but not out MATRIX example -- are descendants of NUMERIC .
+COMPARABLE describes objects on which comparison operations <, <=, >, >= are available, with the properties of a total preorder (transitivity, irreflexivity). Kernel Library classes such as CHARACTER, STRING and INTEGER -- but not out MATRIX example -- are descendants of NUMERIC.
-For such classes it is again essential to permit effective features in a deferred class, and to include assertions. For example class COMPARABLE declares infix "<" as deferred, and expresses >, >= and < effectively in terms of it.
+For such classes it is again essential to permit effective features in a deferred class, and to include assertions. For example class COMPARABLE declares infix "<" as deferred, and expresses >, >= and < effectively in terms of it.
-{{note|The type like Current will be explained in [[9 Inheritance|"Covariance and anchored declarations", page 79]] ; you may understand it, in the following class, as equivalent to COMPARABLE . }}
+{{note|The type like Current will be explained in [[9 Inheritance#Covariance_and_anchored_declarations|"Covariance and anchored declarations"]] ; you may understand it, in the following class, as equivalent to COMPARABLE. }}
indexing
- description: "Objects that can be compared according to a total preorder relation"
+ description: "Objects that can be compared according to a total preorder relation"
deferred class
- COMPARABLE
+ COMPARABLE
feature -- Comparison
- infix "<" (other: like Current): BOOLEAN is
- -- Is current object less than `other'?
- require
- other_exists: other /= Void
- deferred
- ensure
- asymmetric: Result implies not (other < Current)
- end
+ infix "<" (other: like Current): BOOLEAN
+ -- Is current object less than `other'?
+ require
+ other_exists: other /= Void
+ deferred
+ ensure
+ asymmetric: Result implies not (other < Current)
+ end
- infix "<=" (other: like Current): BOOLEAN is
- -- Is current object less than or equal to `other'?
- require
- other_exists: other /= Void
- do
- Result := (Current < other) or is_equal (other)
- ensure
- definition: Result = (Current < other) or is_equal (other)
- end
+ infix "<=" (other: like Current): BOOLEAN
+ -- Is current object less than or equal to `other'?
+ require
+ other_exists: other /= Void
+ do
+ Result := (Current < other) or is_equal (other)
+ ensure
+ definition: Result = (Current < other) or is_equal (other)
+ end
- ... Other features: infix ">", min, max, ...
+ ... Other features: infix ">", min, max, ...
invariant
- irreflexive: not (Current < Current)
+ irreflexive: not (Current < Current)
end -- class COMPARABLE
@@ -360,41 +386,41 @@ end -- class COMPARABLE
It is often necessary to define a new class in terms of several existing ones. For example:
-The Kernel Library classes INTEGER and REAL must inherit from both NUMERIC and COMPARABLE .
+The Kernel Library classes INTEGER and REAL must inherit from both NUMERIC and COMPARABLE.
-A class TENNIS_PLAYER , in a system for keeping track of player ranking, will inherit from COMPARABLE , as well as from other domain-specific classes.
+A class TENNIS_PLAYER, in a system for keeping track of player ranking, will inherit from COMPARABLE, as well as from other domain-specific classes.
-A class COMPANY_PLANE may inherit from both PLANE and ASSET .
+A class COMPANY_PLANE may inherit from both PLANE and ASSET.
-Class ARRAYED_LIST , describing an implementation of lists through arrays, may inherit from both LIST and ARRAY .
+Class ARRAYED_LIST, describing an implementation of lists through arrays, may inherit from both LIST and ARRAY.
In all such cases multiple inheritance provides the answer.
-Multiple inheritance can cause '''name clashes''' : two parents may include a feature with the same name. This would conflict with the ban on name overloading within a class -- the rule that no two features of a class may have the same name. Eiffel provides a simple way to remove the name clash at the point of inheritance through the rename subclause, as in
+Multiple inheritance can cause '''name clashes''' : two parents may include a feature with the same name. This would conflict with the ban on name overloading within a class -- the rule that no two features of a class may have the same name. Eiffel provides a simple way to remove the name clash at the point of inheritance through the rename subclause, as in
indexing
- description: "Sequential finite lists implemented as arrays"
+ description: "Sequential finite lists implemented as arrays"
class
- ARRAYED_LIST [G]
+ ARRAYED_LIST [G]
inherit
- LIST [G]
+ LIST [G]
- ARRAY [G]
- rename
- count as capacity,
- item as array_item
- end
+ ARRAY [G]
+ rename
+ count as capacity,
+ item as array_item
+ end
feature
- ...
+ ...
end -- class ARRAYED_LIST
-Here both LIST and ARRAY have features called count and item . To make the new class valid, we give new names to the features inherited from ARRAY , which will be known within ARRAYED_LIST as capacity and array_item . Of course we could have renamed the LIST versions instead, or renamed along both inheritance branches.
+Here both LIST and ARRAY have features called count and item. To make the new class valid, we give new names to the features inherited from ARRAY, which will be known within ARRAYED_LIST as capacity and array_item. Of course we could have renamed the LIST versions instead, or renamed along both inheritance branches.
Every feature of a class has a '''final name''' : for a feature introduced in the class itself ("immediate" feature) it is the name appearing in the declaration; for an inherited feature that is not renamed, it is the feature's name in the parent; for a renamed feature, it is the name resulting from the renaming. This definition yields a precise statement of the rule against in-class overloading:
@@ -402,9 +428,9 @@ Every feature of a class has a '''final name''' : for a feature introduced in th
It is interesting to compare renaming and redefinition. The principal distinction is between features and feature names. Renaming keeps a feature, but changes its name. Redefinition keeps the name, but changes the feature. In some cases, it is of course appropriate to do both.
-Renaming is interesting even in the absence of name clashes. A class may inherit from a parent a feature which it finds useful for its purposes, but whose name, appropriate for the context of the parent, is not consistent with the context of the heir. This is the case with ARRAY 's feature count in the last example: the feature that defines the number of items in an array -- the total number of available entries -- becomes, for an arrayed list, the maximum number of list items; the truly interesting indication of the number of items is the count of how many items have been inserted in the list, as given by feature count from LIST . But even if we did not have a name clash because of the two inherited count features we should rename ARRAY 's count as capacity to maintain the consistency of the local feature terminology.
+Renaming is interesting even in the absence of name clashes. A class may inherit from a parent a feature which it finds useful for its purposes, but whose name, appropriate for the context of the parent, is not consistent with the context of the heir. This is the case with ARRAY's feature count in the last example: the feature that defines the number of items in an array -- the total number of available entries -- becomes, for an arrayed list, the maximum number of list items; the truly interesting indication of the number of items is the count of how many items have been inserted in the list, as given by feature count from LIST. But even if we did not have a name clash because of the two inherited count features we should rename ARRAY 's count as capacity to maintain the consistency of the local feature terminology.
-The rename subclause appears before all the other feature adaptation subclauses -- redefine already seen, and the remaining ones export , undefine and select -- since an inherited feature that has been renamed sheds its earlier identity once and for all: within the class, and to its own clients and descendants, it will be known solely through the new name. The original name has simply disappeared from the name space. This is essential to the view of classes presented earlier: self-contained, consistent abstractions prepared carefully for the greatest enjoyment of clients and descendants.
+The rename subclause appears before all the other feature adaptation subclauses -- redefine already seen, and the remaining ones export, undefine and select -- since an inherited feature that has been renamed sheds its earlier identity once and for all: within the class, and to its own clients and descendants, it will be known solely through the new name. The original name has simply disappeared from the name space. This is essential to the view of classes presented earlier: self-contained, consistent abstractions prepared carefully for the greatest enjoyment of clients and descendants.
==Inheritance and contracts==
@@ -414,66 +440,68 @@ The first rule is that invariants accumulate down an inheritance structure:
{{rule|name=Invariant Accumulation|text=The invariants of all the parents of a class apply to the class itself. }}
-The invariant of a class is automatically considered to include -- in the sense of logical "and" -- the invariants of all its parents. This is a consequence of the view of inheritance as an "is" relation: if we may consider every instance of B as an instance of A , then every consistency constraint on instances of A must also apply to instances of B .
+The invariant of a class is automatically considered to include -- in the sense of logical "and" -- the invariants of all its parents. This is a consequence of the view of inheritance as an "is" relation: if we may consider every instance of B as an instance of A, then every consistency constraint on instances of A must also apply to instances of B.
Next we consider routine preconditions and postconditions. The rule here will follow from an examination of what contracts mean in the presence of polymorphism and dynamic binding.
-Consider a parent A and a proper descendant B (a direct heir on the following figure), which redefines a routine r inherited from A .
- [[Image:tutorial-12]]
-As a result of dynamic binding, a call a1 . r from a client C may be serviced not by A 's version of r but by B 's version if a1 , although declared of type A , becomes at run time attached to an instance of B . This shows the combination of inheritance, redefinition, polymorphism and dynamic binding as providing a form of subcontracting; A subcontracts certain calls to B .
+Consider a parent A and a proper descendant B (a direct heir on the following figure), which redefines a routine r inherited from A.
-The problem is to keep subcontractors honest. Assuming preconditions and postconditions as shown on the last figure, a call in C of the form
+[[Image:tutorial-12]]
+
+As a result of dynamic binding, a call a1 . r from a client C may be serviced not by A 's version of r but by B 's version if a1, although declared of type A, becomes at run time attached to an instance of B. This shows the combination of inheritance, redefinition, polymorphism and dynamic binding as providing a form of subcontracting; A subcontracts certain calls to B.
+
+The problem is to keep subcontractors honest. Assuming preconditions and postconditions as shown on the last figure, a call in C of the form
-if a1.pre then a1.r end
+ if a1.pre then a1.r end
-or just a1.q ; a1.r where the postcondition of q implies the precondition pre of r , satisfies the terms of the contract and hence is entitled to being handled correctly -- to terminate in a state satisfying a1 . post . But if we let the subcontractor B redefine the assertions to arbitrary ''pre'' ' and ''post''', this is not necessarily the case: ''pre''' could be stronger than ''pre'', enabling B not to process correctly certain calls that are correct from A 's perspective; and ''post''' could be weaker than ''post'', enabling B to do less of a job than advertized for r in the Contract Form of A , the only official reference for authors of client classes such as C . (An assertion p is stronger than or equal to an assertion q if p implies q in the sense of boolean implication.)
+or just a1.q; a1.r where the postcondition of q implies the precondition pre of r, satisfies the terms of the contract and hence is entitled to being handled correctly -- to terminate in a state satisfying a1 . post. But if we let the subcontractor B redefine the assertions to arbitrary ''pre'' ' and ''post''', this is not necessarily the case: ''pre''' could be stronger than ''pre'', enabling B not to process correctly certain calls that are correct from A's perspective; and ''post''' could be weaker than ''post'', enabling B to do less of a job than advertized for r in the Contract Form of A, the only official reference for authors of client classes such as C. (An assertion p is stronger than or equal to an assertion q if p implies q in the sense of boolean implication.)
The rule, then, is that for the redefinition to be correct the new precondition ''pre''' must be weaker than or equal to the original ''pre'', and the new postcondition ''post''' must be stronger than or equal to the original ''post'''.
-Because it is impossible to check simply that an assertion is weaker or stronger than another, the language rule relies on different forms of the assertion constructs, else require and then ensure , for redeclared routines. They rely on the mathematical property that, for any assertions p and q, p implies ( p or q ) , and (p and q) implies p . For a precondition, using else require with a new assertion will perform an or , which can only weaken the original; for a postcondition, then ensure will perform an and , which can only strengthen the original. Hence the rule:
+Because it is impossible to check simply that an assertion is weaker or stronger than another, the language rule relies on different forms of the assertion constructs, else require and then ensure, for redeclared routines. They rely on the mathematical property that, for any assertions p and q, p implies ( p or q ), and (p and q) implies p. For a precondition, using else require with a new assertion will perform an or, which can only weaken the original; for a postcondition, then ensure will perform an and, which can only strengthen the original. Hence the rule:
{{rule|name=Assertion Redeclaration|text=In the redeclared version of a routine, it is not permitted to use a require or ensure clause. Instead you may: Introduce a new condition with require else, for or-ing with the original precondition. Introduce a new condition with ensure then, for and-ing with the original postcondition. In the absence of such a clause, the original assertions are retained. }}
The last case -- retaining the original -- is frequent but by no means universal.
-The Assertion Redeclaration rule applies to '''redeclarations'''. This terms covers not just redefinition but also effecting (the implementation, by a class, of a feature that it inherits deferred). The rules -- not just for assertions but also, as reviewed below, for typing -- are indeed the same in both cases. Without the Assertion Redeclaration rule, assertions on deferred features, such as those on extend , count and forth in [[9 Inheritance|"Deferred features and classes", page 57]] , would be almost useless -- wishful thinking; the rule makes them binding on all effectings in descendants.
+The Assertion Redeclaration rule applies to '''redeclarations'''. This terms covers not just redefinition but also effecting (the implementation, by a class, of a feature that it inherits deferred). The rules -- not just for assertions but also, as reviewed below, for typing -- are indeed the same in both cases. Without the Assertion Redeclaration rule, assertions on deferred features, such as those on extend, count and forth in [[9 Inheritance#Deferred_features_and_classes|"Deferred features and classes"]] , would be almost useless -- wishful thinking; the rule makes them binding on all effectings in descendants.
-From the Assertion Redeclaration rule follows an interesting technique: '''abstract preconditions'''. What needs to be weakened for a precondition (or strengthened for a postcondition) is not the assertion's concrete semantics but its abstract specification as seen by the client. A descendant can change the implementation of that specification as it pleases, even to the effect of strengthening the concrete precondition, as long as the abstract form is kept or weakened. The precondition of procedure extend in the deferred class LIST provided an example. We wrote the routine (page [[9 Inheritance|58]] ) as
+From the Assertion Redeclaration rule follows an interesting technique: '''abstract preconditions'''. What needs to be weakened for a precondition (or strengthened for a postcondition) is not the assertion's concrete semantics but its abstract specification as seen by the client. A descendant can change the implementation of that specification as it pleases, even to the effect of strengthening the concrete precondition, as long as the abstract form is kept or weakened. The precondition of procedure extend in the deferred class LIST provided an example. We wrote the routine (in [[9 Inheritance#Deferred_features_and_classes|"Deferred features and classes"]] ) as
-extend (x: G) is
- -- Add `x' at end of list.
- require
- space_available: not full
- deferred
- ensure
- one_more: count = old count + 1
- end
+ extend (x: G)
+ -- Add `x' at end of list.
+ require
+ space_available: not full
+ deferred
+ ensure
+ one_more: count = old count + 1
+ end
-The precondition expresses that it is only possible to add an item to a list if the representation is not full. We may well consider -- in line with the Eiffel principle that whenever possible structures should be of unbounded capacity -- that LIST should by default make full always return false:
+The precondition expresses that it is only possible to add an item to a list if the representation is not full. We may well consider -- in line with the Eiffel principle that whenever possible structures should be of unbounded capacity -- that LIST should by default make full always return false:
-full: BOOLEAN is
- -- Is representation full?
- -- (Default: no)
- do
- Result := False
- end
+ full: BOOLEAN
+ -- Is representation full?
+ -- (Default: no)
+ do
+ Result := False
+ end
-Now a class BOUNDED_LIST that implements bounded-size lists (inheriting, like the earlier ARRAYED_LIST , from both LIST and ARRAY ) may redefine full :
+Now a class BOUNDED_LIST that implements bounded-size lists (inheriting, like the earlier ARRAYED_LIST, from both LIST and ARRAY) may redefine full:
-full: BOOLEAN is
- -- Is representation full?
- -- (Answer: if and only if number of items is capacity)
- do
- Result := (count = capacity)
- end
+ full: BOOLEAN
+ -- Is representation full?
+ -- (Answer: if and only if number of items is capacity)
+ do
+ Result := (count = capacity)
+ end
-Procedure extend remains applicable as before; any client that used it properly with LIST can rely polymorphically on the FIXED_LIST implementation. The abstract precondition of extend has not changed, even though the concrete implementation of that precondition has in fact been strengthened.
+Procedure extend remains applicable as before; any client that used it properly with LIST can rely polymorphically on the FIXED_LIST implementation. The abstract precondition of extend has not changed, even though the concrete implementation of that precondition has in fact been strengthened.
-Note that a class such as BOUNDED_LIST , the likes of which indeed appear in EiffelBase, is not a violation of the Eiffel advice to stay away from fixed-size structures. The corresponding structures are bounded, but the bounds are changeable. Although extend requires not full , another feature, called force in all applicable classes, will add an element at the appropriate position by resizing and reallocating the structure if necessary. Even arrays in Eiffel are not fixed-size, and have a procedure force with no precondition, accepting any index position.
+Note that a class such as BOUNDED_LIST, the likes of which indeed appear in EiffelBase, is not a violation of the Eiffel advice to stay away from fixed-size structures. The corresponding structures are bounded, but the bounds are changeable. Although extend requires not full, another feature, called force in all applicable classes, will add an element at the appropriate position by resizing and reallocating the structure if necessary. Even arrays in Eiffel are not fixed-size, and have a procedure force with no precondition, accepting any index position.
The Assertion Redeclaration rule, together with the Invariant Accumulation rule, provides the right methodological perspective for understanding inheritance and the associated mechanisms. Defining a class as inheriting from another is a strong commitment; it means inheriting not only the features but the logical constraints. Redeclaring a routine is bound by a similar committment: to provide a new implementation (or, for an effecting, a first implementation) of a previously defined semantics, as expressed by the original contract. Usually you have a wide margin for choosing your implementation, since the contract only defines a range of possible behaviors (rather than just one behavior), but you '''must''' remain within that range. Otherwise you would be perverting the goals of redeclaration, using this mechanism as a sort of late-stage hacking to override bugs in ancestor classes.
@@ -483,73 +511,75 @@ It is not an error to inherit two deferred features from different parents under
More generally, it is permitted to have any number of deferred features and at most one effective feature that share the same name: the effective version, if present will effect all the others.
-All this is not a violation of the Final Name rule (page [[9 Inheritance|65]] ), since the name clashes prohibited by the rule involve two different features having the same final name; here the result is just one feature, resulting from the join of all the inherited versions.
+All this is not a violation of the Final Name rule (defined in [[9 Inheritance#Multiple_inheritance_and_renaming|"Multiple inheritance and renaming"]] ), since the name clashes prohibited by the rule involve two different features having the same final name; here the result is just one feature, resulting from the join of all the inherited versions.
-Sometimes we may want to join ''effective'' features inherited from different parents, assuming again the features have compatible signatures. One way is to redefine them all into a new version; then they again become one feature, with no name clash in the sense of the Final Name rule. But in other cases we may simply want one of the inherited implementations to take over the others. The solution is to revert to the preceding case by '''uneffecting''' the other features; uneffecting an inherited effective feature makes it deferred (this is the reverse of effecting, which turns an inherited deferred feature into an effective one). The syntax uses the undefine subclause:
+Sometimes we may want to join ''effective'' features inherited from different parents, assuming again the features have compatible signatures. One way is to redefine them all into a new version; then they again become one feature, with no name clash in the sense of the Final Name rule. But in other cases we may simply want one of the inherited implementations to take over the others. The solution is to revert to the preceding case by '''uneffecting''' the other features; uneffecting an inherited effective feature makes it deferred (this is the reverse of effecting, which turns an inherited deferred feature into an effective one). The syntax uses the undefine subclause:
class D
inherit
- A
- rename
- g as f
- -- g was effective in A
- undefine
- f
- end
+ A
+ rename
+ g as f
+ -- g was effective in A
+ undefine
+ f
+ end
- B
- undefine
- f
- -- f was effective in B
- end
+ B
+ undefine
+ f
+ -- f was effective in B
+ end
- C
- -- C also has an effective feature f , which will serve as
- -- implementation for the result of the join.
+ C
+ -- C also has an effective feature f , which will serve as
+ -- implementation for the result of the join.
feature
- ...
+ ...
-Again what counts, to determine if there is an invalid name clash, is the final name of the features. In this example to of the joined features were originally called f ; the one from A was called g , but in D it is renamed as f , so without the undefinition it would cause an invalid name clash.
+Again what counts, to determine if there is an invalid name clash, is the final name of the features. In this example to of the joined features were originally called f; the one from A was called g, but in D it is renamed as f, so without the undefinition it would cause an invalid name clash.
Feature joining is the most common application of uneffecting. In some non-joining cases, however, it may be useful to forget the original implementation of a feature and let it start a new life devoid of any burden from the past.
==Changing the export status==
-Another Feature Adaptation subclause, export , makes it possible to change the export status of an inherited feature. By default -- covering the behavior desired in the vast majority of practical cases -- an inherited feature keeps its original export status (exported, secret, selectively exported). In some cases, however, this is not appropriate:
+Another Feature Adaptation subclause, export, makes it possible to change the export status of an inherited feature. By default -- covering the behavior desired in the vast majority of practical cases -- an inherited feature keeps its original export status (exported, secret, selectively exported). In some cases, however, this is not appropriate:
A feature may have played a purely implementation-oriented role in the parent, but become interesting to clients of the heir. Its status will change from secret to exported.
-In implementation inheritance (for example ARRAYED_LIST inheriting from ARRAY ) an exported feature of the parent may not be suitable for direct use by clients of the heir. The change of status in this case is from exported to secret.
+In implementation inheritance (for example ARRAYED_LIST inheriting from ARRAY) an exported feature of the parent may not be suitable for direct use by clients of the heir. The change of status in this case is from exported to secret.
You can achieve either of these goals by writing
class D inherit
- A
- export {X, Y, ...} feature1, feature2, ... end
+ A
+ export {X, Y, ...} feature1, feature2, ... end
- ...
+ ...
-This gives a new export status to the features listed (under their final names since, as noted, export like all other subclauses comes after rename if present): they become exported to the classes listed. In most cases this list of classes, X , Y , ..., consists of just ANY , to re-export a previously secret feature, or NONE , to hide a previously exported feature. It is also possible, in lieu of the feature list, to use the keyword all to apply the new status to all features inherited from the listed parent. Then there can be more than one class-feature list, as in
+This gives a new export status to the features listed (under their final names since, as noted, export like all other subclauses comes after rename if present): they become exported to the classes listed. In most cases this list of classes, X, Y, ..., consists of just ANY, to re-export a previously secret feature, or NONE, to hide a previously exported feature. It is also possible, in lieu of the feature list, to use the keyword all to apply the new status to all features inherited from the listed parent. Then there can be more than one class-feature list, as in
class ARRAYED_LIST [G] inherit
- ARRAY [G]
- rename
- count as capacity, item as array_item, put as array_put
- export
- {NONE} all
- {ANY} capacity
- end
+ ARRAY [G]
+ rename
+ count as capacity, item as array_item, put as array_put
+ export
+ {NONE} all
+ {ANY} capacity
+ end
- ...
+
+
+ ...
-where any explicit listing of a feature, such as capacity , takes precedence over the export status specified for all . Here most features of ARRAY are secret in ARRAYED_LIST , because the clients should not permitted to manipulate array entries directly: they will manipulate them indirectly through list features such as extend and item , whose implementation relies on array_item and array_put . But ARRAY 's feature count remains useful, under the name capacity , to the clients of ARRAYED_LIST .
+where any explicit listing of a feature, such as capacity, takes precedence over the export status specified for all. Here most features of ARRAY are secret in ARRAYED_LIST, because the clients should not permitted to manipulate array entries directly: they will manipulate them indirectly through list features such as extend and item, whose implementation relies on array_item and array_put. But ARRAY's feature count remains useful, under the name capacity, to the clients of ARRAYED_LIST.
==Flat and Flat-Contract Forms==
@@ -557,25 +587,27 @@ Thanks to inheritance, a concise class text may achieve a lot, relying on all th
This is part of the power of the object-oriented form of reuse, but can create a comprehension and documentation problem when the inheritance structures become deep: how does one understand such a class, either as client author or as maintainer? For clients, the Contract Form, entirely deduced from the class text, does not tell the full story about available features; and maintainers must look to proper ancestors for much of the relevant information.
-These observations suggest ways to produce, from a class text, a version that is equivalent feature-wise and assertion-wise, but has no inheritance dependency. This is called the '''Flat Form''' of the class. It is a class text that has no inheritance clause and includes all the features of the class, immediate (declared in the class itself) as well as inherited. For the inherited features, the flat form must of course take account of all the feature adaptation mechanisms: renaming (each feature must appear under its final name), redefinition, effecting, uneffecting and export status change. For redeclared features, else require clauses are or-ed with the precursors' preconditions, and then ensure clauses are and-ed with precursors' postconditions. For invariants, all the ancestors' clauses are concatenated. As a result, the flat form yields a view of the class, its features and its assertions that conforms exactly to the view offered to clients and (except for polymorphic uses) heirs.
+These observations suggest ways to produce, from a class text, a version that is equivalent feature-wise and assertion-wise, but has no inheritance dependency. This is called the '''Flat Form''' of the class. It is a class text that has no inheritance clause and includes all the features of the class, immediate (declared in the class itself) as well as inherited. For the inherited features, the flat form must of course take account of all the feature adaptation mechanisms: renaming (each feature must appear under its final name), redefinition, effecting, uneffecting and export status change. For redeclared features, else require clauses are or-ed with the precursors' preconditions, and then ensure clauses are and-ed with precursors' postconditions. For invariants, all the ancestors' clauses are concatenated. As a result, the flat form yields a view of the class, its features and its assertions that conforms exactly to the view offered to clients and (except for polymorphic uses) heirs.
-As with the Contract Form ( [[8 Design by Contract (tm), Assertions and Exceptions|"The contract form of a class", page 44]] ), producing the Flat Form is the responsibility of tools in the development environment. In EiffelStudio, you will just click the "Flat" icon.
+As with the Contract Form ( [[8 Design by Contract (tm), Assertions and Exceptions#The_contract_form_of_a_class|"The contract form of a class"]] ), producing the Flat Form is the responsibility of tools in the development environment. In EiffelStudio, you will just click the "Flat" icon.
The Contract Form of the Flat Form of a class is known as its '''Flat-Contract Form'''. It gives the complete interface specification, documenting all exported features and assertions -- immediate or inherited -- and hiding implementation aspects. It is the appropriate documentation for a class.
==Repeated inheritance and selection==
-An inheritance mechanism, following from multiple inheritance, remains to be seen. Through multiple inheritance, a class can be a proper descendant of another through more than one path. This is called repeated inheritance and can be indirect, as in the following figure, or even direct, when a class D lists a class A twice in its inherit clause.
- [[Image:tutorial-13]]
-The figure's particular example is in fact often used by introductory presentations of multiple inheritance, which is a pedagogical mistake: simple multiple inheritance examples (such as INTEGER inheriting from NUMERIC and COMPARABLE , or COMPANY_PLANE from ASSET and PLANE ) should involve the combination of '''separate abstractions'''. Repeated inheritance is an advanced technique; although invaluable, it does not arise in elementary uses and requires a little more care.
+An inheritance mechanism, following from multiple inheritance, remains to be seen. Through multiple inheritance, a class can be a proper descendant of another through more than one path. This is called repeated inheritance and can be indirect, as in the following figure, or even direct, when a class D lists a class A twice in its inherit clause.
-In fact there is only one non-trivial issue in repeated inheritance: what does a feature of the repeated ancestor, such as change_address and computer_account , mean for the repeated descendant, here TEACHING_ASSISTANT ? (The example features chosen involve a routine and an attribute; the basic rules will be the same.)
+[[Image:tutorial-13]]
+
+The figure's particular example is in fact often used by introductory presentations of multiple inheritance, which is a pedagogical mistake: simple multiple inheritance examples (such as INTEGER inheriting from NUMERIC and COMPARABLE, or COMPANY_PLANE from ASSET and PLANE) should involve the combination of '''separate abstractions'''. Repeated inheritance is an advanced technique; although invaluable, it does not arise in elementary uses and requires a little more care.
+
+In fact there is only one non-trivial issue in repeated inheritance: what does a feature of the repeated ancestor, such as change_address and computer_account, mean for the repeated descendant, here TEACHING_ASSISTANT? (The example features chosen involve a routine and an attribute; the basic rules will be the same.)
There are two possibilities: sharing (the repeatedly inherited feature yields just one feature in the repeated descendant) and duplication (it yields two). Examination of various cases shows quickly that a fixed policy, or one that would apply to all the features of a class, would be inappropriate.
-Feature change_address calls for sharing: as a teaching assistant, you may be both teacher and student, but you are just one person, with just one official domicile.
+Feature change_address calls for sharing: as a teaching assistant, you may be both teacher and student, but you are just one person, with just one official domicile.
-If there are separate accounts for students' course work and for faculty, you may need one of each kind, suggesting that computer_account calls for duplication.
+If there are separate accounts for students' course work and for faculty, you may need one of each kind, suggesting that computer_account calls for duplication.
The Eiffel rule enables, once again, the software developer to craft the resulting class so as to tune it to the exact requirements. Not surprisingly, it is based on names, in accordance with the Final Name rule (no in-class overloading):
@@ -583,125 +615,121 @@ The Eiffel rule enables, once again, the software developer to craft the resulti
So to tune the repeated descendant, feature by feature, for sharing and replication it suffices to use renaming.
-Doing nothing will cause sharing, which is indeedthe desired policy in most cases (especially those cases of unintended repeated inheritance: making D inherit from A even though it also inherits from B , which you forgot is already a descendant of A ).
+Doing nothing will cause sharing, which is indeedthe desired policy in most cases (especially those cases of unintended repeated inheritance: making D inherit from A even though it also inherits from B, which you forgot is already a descendant of A).
-If you use renaming somewhere along the way, so that the final names are different, you will obtain two separate features. It does not matter where the renaming occurs; all that counts is whether in the common descendant, TEACHING_ASSISTANT in the last figure, the names are the same or different. So you can use renaming at that last stage to cause replication; but if the features have been renamed higher you can also use last-minute renaming to avoid replication, by bringing them back to a single name.
+If you use renaming somewhere along the way, so that the final names are different, you will obtain two separate features. It does not matter where the renaming occurs; all that counts is whether in the common descendant, TEACHING_ASSISTANT in the last figure, the names are the same or different. So you can use renaming at that last stage to cause replication; but if the features have been renamed higher you can also use last-minute renaming to avoid replication, by bringing them back to a single name.
-The Repeated Inheritance rule gives the desired flexibility to disambiguate the meaning of repeatedly inherited features. There remains a problem in case of redeclaration and polymorphism. Assume that somewhere along the inheritance paths one or both of two replicated versions of a feature f , such as computer_account in the example, has been redeclared; we need to define the effect of a call a.f ( a.computer_account in the example) if a is of the repeated ancestor type, here UNIVERSITY_PERSON , and has become attached as a result of polymorphism to an instance of the repeated descendant, here TEACHING_ASSISTANT . If one or more of the intermediate ancestors has redefined its version of the feature, the dynamically-bound call has two or moreversions to choose from.
+The Repeated Inheritance rule gives the desired flexibility to disambiguate the meaning of repeatedly inherited features. There remains a problem in case of redeclaration and polymorphism. Assume that somewhere along the inheritance paths one or both of two replicated versions of a feature f, such as computer_account in the example, has been redeclared; we need to define the effect of a call a.f ( a.computer_account in the example) if a is of the repeated ancestor type, here UNIVERSITY_PERSON, and has become attached as a result of polymorphism to an instance of the repeated descendant, here TEACHING_ASSISTANT. If one or more of the intermediate ancestors has redefined its version of the feature, the dynamically-bound call has two or moreversions to choose from.
-A select clause will resolve the ambiguity, as in
+A select clause will resolve the ambiguity, as in
class TEACHING_ASSISTANT
inherit
- TEACHER
- rename
- computer_account as faculty_account
- select
- faculty_account
- end
+ TEACHER
+ rename
+ computer_account as faculty_account
+ select
+ faculty_account
+ end
- STUDENT
- rename
- computer_account as student_account
- end
+ STUDENT
+ rename
+ computer_account as student_account
+ end
- ...
+ ...
-We assume here that that no other renaming has occurred -- TEACHING_ASSISTANT takes care of the renaming to ensure replication -- but that one of the two parents has redefined computer_account , for example TEACHER to express the special privileges of faculty accounts. In such a case the rule is that one (and exactly one) of the two parent clauses in TEACHING_ASSISTANT '''must''' select the corresponding version. Note that no problem arises for an entity declared as
+We assume here that that no other renaming has occurred -- TEACHING_ASSISTANT takes care of the renaming to ensure replication -- but that one of the two parents has redefined computer_account, for example TEACHER to express the special privileges of faculty accounts. In such a case the rule is that one (and exactly one) of the two parent clauses in TEACHING_ASSISTANT '''must''' select the corresponding version. Note that no problem arises for an entity declared as
-ta: TEACHING_ASSISTANT
+ ta: TEACHING_ASSISTANT
-since the valid calls are of the form ta.faculty_account and ta.student_account , neither of them ambiguous; the call ta.computer_account would be invalid, since after the renamings class TEACHING_ASSISTANT has no feature of that name. The select only applies to a call
+since the valid calls are of the form ta.faculty_account and ta.student_account, neither of them ambiguous; the call ta.computer_account would be invalid, since after the renamings class TEACHING_ASSISTANT has no feature of that name. The select only applies to a call
-up.computer_account
-
+ up.computer_account
+with up of type UNIVERSITY_PERSON, dynamically attached to an instance of TEACHING_ASSISTANT; then the select resolves the ambiguity by causing the call to use the version from TEACHER.
-with up of type UNIVERSITY_PERSON , dynamically attached to an instance of TEACHING_ASSISTANT ; then the select resolves the ambiguity by causing the call to use the version from TEACHER .
+So if you traverse a list computer_users: LIST [UNIVERSITY_PERSON] to print some information about the computer account of each list element, the account used for a teaching assistant is the faculty account, not the student account.
-So if you traverse a list computer_users: LIST [UNIVERSITY_PERSON] to print some information about the computer account of each list element, the account used for a teaching assistant is the faculty account, not the student account.
+You may, if desired, redefine faculty_account in class TEACHING_ASSISTANT, using student_account if necessary, to take into consideration the existence of another account. But in all cases we need a precise disambiguation of what computer_account means for a TEACHING_ASSISTANT object known only through a UNIVERSITY_PERSON entity.
-You may, if desired, redefine faculty_account in class TEACHING_ASSISTANT , using student_account if necessary, to take into consideration the existence of another account. But in all cases we need a precise disambiguation of what computer_account means for a TEACHING_ASSISTANT object known only through a UNIVERSITY_PERSON entity.
-
-The select is only needed in case of replication. If the Repeated Inheritance rule would imply sharing, as with change_address, and one or both of the shared versions has been redeclared, the Final Name rule makes the class invalid, since it now has '''two different features''' with the same name. (This is only a problem if both versions are effective; if one or both are deferred there is no conflict but a mere case of feature joining as explained in [[9 Inheritance|"Join and uneffecting", page 70]] .) The two possible solutions follow from the previous discussions:
+The select is only needed in case of replication. If the Repeated Inheritance rule would imply sharing, as with change_address, and one or both of the shared versions has been redeclared, the Final Name rule makes the class invalid, since it now has '''two different features''' with the same name. (This is only a problem if both versions are effective; if one or both are deferred there is no conflict but a mere case of feature joining as explained in [[9 Inheritance#Join_and_uneffecting|"Join and uneffecting"]] .) The two possible solutions follow from the previous discussions:
If you do want sharing, one of the two versions must take precedence over the other. It suffices to '''undefine''' the other, and everything gets back to order. Alternatively, you can redefine both into a new version, which takes precedence over both.
-If you want to keep both versions, switch from sharing to replication: rename one or both of the features so that they will have different names; then you must select one of them.
+If you want to keep both versions, switch from sharing to replication: rename one or both of the features so that they will have different names; then you must select one of them.
==Constrained genericity==
-Eiffel's inheritance mechanism has an important application to extending the flexibility of the '''genericity''' mechanism. In a class SOME_CONTAINER [G] , as noted (section [[7 Genericity and Arrays|7]] ), the only operations available on entities of type G , the formal generic parameter, are those applicable to entities of all types. A generic class may, however, need to assume more about the generic parameter, as with a class SORTABLE_ARRAY [G ...] which will have a procedure sort that needs, at some stage, to perform tests of the form
+Eiffel's inheritance mechanism has an important application to extending the flexibility of the '''genericity''' mechanism. In a class SOME_CONTAINER [G], as noted in [[7 Genericity and Arrays|"Genericity and Arrays"]] ), the only operations available on entities of type G, the formal generic parameter, are those applicable to entities of all types. A generic class may, however, need to assume more about the generic parameter, as with a class SORTABLE_ARRAY [G ...] which will have a procedure sort that needs, at some stage, to perform tests of the form
-if item (i) < item (j) then ...
+ if item (i) < item (j) then ...
-
-where item (i) and item (j) are of type G . But this requires the availability of a feature infix "<" in all types that may serve as actual generic parameters corresponding to G . Using the type SORTABLE_ARRAY [INTEGER] should be permitted, because INTEGER has such a feature; but not SORTABLE_ARRAY [COMPLEX] if there is no total order relation on COMPLEX .
+where item (i) and item (j) are of type G. But this requires the availability of a feature infix "<" in all types that may serve as actual generic parameters corresponding to G. Using the type SORTABLE_ARRAY [INTEGER] should be permitted, because INTEGER has such a feature; but not SORTABLE_ARRAY [COMPLEX] if there is no total order relation on COMPLEX.
To cover such cases, declare the class as
class SORTABLE_ARRAY [G -> COMPARABLE]
+making it '''constrained generic'''. The symbol -> recalls the arrow of inheritance diagrams; what follows it is a type, known as the generic constraint. Such a declaration means that:
-making it '''constrained generic'''. The symbol -> recalls the arrow of inheritance diagrams; what follows it is a type, known as the generic constraint. Such a declaration means that:
+Within the class, you may apply the features of the generic constraint -- here the features of COMPARABLE: infix "<", infix "<" etc. -- to expressions of type G.
-Within the class, you may apply the features of the generic constraint -- here the features of COMPARABLE : infix "<" , infix "<" etc. -- to expressions of type G .
+A generic derivation is only valid if the chosen actual generic parameter conforms to the constraint. Here you can use SORTABLE_ARRAY [INTEGER] since INTEGER inherits from COMPARABLE, but not SORTABLE_ARRAY [COMPLEX] if COMPLEX is not a descendant of COMPARABLE.
-A generic derivation is only valid if the chosen actual generic parameter conforms to the constraint. Here you can use SORTABLE_ARRAY [INTEGER] since INTEGER inherits from COMPARABLE , but not SORTABLE_ARRAY [COMPLEX] if COMPLEX is not a descendant of COMPARABLE .
+A class can have a mix of constrained and unconstrained generic parameters, as in the EiffelBase class HASH_TABLE [G, H -> HASHABLE] whose first parameter represents the types of objects stored in a hash table, the second representing the types of the keys used to store them, which must be HASHABLE. As these examples suggest, structural property classes such as COMPARABLE, NUMERIC and HASHABLE are the most common choice for generic constraints.
-A class can have a mix of constrained and unconstrained generic parameters, as in the EiffelBase class HASH_TABLE [G, H -> HASHABLE] whose first parameter represents the types of objects stored in a hash table, the second representing the types of the keys used to store them, which must be HASHABLE . As these examples suggest, structural property classes such as COMPARABLE , NUMERIC and HASHABLE are the most common choice for generic constraints.
-
-Unconstrained genericity, as in C [G] , is defined as equivalent to C [G -> ANY] .
+Unconstrained genericity, as in C [G], is defined as equivalent to C [G -> ANY].
==Assignment attempt==
-The Type Conformance rule ( [[9 Inheritance|"Polymorphism", page 53]] ) ensures type safety by requiring all assignments to be from a more specific source to a more general target.
+The Type Conformance rule ( [[9 Inheritance#Polymorphism|"Polymorphism"]] ) ensures type safety by requiring all assignments to be from a more specific source to a more general target.
-Sometimes you can't be sure of the source object's type. This happens for example when the object comes from the outside -- a file, a database, a network. The persistence storage mechanism( [[6 The Dynamic Structure: Execution Model|"Deep operations and persistence", page 30]] ) includes, along with the procedure store seen there, the reverse operation, a function retrieved which yields an object structure retrieved from a file or network, to which it was sent using store . But retrieved as declared in the corresponding class STORABLE of EiffelBase can only return the most general type, ANY ; it is not possible to know its exact type until execution time, since the corresponding objects are not under the control of the retrieving system, and might even have been corrupted by some external agent.
+Sometimes you can't be sure of the source object's type. This happens for example when the object comes from the outside -- a file, a database, a network. The persistence storage mechanism( [[6 The Dynamic Structure: Execution Model#Deep_operations_and_persistence|"Deep operations and persistence"]] ) includes, along with the procedure store seen there, the reverse operation, a function retrieved which yields an object structure retrieved from a file or network, to which it was sent using store. But retrieved as declared in the corresponding class STORABLE of EiffelBase can only return the most general type, ANY; it is not possible to know its exact type until execution time, since the corresponding objects are not under the control of the retrieving system, and might even have been corrupted by some external agent.
In such cases you cannot trust the declared type but must check it against the type of an actual run-time object. Eiffel introduces for this purpose the '''assignment attempt''' operation, written
-x ?= y
+ x ?= y
-with the following effect (only applicable if x is a writable entity of reference type):
+with the following effect (only applicable if x is a writable entity of reference type):
-If y is attached, at the time of the instruction's execution to an object whose type conforms to the type of x , perform a normal reference assignment.
+If y is attached, at the time of the instruction's execution to an object whose type conforms to the type of x, perform a normal reference assignment.
-Otherwise (if y is void, or attached to a non-conforming object), make x void.
+Otherwise (if y is void, or attached to a non-conforming object), make x void.
Using this mechanism, a typical object structure retrieval will be of the form
-x ?= retrieved
+ x ?= retrieved
-if x = Void then
- "We did not get what we expected"
-else
- "Proceed with normal computation, which will typically involve calls of the form x.some_feature "
-end
+ if x = Void then
+ "We did not get what we expected"
+ else
+ "Proceed with normal computation, which will typically involve calls of the form x.some_feature "
+ end
-As another application, assume we have a LIST [ACCOUNT] and class SAVINGS_ACCOUNT , a descendant of ACCOUNT , has a feature interest_rate which was not in ACCOUNT . We want to find the maximum interest rate for savings accounts in the list. Assignment attempt easily solves the problem:
+As another application, assume we have a LIST [ACCOUNT] and class SAVINGS_ACCOUNT, a descendant of ACCOUNT, has a feature interest_rate which was not in ACCOUNT. We want to find the maximum interest rate for savings accounts in the list. Assignment attempt easily solves the problem:
-local
- s: SAVINGS_ACCOUNT
-do
- from account_list.start until account_list.after loop
- s ?= acc_list.item
- -- item from LIST yields the element at
- -- cursor position
- if s /= Void and then s.interest_rate > Result then
- -- Using and then (rather than and) guarantees
- -- that s.interest_rate is not evaluated
- -- if s = Void is true.
- Result := s.interest_rate
- end
- account_list.forth
- end
-end
+ local
+ s: SAVINGS_ACCOUNT
+ do
+ from account_list.start until account_list.after loop
+ s ?= acc_list.item
+ -- item from LIST yields the element at
+ -- cursor position
+ if s /= Void and then s.interest_rate > Result then
+ -- Using and then (rather than and) guarantees
+ -- that s.interest_rate is not evaluated
+ -- if s = Void is true.
+ Result := s.interest_rate
+ end
+ account_list.forth
+ end
+ end
Note that if there is no savings account at all in the list the assignment attempt will always yield void, so that the result of the function will be 0, the default initialization.
@@ -712,73 +740,75 @@ Assignment attempt is useful in the cases cited -- access to external objects be
The final property of Eiffel inheritance involves the rules for adapting not only the implementation of inherited features (through redeclaration of either kind, redeclaration and redefinition, as seen so far) and their contracts (through the Assertion Redeclaration rule), but also their types. More general than type is the notion of a feature's '''signature''', defined by the number of its arguments, their types, the indication of whether it has a result (that is to say, is a function or attribute rather than a procedure) and, if so, the type of the result.
-In many cases the signature of a redeclared feature remains the same as the original's. But in some cases you may want to adapt it to the new class. Assume for example that class ACCOUNT has features
+In many cases the signature of a redeclared feature remains the same as the original's. But in some cases you may want to adapt it to the new class. Assume for example that class ACCOUNT has features
-owner: HOLDER
+ owner: HOLDER
-set_owner (h: HOLDER) is
- -- Make `h' the account owner.
- require
- not_void: h /= Void
- do
- owner := h
- end
+ set_owner (h: HOLDER)
+ -- Make `h' the account owner.
+ require
+ not_void: h /= Void
+ do
+ owner := h
+ end
-We introduce an heir BUSINESS_ACCOUNT of ACCOUNT to represent special business accounts, corresponding to class BUSINESS inheriting from HOLDER :
- [[Image:tutorial-14]]
-Clearly, we must redefine owner in class BUSINESS_ACCOUNT to yield a result of type BUSINESS ; the same signature redefinition must be applied to the argument of set_owner . This case is typical of the general scheme of signature redefinition: in a descendant, you may need to redefine both results and arguments to types conforming to the originals. This is reflected by a language rule:
+We introduce an heir BUSINESS_ACCOUNT of ACCOUNT to represent special business accounts, corresponding to class BUSINESS inheriting from HOLDER:
+
+[[Image:tutorial-14]]
+
+Clearly, we must redefine owner in class BUSINESS_ACCOUNT to yield a result of type BUSINESS; the same signature redefinition must be applied to the argument of set_owner. This case is typical of the general scheme of signature redefinition: in a descendant, you may need to redefine both results and arguments to types conforming to the originals. This is reflected by a language rule:
{{rule|name=Covariance|text=In a feature redeclaration, both the result type if the feature is a query (attribute or function) and the type of any argument if it is a routine (procedure or function) must conform to the original type as declared in the precursor version. }}
The term "covariance" reflects the property that all types -- those of arguments and those of results -- vary together in the same direction as the inheritance structure.
-If a feature such as set_owner has to be redefined for more than its signature -- to update its implementation or assertions -- the signature redefinition will be explicit. For example set_owner could do more for business owners than it does for ordinary owners. Then the redefinition will be of the form
+If a feature such as set_owner has to be redefined for more than its signature -- to update its implementation or assertions -- the signature redefinition will be explicit. For example set_owner could do more for business owners than it does for ordinary owners. Then the redefinition will be of the form
-set_owner (b: BUSINESS) is
- -- Make b the account owner.
- do
- ... New routine body ...
- end
+ set_owner (b: BUSINESS)
+ -- Make b the account owner.
+ do
+ ... New routine body ...
+ end
-In other cases, however, the body will be exactly the same as in the precursor. Then explicit redefinition would be tedious, implying much text duplication. The mechanism of '''anchored redeclaration''' solves this problem. The original declaration of set_owner in ACCOUNT should be of the form
+In other cases, however, the body will be exactly the same as in the precursor. Then explicit redefinition would be tedious, implying much text duplication. The mechanism of '''anchored redeclaration''' solves this problem. The original declaration of set_owner in ACCOUNT should be of the form
-set_owner (h: like owner) is
- -- Make h the account owner.
- -- The rest as before:
- require
- not_void: h /= Void
- do
- owner := h
- end
+ set_owner (h: like owner)
+ -- Make h the account owner.
+ -- The rest as before:
+ require
+ not_void: h /= Void
+ do
+ owner := h
+ end
-A like ''anchor'' type, known as an anchored type, may appear in any context in which ''anchor'' has a well-defined type; ''anchor'' can be an attribute or function of the enclosing class, or an argument of the enclosing routine. Then, assuming T is the type of ''anchor'', the type like ''anchor'' means the following:
+A like ''anchor'' type, known as an anchored type, may appear in any context in which ''anchor'' has a well-defined type; ''anchor'' can be an attribute or function of the enclosing class, or an argument of the enclosing routine. Then, assuming T is the type of ''anchor'', the type like ''anchor'' means the following:
-In the class in which it appears, like ''anchor'' means the same as T . For example, in set_owner above, the declaration of h has the same effect as if h had been declared of type HOLDER , the type of the anchor owner in class ACCOUNT .
+In the class in which it appears, like ''anchor'' means the same as T. For example, in set_owner above, the declaration of h has the same effect as if h had been declared of type HOLDER, the type of the anchor owner in class ACCOUNT.
-The difference comes in proper descendants: if a type redefinition changes the type of ''anchor'', any entity declared like ''anchor'' will be considered to have been redefined too.
+The difference comes in proper descendants: if a type redefinition changes the type of ''anchor'', any entity declared like ''anchor'' will be considered to have been redefined too.
This means that anchored declaration are a form of of implicit covariant redeclaration.
-In the example, class BUSINESS_ACCOUNT only needs to redefine the type of owner (to BUSINESS ). It doesn't have to redefine set_owner except if it needs to change its implementation or assertions.
+In the example, class BUSINESS_ACCOUNT only needs to redefine the type of owner (to BUSINESS). It doesn't have to redefine set_owner except if it needs to change its implementation or assertions.
-It is possible to use Current as anchor; the declaration like Current denotes a type based on the current class (with the same generic parameters if any). This is in fact a common case; we saw in [[9 Inheritance|"Structural property classes", page 62]] , that it applies in class COMPARABLE to features such as
+It is possible to use Current as anchor; the declaration like Current denotes a type based on the current class (with the same generic parameters if any). This is in fact a common case; we saw in [[9 Inheritance#Structural_property_classes|"Structural property classes"]] , that it applies in class COMPARABLE to features such as
-infix "<" (other: like Current): BOOLEAN is ...
+ infix "<" (other: like Current): BOOLEAN ...
-since we only want to compare two comparable elements of compatible types -- but not, for example, integer and strings, even if both types conform to COMPARABLE . (A "balancing rule" makes it possible, however, to mix the various arithmetic types, consistently with mathematical traditions, in arithmetic expressions such as 3 + 45.82 or boolean expressions such as 3 < 45. 82 .)
+since we only want to compare two comparable elements of compatible types -- but not, for example, integer and strings, even if both types conform to COMPARABLE. (A "balancing rule" makes it possible, however, to mix the various arithmetic types, consistently with mathematical traditions, in arithmetic expressions such as 3 + 45.82 or boolean expressions such as 3 < 45.82.)
-Similarly, class ANY declares procedure copy as
+Similarly, class ANY declares procedure copy as
-copy (other: like Current) is ...
+ copy (other: like Current) ...
-with the argument anchored to the current object. Function clone , for its part, has signature clone (other: ANY): like other , with both argument and result anchored to the argument, so that for any x the type of clone (x) is the same as the type of x .
+with the argument anchored to the current object.
-A final, more application-oriented example of anchoring to Current is the feature merge posited in an earlier example (page [[6 The Dynamic Structure: Execution Model|13]] ) with the signature merge (other: ACCOUNT) . By using instead merge (other: like Current) we can ensure that in any descendant class -- BUSINESS_ACCOUNT , SAVINGS_ACCOUNT , MINOR_ACCOUNT ... -- an account will only be mergeable with another of a compatible type.
+A final, more application-oriented example of anchoring to Current is the feature merge posited in an earlier example (in [[6 The Dynamic Structure: Execution Model|"The Dynamic Structure: Execution Model"]] ) with the signature merge (other: ACCOUNT). By using instead merge (other: like Current) we can ensure that in any descendant class -- BUSINESS_ACCOUNT, SAVINGS_ACCOUNT, MINOR_ACCOUNT ... -- an account will only be mergeable with another of a compatible type.
Covariance makes static type checking more delicate; mechanisms of "system validity" and "catcalls" address the problem, discussed in detail in the book [http://eiffel.com/doc/oosc/ Object-Oriented Software Construction] (see the bibliography).
diff --git a/documentation/current/method/eiffel-tutorial-et/et-other-mechanisms.wiki b/documentation/current/method/eiffel-tutorial-et/et-other-mechanisms.wiki
index 429e6a6b..f93ae567 100644
--- a/documentation/current/method/eiffel-tutorial-et/et-other-mechanisms.wiki
+++ b/documentation/current/method/eiffel-tutorial-et/et-other-mechanisms.wiki
@@ -8,9 +8,9 @@ We now examine a few important mechanisms that complement the preceding picture:
The Eiffel's method obsession with extendibility, reusability and maintainability yields, as has been seen, modular and decentralized architectures, where inter-module coupling is limited to the strictly necessary, interfaces are clearly delimited, and all the temptations to introduce obscure dependencies, in particular global variables, have been removed. There is a need, however, to let various components of a system access common objects, without requiring their routines to pass these objects around as arguments (which would only be slightly better than global variables). For example various classes may need to perform output to a common "console window", represented by a shared object.
-Eiffel addresses this need through an original mechanism that also takes care of another important issue, poorly addressed by many design and programming approaches: initialization. The idea is simple: if instead of do the implementation of an effective routine starts with the keyword once , it will only be executed the first time the routine is called during a system execution (or, in a multi-threaded environment, the first time in each thread), regardless of what the caller was. Subsequent calls from the same caller or others will have no effect; if the routine is a function, it will always return the result computed by the first call -- object if an expanded type, reference otherwise.
+Eiffel addresses this need through an original mechanism that also takes care of another important issue, poorly addressed by many design and programming approaches: initialization. The idea is simple: if instead of do the implementation of an effective routine starts with the keyword once, it will only be executed the first time the routine is called during a system execution (or, in a multi-threaded environment, the first time in each thread), regardless of what the caller was. Subsequent calls from the same caller or others will have no effect; if the routine is a function, it will always return the result computed by the first call -- object if an expanded type, reference otherwise.
-In the case of procedures, this provides a convenient initialization mechanism. A delicate problem in the absence of a once mechanism is how to provide the users of a library with a set of routines which they can call in any order, but which all need, to function properly, the guarantee that some context had been properly set up. Asking the library clients to precede the first call with a call to an initialization procedure setup is not only user-unfriendly but silly: in a well-engineered system we will want to check proper set-up in every of the routines, and report an error if necessary; but then if we were able to detect improper set-up we might as well shut up and set up ourselves (by calling setup ). This is not easy, however, since the object on which we call setup must itself be properly initialized, so we are only pushing the problem further. Making setup a once procedure solves it: we can simply include a call
+In the case of procedures, this provides a convenient initialization mechanism. A delicate problem in the absence of a once mechanism is how to provide the users of a library with a set of routines which they can call in any order, but which all need, to function properly, the guarantee that some context had been properly set up. Asking the library clients to precede the first call with a call to an initialization procedure setup is not only user-unfriendly but silly: in a well-engineered system we will want to check proper set-up in every of the routines, and report an error if necessary; but then if we were able to detect improper set-up we might as well shut up and set up ourselves (by calling setup). This is not easy, however, since the object on which we call setup must itself be properly initialized, so we are only pushing the problem further. Making setup a once procedure solves it: we can simply include a call
setup
@@ -26,11 +26,11 @@ console: WINDOW is
end
-Whatever client first calls this function will create the appropriate window and return a reference to it. Subsequent calls, from anywhere in the system, will return that same reference. The simplest way to make this function available to a set of classes is to include it in a class SHARED_STRUCTURES which the classes needing a set of related shared objects will simply inherit.
+Whatever client first calls this function will create the appropriate window and return a reference to it. Subsequent calls, from anywhere in the system, will return that same reference. The simplest way to make this function available to a set of classes is to include it in a class SHARED_STRUCTURES which the classes needing a set of related shared objects will simply inherit.
-For the classes using it, console , although a function, looks very much as if it were an attribute -- only one referring to a shared object.
+For the classes using it, console, although a function, looks very much as if it were an attribute -- only one referring to a shared object.
-The "Hello World" system at the beginning of this discussion (section [[4 Hello World|4]] ) used an output instruction of the form io . put_string ( Some string ) . This is another example of the general scheme illustrated by console . Feature io , declared in ANY and hence usable by all classes, is a once function that returns an object of type STANDARD_FILES (another Kernel Library class) providing access to basic input and output features, one of which is procedure put_string . Because basic input and output must all work on the same files, io should clearly be a once function, shared by all classes that need these mechanisms.
+The "Hello World" system at the beginning of this discussion (section [[4 Hello World|4]] ) used an output instruction of the form io. put_string ( Some string ). This is another example of the general scheme illustrated by console. Feature io, declared in ANY and hence usable by all classes, is a once function that returns an object of type STANDARD_FILES (another Kernel Library class) providing access to basic input and output features, one of which is procedure put_string. Because basic input and output must all work on the same files, io should clearly be a once function, shared by all classes that need these mechanisms.
==Constant and unique attributes==
@@ -43,34 +43,34 @@ Solar_system_planet_count: INTEGER is 9
These will have the same value for every instance and hence do not need to occupy any space in objects at execution time. (In other approaches similar needs would be addressed by symbolic constants, as in Pascal or Ada, or macros, as in C.)
-What comes after the is is a manifest constant: a self-denoting value of the appropriate type. Manifest constants are available for integers, reals (also used for doubles), booleans ( True and False ), characters (in single quotes, as 'A' , with special characters expressed using a percent sign as in '%N' for new line, '%B' for backspace and '%U' for null).
+What comes after the is is a manifest constant: a self-denoting value of the appropriate type. Manifest constants are available for integers, reals (also used for doubles), booleans ( True and False), characters (in single quotes, as 'A', with special characters expressed using a percent sign as in '%N' for new line, '%B' for backspace and '%U' for null).
For integer constants, it is also possible to avoid specifying the values. A declaration of the form
a, b, c, ... n : INTEGER is unique
-introduces a , b , c , ... n as constant integer attributes, whose value are assigned by the Eiffel compiler rather than explicitly by the programmer. The values are different for all unique attributes in a system; they are all positive, and, in a single declaration such as the above, guaranteed to be consecutive (so that you may use an invariant property of the form code > a and code < n to express that code should be one of the values). This mechanism replaces the "enumerated types" found in many languages, without suffering from the same problems. (Enumerated types have an ill-defined place in the type system; and it is not clear what operations are permitted.)
+introduces a, b, c, ... n as constant integer attributes, whose value are assigned by the Eiffel compiler rather than explicitly by the programmer. The values are different for all unique attributes in a system; they are all positive, and, in a single declaration such as the above, guaranteed to be consecutive (so that you may use an invariant property of the form code > a and code < n to express that code should be one of the values). This mechanism replaces the "enumerated types" found in many languages, without suffering from the same problems. (Enumerated types have an ill-defined place in the type system; and it is not clear what operations are permitted.)
-You may use Unique values in conjunction with the inspect multi-branch instruction studied in the next section. They are only appropriate for codes that can take on a fixed number of well-defined values -- not as a way to program operations with many variants, a need better addressed by the object-oriented technique studied earlier and relying on inheritance, polymorphism, redeclaration and dynamic binding.
+You may use Unique values in conjunction with the inspect multi-branch instruction studied in the next section. They are only appropriate for codes that can take on a fixed number of well-defined values -- not as a way to program operations with many variants, a need better addressed by the object-oriented technique studied earlier and relying on inheritance, polymorphism, redeclaration and dynamic binding.
Manifest constants are also available for strings, using double quotes as in
User_friendly_error_message: STRING is "Go get a life !"
-with special characters again using the % codes. It is also possible to declare manifest arrays using double angle brackets:
+with special characters again using the % codes. It is also possible to declare manifest arrays using double angle brackets:
<<1, 2, 3, 5, 7, 11, 13, 17, 19>>
-which is an expression of type ARRAY [INTEGER] . Manifest arrays and strings are not atomic, but denote instances of the Kernel Library classes STRING and ARRAY , as can be produced by once functions.
+which is an expression of type ARRAY [INTEGER]. Manifest arrays and strings are not atomic, but denote instances of the Kernel Library classes STRING and ARRAY, as can be produced by once functions.
==Instructions==
Eiffel has a remarkably small set of instructions. The basic computational instructions have been seen: creation, assignment, assignment attempt, procedure call, retry. They are complemented by control structures: conditional, multi-branch, loop, as well as debug and check.
-A conditional instruction has the form if ... then ... elseif ... then ... else ... end . The elseif ... then ... part (of which there may be more than one) and the else ... part are optional. After if and elseif comes a boolean expression; after then , elseif and else come zero or more instructions.
+A conditional instruction has the form if ... then ... elseif ... then ... else ... end. The elseif ... then ... part (of which there may be more than one) and the else ... part are optional. After if and elseif comes a boolean expression; after then, elseif and else come zero or more instructions.
A multi-branch instruction has the form
@@ -86,11 +86,11 @@ else
end
-where the else ''inst0'' part is optional, exp is a character or integer expression, ''v1'', ''v1'', ... are constant values of the same type as exp , all different, and ''inst0'', ''inst1'', ''inst2'', ... are sequences of zero or more instructions. In the integer case, it is often convenient to use unique values ( [[10 Other Mechanisms|"Constant and unique attributes", page 83]] ) for the ''vi''
+where the else ''inst0'' part is optional, exp is a character or integer expression, ''v1'', ''v1'', ... are constant values of the same type as exp, all different, and ''inst0'', ''inst1'', ''inst2'', ... are sequences of zero or more instructions. In the integer case, it is often convenient to use unique values ( [[10 Other Mechanisms|"Constant and unique attributes", page 83]] ) for the ''vi''
-The effect of such a multi-branch instruction, if the value of exp is one of the ''vi'', is to execute the corresponding ''insti''. If none of the ''vi'' matches, the instruction executes ''inst0'', unless there is no else part, in which case it triggers an exception.
+The effect of such a multi-branch instruction, if the value of exp is one of the ''vi'', is to execute the corresponding ''insti''. If none of the ''vi'' matches, the instruction executes ''inst0'', unless there is no else part, in which case it triggers an exception.
-{{note|Raising an exception is the proper behavior, since the absence of an else indicates that the author asserts that one of the values will match. If you want an instruction that does nothing in this case, rather than cause an exception, use an else part with an empty ''inst0''. In contrast, if c then ''inst'' end with no else part does nothing in the absence of an else part, since in this case there is no implied claim that c must hold. }}
+{{note|Raising an exception is the proper behavior, since the absence of an else indicates that the author asserts that one of the values will match. If you want an instruction that does nothing in this case, rather than cause an exception, use an else part with an empty ''inst0''. In contrast, if c then ''inst'' end with no else part does nothing in the absence of an else part, since in this case there is no implied claim that c must hold. }}
The loop construct has the form
@@ -107,17 +107,17 @@ loop
end
-where the invariant ''inv'' and variant ''var'' parts are optional, the others required. ''initialization'' and ''body'' are sequences of zero or more instructions; ''exit'' and ''inv'' are boolean expressions (more precisely, ''inv'' is an assertion); ''var'' is an integer expression.
+where the invariant ''inv'' and variant ''var'' parts are optional, the others required. ''initialization'' and ''body'' are sequences of zero or more instructions; ''exit'' and ''inv'' are boolean expressions (more precisely, ''inv'' is an assertion); ''var'' is an integer expression.
-The effect is to execute ''initialization'', then, zero or more times until ''exit'' is satisfied, to execute ''body''. (If after ''initialization'' the value of ''exit'' is already true, ''body'' will not be executed at all.) Note that the syntax of loops always includes an initialization, as most loops require some preparation. If not, just leave ''initialization''> empty, while including the from since it is a required component.
+The effect is to execute ''initialization'', then, zero or more times until ''exit'' is satisfied, to execute ''body''. (If after ''initialization'' the value of ''exit'' is already true, ''body'' will not be executed at all.) Note that the syntax of loops always includes an initialization, as most loops require some preparation. If not, just leave ''initialization''> empty, while including the from since it is a required component.
The assertion ''inv'', if present, expresses a '''loop invariant''' (not to be confused with class invariants). For the loop to be correct, ''initialization'' must ensure ''inv'', and then every iteration of ''body'' executed when ''exit'' is false must preserve the invariant; so the effect of the loop is to yield a state in which both ''inv'' and ''exit'' are true. The loop must terminate after a finite number of iterations, of course; this can be guaranteed by using a '''loop variant''' ''var''. It must be an integer expression whose value is non-negative after execution of ''initialization'', and decreased by at least one, while remain non-negative, by any execution of ''body'' when ''exit'' is false; since a non-negative integer cannot be decreased forever, this ensures termination. The assertion monitoring mode, if turned on at the highest level, will check these properties of the invariant and variant after initialization and after each loop iteration, triggering an exception if the invariant does not hold or the variant is negative or does not decrease.
-An occasionally useful instruction is debug ( ''Debug_key'', ... ) ''instructions'' end where ''instructions'' is a sequence of zero or more instructions and the part in parentheses is optional, containing if present one or more strings, called debug keys. The EiffelStudio compiler lets you specify the corresponding debug compilation option: yes , no , or an explicit debug key. The ''instructions''will be executed if and only if the corresponding option is on. The obvious use is for instructions that should be part of the system but executed only in some circumstances, for example to provide extra debugging information.
+An occasionally useful instruction is debug (''Debug_key'', ... ) ''instructions'' end where ''instructions'' is a sequence of zero or more instructions and the part in parentheses is optional, containing if present one or more strings, called debug keys. The EiffelStudio compiler lets you specify the corresponding debug compilation option: yes, no, or an explicit debug key. The ''instructions''will be executed if and only if the corresponding option is on. The obvious use is for instructions that should be part of the system but executed only in some circumstances, for example to provide extra debugging information.
-The final instruction is connected with Design by Contract. The instruction check ''Assertion'' end , where ''Assertion'' is a sequence of zero or more assertions, will have no effect unless assertion monitoring is turned on at the Check level or higher. If so it will evaluate all the assertions listed, having no further effect if they are all satisfied; if any one of them does not hold, the instruction will trigger an exception.
+The final instruction is connected with Design by Contract. The instruction check ''Assertion'' end, where ''Assertion'' is a sequence of zero or more assertions, will have no effect unless assertion monitoring is turned on at the Check level or higher. If so it will evaluate all the assertions listed, having no further effect if they are all satisfied; if any one of them does not hold, the instruction will trigger an exception.
-This instruction serves to state properties that are expected to be satisfied at some stages of the computation -- other than the specific stages, such as routine entry and exit, already covered by the other assertion mechanisms such as preconditions, postconditions and invariants. A recommended use of check involves calling a routine with a precondition, where the call, for good reason, does not explicitly test for the precondition. Consider a routine of the form
+This instruction serves to state properties that are expected to be satisfied at some stages of the computation -- other than the specific stages, such as routine entry and exit, already covered by the other assertion mechanisms such as preconditions, postconditions and invariants. A recommended use of check involves calling a routine with a precondition, where the call, for good reason, does not explicitly test for the precondition. Consider a routine of the form
r (ref: SOME_REFERENCE_TYPE) is
require
@@ -128,12 +128,12 @@ r (ref: SOME_REFERENCE_TYPE) is
end
-Because of the call to some_feature , the routine will only work if its precondition is satisfied on entry. To guarantee this precondition, the caller may protect it by the corresponding test, as in
+Because of the call to some_feature, the routine will only work if its precondition is satisfied on entry. To guarantee this precondition, the caller may protect it by the corresponding test, as in
if x /= Void then a.r (x) end
-but this is not the only possible scheme; for example if an create x appears shortly before the call we know x is not void and do not need the protection. It is a good idea in such cases to use a check instruction to document this property, if only to make sure that a reader of the code will realize that the omission of an explicit test (justified or not) was not a mistake. This is particularly appropriate if the justification for not testing the precondition is less obvious. For example x could have been obtained, somewhere else in the algorithm, as clone (y) for some y that you know is not void. You should document this knowledge by writing the call as
+but this is not the only possible scheme; for example if an create x appears shortly before the call we know x is not void and do not need the protection. It is a good idea in such cases to use a check instruction to document this property, if only to make sure that a reader of the code will realize that the omission of an explicit test (justified or not) was not a mistake. This is particularly appropriate if the justification for not testing the precondition is less obvious. For example x could have been obtained, somewhere else in the algorithm, as clone (y) for some y that you know is not void. You should document this knowledge by writing the call as
check
x_not_void: x /= Void end
@@ -143,7 +143,7 @@ end
a.r (x)
-{{recommended|An extra indentation of the check part to separate it from the algorithm proper; and inclusion of a comment listing the rationale behind the developer's decision not to check explicitly for the precondition. }}
+{{recommended|An extra indentation of the check part to separate it from the algorithm proper; and inclusion of a comment listing the rationale behind the developer's decision not to check explicitly for the precondition. }}
In production mode with assertion monitoring turned off, this instruction will have no effect. But it will be precious for a maintainer of the software who is trying to figure out what it does, and in the process to reconstruct the original developer's reasoning. (The maintainer might of course be the same person as the developer, six months later.) And if the rationale is wrong somewhere, turning assertion checking on will immediately uncover the bug.
@@ -153,7 +153,7 @@ One of the conditions for producing truly great reusable software is to recogniz
This raises the issue of backward compatibility: how to move forward with a better design, without compromising existing applications that used the previous version?
-The notion of obsolete class and feature helps address this issue. By declaring a feature as obsolete , using the syntax
+The notion of obsolete class and feature helps address this issue. By declaring a feature as obsolete, using the syntax
enter (i: INTEGER; x: G) is
obsolete
@@ -165,18 +165,18 @@ enter (i: INTEGER; x: G) is
end
-you state that you are now advising against using it, and suggest a replacement through the message that follows the keyword obsolete , a mere string. The obsolete feature is still there, however; using it will cause no other harm than a warning message when someone compiles a system that includes a call to it. Indeed, you don't want to hold a gun to your client authors' forehead (''"Upgrade now or die !"''); but you do want to let them know that there is a new version and that they should upgrade at their leisure.
+you state that you are now advising against using it, and suggest a replacement through the message that follows the keyword obsolete, a mere string. The obsolete feature is still there, however; using it will cause no other harm than a warning message when someone compiles a system that includes a call to it. Indeed, you don't want to hold a gun to your client authors' forehead (''"Upgrade now or die !"''); but you do want to let them know that there is a new version and that they should upgrade at their leisure.
Besides routines, you may also mark classes as obsolete.
-The example above is a historical one, involving an early change of interface for the EiffelBase library class ARRAY ; the change affected both the feature's name, with a new name ensuring better consistency with other classes, and the order of arguments, again for consistency. It shows the recommended style for using obsolete :
+The example above is a historical one, involving an early change of interface for the EiffelBase library class ARRAY; the change affected both the feature's name, with a new name ensuring better consistency with other classes, and the order of arguments, again for consistency. It shows the recommended style for using obsolete:
* In the message following the keyword, explain the recommended replacement. This message will be part of the warning produced by the compiler for a system that includes the obsolete element.
* In the body of the routine, it is usually appropriate, as here, to replace the original implementation by a call to the new version. This may imply a small performance overhead, but simplifies maintenance and avoids errors.
It is good discipline not to let obsolete elements linger around for too long. The next major new release, after a suitable grace period, should remove them.
-The design flexibility afforded by the obsolete keyword is critical to ensure the harmonious long-term development of ambitious reusable software.
+The design flexibility afforded by the obsolete keyword is critical to ensure the harmonious long-term development of ambitious reusable software.
==Creation variants==
@@ -186,12 +186,12 @@ create x.make (2000)
create x
-the first one if the corresponding class has a create clause, the second one if not. In either form you may include a type name in braces, as in
+the first one if the corresponding class has a create clause, the second one if not. In either form you may include a type name in braces, as in
create {SAVINGS_ACCOUNT} x.make (2000)
-which is valid only if the type listed, here SAVINGS_ACCOUNT , conforms to the type of x , assumed here to be ACCOUNT . This avoids introducing a local entity, as in
+which is valid only if the type listed, here SAVINGS_ACCOUNT, conforms to the type of x, assumed here to be ACCOUNT. This avoids introducing a local entity, as in
local
xs: SAVINGS_ACCOUNT
@@ -216,16 +216,16 @@ do
...
-Unlike creation instructions, creation expressions must always list the type explicitly, {ACCOUNT} in the example. They are useful in the case shown: creating an object that only serves as an argument to be passed to a routine. If you need to retain access to the object through an entity, the instruction create x ... is the appropriate construct.
+Unlike creation instructions, creation expressions must always list the type explicitly, {ACCOUNT} in the example. They are useful in the case shown: creating an object that only serves as an argument to be passed to a routine. If you need to retain access to the object through an entity, the instruction create x ... is the appropriate construct.
-The creation mechanism gets an extra degree of flexibility through the notion of default_create . The simplest form of creation instruction, create x without an explicit creation procedure, is actually an abbreviation for create x.default_create , where default_create is a procedure defined in class ANY to do nothing. By redefining default_create in one of your classes, you can ensure that create x will take care of non-default initialization (and ensure the invariant if needed). When a class has no create clause, it's considered to have one that lists only default_create . If you want to allow create x as well as the use of some explicit creation procedures, simply list default_create along with these procedures in the create clause. To disallow creation altogether, include an empty create clause, although this technique is seldom needed since most non-creatable classes are deferred, and one can't instantiate a deferred class.
+The creation mechanism gets an extra degree of flexibility through the notion of default_create. The simplest form of creation instruction, create x without an explicit creation procedure, is actually an abbreviation for create x.default_create, where default_create is a procedure defined in class ANY to do nothing. By redefining default_create in one of your classes, you can ensure that create x will take care of non-default initialization (and ensure the invariant if needed). When a class has no create clause, it's considered to have one that lists only default_create. If you want to allow create x as well as the use of some explicit creation procedures, simply list default_create along with these procedures in the create clause. To disallow creation altogether, include an empty create clause, although this technique is seldom needed since most non-creatable classes are deferred, and one can't instantiate a deferred class.
-One final twist is the mechanism for creating instances of formal generic parameters. For x of type G in a class C [G] , it wouldn't be safe to allow create x , since G stands for many possible types, all of which may have their own creation procedures. To allow such creation instructions, we rely on constrained genericity. You may declare a class as
+One final twist is the mechanism for creating instances of formal generic parameters. For x of type G in a class C [G], it wouldn't be safe to allow create x, since G stands for many possible types, all of which may have their own creation procedures. To allow such creation instructions, we rely on constrained genericity. You may declare a class as
[G -> T create cp end]
-to make G constrained by T , as we learned before, and specify that any actual generic parameter must have cp among its creation procedures. Then it's permitted to use create x.cp , with arguments if required by cp , since it is guaranteed to be safe. The mechanism is very general since you may use ANY for T and default_create for cp . The only requirement on cp is that it must be a procedure of T , not necessarily a creation procedure; this permits using the mechanism even if T is deferred, a common occurrence. It's only descendants of T that must make cp a creation procedure, by listing it in the create clause, if they want to serve as actual generic parameters for C .
+to make G constrained by T, as we learned before, and specify that any actual generic parameter must have cp among its creation procedures. Then it's permitted to use create x.cp, with arguments if required by cp, since it is guaranteed to be safe. The mechanism is very general since you may use ANY for T and default_create for cp. The only requirement on cp is that it must be a procedure of T, not necessarily a creation procedure; this permits using the mechanism even if T is deferred, a common occurrence. It's only descendants of T that must make cp a creation procedure, by listing it in the create clause, if they want to serve as actual generic parameters for C.
==Tuple types==
@@ -234,24 +234,24 @@ The study of genericity described arrays. Another common kind of container objec
TUPLE [X, Y, Z]
-denoting a tuple of least three elements, such that the type of the first conforms to X , the second to Y , and the third to Z .
+denoting a tuple of least three elements, such that the type of the first conforms to X, the second to Y, and the third to Z.
-You may list any number of types in brackets, including none at all: TUPLE , with no types in brackets, denotes tuples of arbitrary length.
+You may list any number of types in brackets, including none at all: TUPLE, with no types in brackets, denotes tuples of arbitrary length.
-{{info|The syntax, with brackets, is intentionally reminiscent of generic classes, but TUPLE is a reserved word, not the name of a class; making it a class would not work since a generic class has a fixed number of generic parameters. You may indeed use TUPLE to obtain the effect of a generic class with a variable number of parameters. }}
+{{info|The syntax, with brackets, is intentionally reminiscent of generic classes, but TUPLE is a reserved word, not the name of a class; making it a class would not work since a generic class has a fixed number of generic parameters. You may indeed use TUPLE to obtain the effect of a generic class with a variable number of parameters. }}
To write the tuples themselves -- the sequences of elements, instances of a tuple type -- you will also use square brackets; for example
[x1, y1, z1]
-with x1 of type X and so on is a tuple of type TUPLE [X, Y, Z] .
+with x1 of type X and so on is a tuple of type TUPLE [X, Y, Z].
-The definition of tuple types states that TUPLE [X1 ... Xn] denotes sequences of at least n elements, of which the first n have types respectively conforming to X1, ..., Xn . Such a sequence may have more than n elements.
+The definition of tuple types states that TUPLE [X1 ... Xn] denotes sequences of at least n elements, of which the first n have types respectively conforming to X1, ..., Xn. Such a sequence may have more than n elements.
-Features available on tuple types include count: INTEGER , yielding the number of elements in a tuple, item (i: INTEGER): ANY which returns the i -th element, and put which replaces an element.
+Features available on tuple types include count: INTEGER, yielding the number of elements in a tuple, item (i: INTEGER): ANY which returns the i-th element, and put which replaces an element.
-Tuples are appropriate when these are the only operations you need, that is to say, you are using sequences with no further structure or properties. Tuples give you "anonymous classes" with predefined features count , item and put . A typical example is a general-purpose output procedure that takes an arbitrary sequence of values, of arbitrary types, and prints them. It may simply take an argument of type TUPLE , so that clients can call it under the form
+Tuples are appropriate when these are the only operations you need, that is to say, you are using sequences with no further structure or properties. Tuples give you "anonymous classes" with predefined features count, item and put. A typical example is a general-purpose output procedure that takes an arbitrary sequence of values, of arbitrary types, and prints them. It may simply take an argument of type TUPLE, so that clients can call it under the form
write ([ ''your_integer'' , ''your_real'', ''your_account''])