From 39314dc2984d6c408b7cdfcba1699f51d3a9e4a9 Mon Sep 17 00:00:00 2001 From: halw Date: Thu, 23 Oct 2008 14:33:54 +0000 Subject: [PATCH] Author:halw Date:2008-10-22T21:23:04.000000Z git-svn-id: https://svn.eiffel.com/eiffel-org/trunk@94 abb3cda0-5349-4a8f-a601-0c33ac3a8c38 --- .../et-other-mechanisms.wiki | 11 +- .../invitation-eiffel-i2e/i2e-classes.wiki | 78 +++++----- ...-combining-genericity-and-inheritance.wiki | 24 ++-- ...rred-classes-and-seamless-development.wiki | 28 ++-- .../i2e-design-contract-and-assertions.wiki | 134 +++++++++--------- ...e-event-driven-programming-and-agents.wiki | 20 +-- .../invitation-eiffel-i2e/i2e-exceptions.wiki | 44 +++--- .../invitation-eiffel-i2e/i2e-genericity.wiki | 18 +-- .../i2e-inheritance.wiki | 12 +- .../i2e-polymorphism-and-dynamic-binding.wiki | 34 ++--- .../i2e-putting-system-together.wiki | 5 +- .../invitation-eiffel-i2e/i2e-types.wiki | 8 +- 12 files changed, 206 insertions(+), 210 deletions(-) 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 b6f51202..9bddd156 100644 --- a/documentation/current/method/eiffel-tutorial-et/et-other-mechanisms.wiki +++ b/documentation/current/method/eiffel-tutorial-et/et-other-mechanisms.wiki @@ -32,7 +32,7 @@ For the classes using it, console, although a function, looks very 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== +==Constant attributes== The attributes studied earlier were variable: each represents a field present in each instance of the class and changeable by its routines. @@ -45,15 +45,6 @@ These will have the same value for every instance and hence do not need to occup What comes after the = 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 = 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.) - -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 !" diff --git a/documentation/current/method/invitation-eiffel-i2e/i2e-classes.wiki b/documentation/current/method/invitation-eiffel-i2e/i2e-classes.wiki index d3f2ea34..a856e72c 100644 --- a/documentation/current/method/invitation-eiffel-i2e/i2e-classes.wiki +++ b/documentation/current/method/invitation-eiffel-i2e/i2e-classes.wiki @@ -20,19 +20,20 @@ The term "entity" generalizes the more common notion of "variable". An entity de An entity is said to be void if it is not attached to any object. By default, entities are void at initialization. To obtain objects at run-time, a routine r appearing in the client class X may use a '''creation instruction''' of the form - create acc + create acc which creates a new direct instance of ACCOUNT, attaches acc to that instance, and initializes all its fields to default values. A variant of this notation, studied below, makes it possible to override the default initializations. Once the client has attached acc to an object, it may call on this object the features defined in class ACCOUNT. Here is an extract with some feature calls using acc as their target: - acc.open ("Jill") - acc.deposit (5000) - if acc.may_withdraw (3000) then - acc.withdraw (3000) - print (acc.balance) - end + acc.open ("Jill") + acc.deposit (5000) + if acc.may_withdraw (3000) then + acc.withdraw (3000) + print (acc.balance) + end + These feature calls use dot notation, of the form target_name.feature_name, possibly followed by a list of arguments in parentheses. Features are of two kinds: * '''Routines''', such as open, deposit, may_withdraw, withdraw, represent computations applicable to instances of the class. @@ -44,47 +45,48 @@ Routines are further divided into '''procedures''' (commands, which do not retur In class ACCOUNT, is feature balance an attribute, or is it a function with no argument? The above extract of the client class X doesn't say, and this ambiguity is intentional. A client of ACCOUNT must not need to know how class ACCOUNT delivers an account's balance when requested: by looking up a field present in each account object, or by calling a function that computes the balance from other fields. Choosing between these techniques is the business of class ACCOUNT, not anybody else's. Because such implementation choices are often changed over the lifetime of a project, it is essential to protect clients against their effects. This is known as the '''Uniform Access Principle''', stating that the choice between representing a property through memory (an attribute) or through an algorithm (function) shall not affect how clients use it. -So much for how client classes will typically use ACCOUNT. Below is a first sketch of how class ACCOUNT itself might look. Line segments beginning with -- are comments. The class includes two feature clauses, introducing its features. The first begins with just the keyword feature, without further qualification; this means that the features declared in this clause are available (or "exported") to all clients of the class. The second clause is introduced by feature { NONE} to indicate that the feature that follows, called add, is available to no client. What appears between the braces is a list of client classes to which the corresponding features are available; NONE is a special class of the Kernel Library, which has no instances, so that add is in effect a secret feature, available only locally to the other routines of class ACCOUNT. So in a client class such as X, the call acc.add ( -3000 ) would be invalid. +So much for how client classes will typically use ACCOUNT. Below is a first sketch of how class ACCOUNT itself might look. Line segments beginning with -- are comments. The class includes two feature clauses, introducing its features. The first begins with just the keyword feature, without further qualification; this means that the features declared in this clause are available (or "exported") to all clients of the class. The second clause is introduced by feature {NONE} to indicate that the feature that follows, called add, is available to no client. What appears between the braces is a list of client classes to which the corresponding features are available; NONE is a special class of the Kernel Library, which has no instances, so that add is in effect a secret feature, available only locally to the other routines of class ACCOUNT. So in a client class such as X, the call acc.add ( -3000 ) would be invalid. -class ACCOUNT +class + ACCOUNT feature - balance: INTEGER - owner: PERSON - minimum_balance: INTEGER = 1000 + balance: INTEGER + owner: PERSON + minimum_balance: INTEGER = 1000 - open (who: PERSON) - -- Assign the account to owner who. - do - owner := who - end + open (who: PERSON) + -- Assign the account to owner who. + do + owner := who + end - deposit (sum: INTEGER) - -- Deposit sum into the account. - do - add (sum) - end + deposit (sum: INTEGER) + -- Deposit sum into the account. + do + add (sum) + end - withdraw (sum: INTEGER) - -- Withdraw sum from the account. - do - add (-sum) - end + withdraw (sum: INTEGER) + -- Withdraw sum from the account. + do + add (-sum) + end - may_withdraw (sum: INTEGER): BOOLEAN - -- Is there enough money to withdraw sum? - do - Result := (balance >= sum + minimum_balance) - end + may_withdraw (sum: INTEGER): BOOLEAN + -- Is there enough money to withdraw sum? + do + Result := (balance >= sum + minimum_balance) + end feature {NONE} - add (sum: INTEGER) - -- Add sum to the balance - do - balance := balance + sum - end + add (sum: INTEGER) + -- Add sum to the balance + do + balance := balance + sum + end end -- ACCOUNT @@ -93,7 +95,7 @@ Let us examine the features in sequence. The do ... INTEGER and REAL, false for BOOLEAN, null character for CHARACTER, and a void reference for reference types. The class designer may also provide clients with different initialization options, as will be seen below in a revised version of this example. -The other public features, withdraw deposit, open, and may_withdraw are straight-forward routines. The special entity Result, used in may_withdraw, denotes the function result; it is initialized on function entry to the default value of the function's result type. You may only use Result in functions. +The other public features, withdraw, deposit, open, and may_withdraw are straight-forward routines. The special entity Result, used in may_withdraw, denotes the function result; it is initialized on function entry to the default value of the function's result type. You may only use Result in functions. The secret procedure add serves for the implementation of the public procedures deposit and withdraw; the designer of ACCOUNT judged it too general to be exported by itself. The clause "= 1000" introduces minimum_balance as a constant attribute, which will not occupy any space in instances of the class; in contrast, every instance has a field for every non-constant attribute such as balance. diff --git a/documentation/current/method/invitation-eiffel-i2e/i2e-combining-genericity-and-inheritance.wiki b/documentation/current/method/invitation-eiffel-i2e/i2e-combining-genericity-and-inheritance.wiki index 59b42304..a967e167 100644 --- a/documentation/current/method/invitation-eiffel-i2e/i2e-combining-genericity-and-inheritance.wiki +++ b/documentation/current/method/invitation-eiffel-i2e/i2e-combining-genericity-and-inheritance.wiki @@ -4,31 +4,31 @@ [[Property:uuid|912e4c38-9add-e478-59c3-5c10aa75d784]] Genericity and inheritance, the two fundamental mechanisms for generalizing classes, may be combined in two fruitful ways. -The first technique yields '''polymorphic data structures'''. Assume that in the generic class LIST [G] the insertion procedure put has a formal argument of type G , representing the element to be inserted. Then with a declaration such as +The first technique yields '''polymorphic data structures'''. Assume that in the generic class LIST [G] the insertion procedure put has a formal argument of type G, representing the element to be inserted. Then with a declaration such as - pl: LIST [POLYGON] + pl: LIST [POLYGON] -the type rules imply that in a call pl.put ( "p" ) the permitted types for the argument p include not just POLYGON , but also RECTANGLE (an heir of POLYGON ) or any other type conforming to POLYGON through inheritance. +the type rules imply that in a call pl.put ( "p" )the permitted types for the argument p include not just POLYGON, but also RECTANGLE (an heir of POLYGON) or any other type conforming to POLYGON through inheritance. -The basic conformance requirement used here is the inheritance-based type compatibility rule: V conforms to T if V is a descendant of T . +The basic conformance requirement used here is the inheritance-based type compatibility rule: V conforms to T if V is a descendant of T. -Structures such as pl may contain objects of different types, hence the name "polymorphic data structure". Such polymorphism is, again, made safe by the type rules: by choosing an actual generic parameter ( POLYGON in the example) based higher or lower in the inheritance graph, you extend or restrict the permissible types of objects in pl . A fully general list would be declared as +Structures such as pl may contain objects of different types, hence the name "polymorphic data structure". Such polymorphism is, again, made safe by the type rules: by choosing an actual generic parameter ( POLYGON in the example) based higher or lower in the inheritance graph, you extend or restrict the permissible types of objects in pl. A fully general list would be declared as - LIST [ANY] + LIST [ANY] -where ANY , a Kernel Library class, is automatically an ancestor of any class that you may write. +where ANY, a Kernel Library class, is automatically an ancestor of any class that you may write. The other mechanism for combining genericity and inheritance is '''constrained genericity'''. By indicating a class name after a formal generic parameter, as in - VECTOR [T -> NUMERIC] + VECTOR [T -> NUMERIC] -you express that only descendants of that class (here NUMERIC ) may be used as the corresponding actual generic parameters. This makes it possible to use the corresponding operations. Here, for example, class VECTOR may define a routine infix "+" for adding vectors, based on the corresponding routine from NUMERIC for adding vector elements. Then by making VECTOR itself inherit from NUMERIC , you ensure that it satisfies its own generic constraint and enable the definition of types such as VECTOR [VECTOR [T]] . +you express that only descendants of that class (here NUMERIC) may be used as the corresponding actual generic parameters. This makes it possible to use the corresponding operations. Here, for example, class VECTOR may define a routine infix "+" for adding vectors, based on the corresponding routine from NUMERIC for adding vector elements. Then by making VECTOR itself inherit from NUMERIC, you ensure that it satisfies its own generic constraint and enable the definition of types such as VECTOR [VECTOR [T]] . -As you have perhaps guessed, unconstrained genericity, as in LIST [G] , may be viewed as an abbreviation for genericity constrained by ANY , as in +As you have perhaps guessed, unconstrained genericity, as in LIST [G] , may be viewed as an abbreviation for genericity constrained by ANY, as in - LIST [G -> ANY] + LIST [G -> ANY] -Something else you may have guessed: if ANY , introduced in this session, is the top of the inheritance structure -- providing all classes with universal features such as equal to compare arbitrary objects and clone to duplicate objects -- then NONE , seen earlier in the notation feature {NONE} , is its bottom. NONE indeed conceptually inherits from all other classes. NONE is, among other things, the type of ensure , the void reference. +Something else you may have guessed: if ANY, introduced in this session, is the top of the inheritance structure -- providing all classes with universal features such as equal to compare arbitrary objects and clone to duplicate objects -- then NONE, seen earlier in the notation feature {NONE}, is its bottom. NONE indeed conceptually inherits from all other classes. NONE is, among other things, the type of ensure, the void reference. diff --git a/documentation/current/method/invitation-eiffel-i2e/i2e-deferred-classes-and-seamless-development.wiki b/documentation/current/method/invitation-eiffel-i2e/i2e-deferred-classes-and-seamless-development.wiki index a3c8b243..adab7596 100644 --- a/documentation/current/method/invitation-eiffel-i2e/i2e-deferred-classes-and-seamless-development.wiki +++ b/documentation/current/method/invitation-eiffel-i2e/i2e-deferred-classes-and-seamless-development.wiki @@ -4,7 +4,7 @@ [[Property:uuid|b3264238-f160-a6fc-0b03-adcd80b1f55a]] The inheritance mechanism includes one more major notion: deferred features and classes. -Declaring a feature f as deferred in a class C expresses that there is no default implementation of f in C ; such implementations will appear in eventual descendants of C . A class that has one or more deferred routines is itself said to be deferred. A non-deferred routine or class -- like all those seen until now -- is said to be '''effective'''. +Declaring a feature f as deferred in a class C expresses that there is no default implementation of f in C; such implementations will appear in eventual descendants of C. A class that has one or more deferred routines is itself said to be deferred. A non-deferred routine or class -- like all those seen until now -- is said to be '''effective'''. For example, a system used by a Department of Motor Vehicles to register vehicles might include a class of the form @@ -41,19 +41,19 @@ This example assumes that no single registration algorithm applies to all kinds Whereas an effective class described an implementation of an abstract data types, a deferred class describes a set of possible implementations. You may not instantiate a deferred class: create v is invalid if v is declared of type VEHICLE. But you may assign to v a reference to an instance of an effective descendant of VEHICLE. For example, assuming CAR and TRUCK provide effective definitions for all deferred routines of VEHICLE, the following will be valid: - v: VEHICLE - c: CAR - t: TRUCK - ... - create c - create t - ... - if "Some test" then - v := c - else - v := t - end - v.register (2008) + v: VEHICLE + c: CAR + t: TRUCK + ... + create c + create t + ... + if "Some test" then + v := c + else + v := t + end + v.register (2008) This example fully exploits polymorphism: depending on the outcome of "Some test", v will be treated as a car or a truck, and the appropriate registration algorithm will be applied. Also, "Some test" may depend on some event whose outcome is impossible to predict until run-time, for example the user clicking with the mouse to select one among several vehicle icons displayed on the screen. diff --git a/documentation/current/method/invitation-eiffel-i2e/i2e-design-contract-and-assertions.wiki b/documentation/current/method/invitation-eiffel-i2e/i2e-design-contract-and-assertions.wiki index 09e5f877..c78dc02b 100644 --- a/documentation/current/method/invitation-eiffel-i2e/i2e-design-contract-and-assertions.wiki +++ b/documentation/current/method/invitation-eiffel-i2e/i2e-design-contract-and-assertions.wiki @@ -7,90 +7,92 @@ If classes are to deserve their definition as abstract data type implementations ==The role of assertions== Eiffel encourages software developers to express formal properties of classes by writing '''assertions''', which may in particular appear in the following roles:
-* Routine '''preconditions''' express the requirements that clients must satisfy whenever they call a routine. For example the designer of ACCOUNT may wish to permit a withdrawal operation only if it keeps the account's balance at or above the minimum. Preconditions are introduced by the keyword require . -* Routine '''postconditions''', introduced by the keyword ensure , express conditions that the routine (the supplier) guarantees on return, if the precondition was satisfied on entry. -* A class '''invariant''' must be satisfied by every instance of the class whenever the instance is externally accessible: after creation, and after any call to an exported routine of the class. The invariant appears in a clause introduced by the keyword invariant , and represents a general consistency constraint imposed on all routines of the class. +* Routine '''preconditions''' express the requirements that clients must satisfy whenever they call a routine. For example the designer of ACCOUNT may wish to permit a withdrawal operation only if it keeps the account's balance at or above the minimum. Preconditions are introduced by the keyword require. +* Routine '''postconditions''', introduced by the keyword ensure, express conditions that the routine (the supplier) guarantees on return, if the precondition was satisfied on entry. +* A class '''invariant''' must be satisfied by every instance of the class whenever the instance is externally accessible: after creation, and after any call to an exported routine of the class. The invariant appears in a clause introduced by the keyword invariant, and represents a general consistency constraint imposed on all routines of the class. -With appropriate assertions, the class ACCOUNT becomes: +With appropriate assertions, the class ACCOUNT becomes: -class ACCOUNT +class + ACCOUNT + create - make + make feature - ... Attributes as before: - balance , minimum_balance , owner , open ... + ... Attributes as before: + balance , minimum_balance , owner , open ... - deposit (sum: INTEGER) - -- Deposit sum into the account. - require - sum >= 0 - do - add (sum) - ensure - balance = old balance + sum - end + deposit (sum: INTEGER) + -- Deposit sum into the account. + require + sum >= 0 + do + add (sum) + ensure + balance = old balance + sum + end - withdraw (sum: INTEGER) - -- Withdraw sum from the account. - require - sum >= 0 - sum <= balance - minimum_balance - do - add (-sum) - ensure - balance = old balance - sum - end + withdraw (sum: INTEGER) + -- Withdraw sum from the account. + require + sum >= 0 + sum <= balance - minimum_balance + do + add (-sum) + ensure + balance = old balance - sum + end - may_withdraw ... -- As before + may_withdraw ... -- As before feature {NONE} - add ... + add ... - make (initial: INTEGER) - -- Initialize account with balance initial. - require - initial >= minimum_balance - do - balance := initial - end + make (initial: INTEGER) + -- Initialize account with balance initial. + require + initial >= minimum_balance + do + balance := initial + end invariant - balance >= minimum_balance + balance >= minimum_balance end -- ACCOUNT -The notation old expression is only valid in a routine postcondition. It denotes the value the expression had on routine entry. +The notation old expression is only valid in a routine postcondition. It denotes the value the expression had on routine entry. ==Creation procedures== -In its last version above, the class now includes a creation procedure, make . With the first version, clients used creation instructions such as create acc1 to create accounts; but then the default initialization, setting balance to zero, violated the invariant. By having one or more creation procedures, listed in the create clause at the beginning of the class text, a class offers a way to override the default initializations. The effect of +In its last version above, the class now includes a creation procedure, make. With the first version, clients used creation instructions such as create acc1 to create accounts; but then the default initialization, setting balance to zero, violated the invariant. By having one or more creation procedures, listed in the create clause at the beginning of the class text, a class offers a way to override the default initializations. The effect of - create acc1.make (5_500) + create acc1.make (5_500) -is to allocate the object (as with the default creation) and to call procedure make on this object, with the argument given. This call is correct since it satisfies the precondition; it will ensure the invariant. +is to allocate the object (as with the default creation) and to call procedure make on this object, with the argument given. This call is correct since it satisfies the precondition; it will ensure the invariant. {{info|The underscore _ in the integer constant ''5_500'' has no semantic effect. The general rule is that you can group digits by sets of three from the right to improve the readability of integer constants. }} -Note that the same keyword, create , serves both to introduce creation instructions and the creation clause listing creation procedures at the beginning of the class. +Note that the same keyword, create, serves both to introduce creation instructions and the creation clause listing creation procedures at the beginning of the class. -A procedure listed in the creation clause, such as make , otherwise enjoys the same properties as other routines, especially for calls. Here the procedure make is secret since it appears in a clause starting with +A procedure listed in the creation clause, such as make, otherwise enjoys the same properties as other routines, especially for calls. Here the procedure make is secret since it appears in a clause starting with - feature {NONE} +feature {NONE} so it would be invalid for a client to include a call such as - acc.make (8_000) + acc.make (8_000) -To make such a call valid, it would suffice to move the declaration of make to the first feature clause of class ACCOUNT , which carries no export restriction. Such a call does not create any new object, but simply resets the balance of a previously created account. +To make such a call valid, it would suffice to move the declaration of make to the first feature clause of class ACCOUNT, which carries no export restriction. Such a call does not create any new object, but simply resets the balance of a previously created account. ==Design by Contract™== -Syntactically, assertions are boolean expressions, with a few extensions such as the old notation. Also, you may split an assertion into two or more clauses, as here with the precondition of withdraw ; this is as if you had separated the clauses with an and , but makes the assertion clearer, especially if it includes many conditions. +Syntactically, assertions are boolean expressions, with a few extensions such as the old notation. Also, you may split an assertion into two or more clauses, as here with the precondition of withdraw; this is as if you had separated the clauses with an and, but makes the assertion clearer, especially if it includes many conditions. Assertions play a central part in the Eiffel method for building reliable object-oriented software. They serve to make explicit the assumptions on which programmers rely when they write software elements that they believe are correct. Writing assertions amounts to spelling out the terms of the '''contract''' which governs the relationship between a routine and its callers. The precondition binds the callers; the postcondition binds the routine. @@ -100,39 +102,39 @@ The underlying theory of Design by Contract™, the centerpiece of the Eiffel Assertions are also an indispensable tool for the documentation of reusable software components: one cannot expect large-scale reuse without a precise documentation of what every component expects (precondition), what it guarantees in return (postcondition) and what general conditions it maintains (invariant). -Documentation tools in EiffelStudio use assertions to produce information for client programmers, describing classes in terms of observable behavior, not implementation. In particular the '''Contract Form''' of a class, also called its "short form", which serves as its interface documentation, is obtained from the full text by removing all non-exported features and all implementation information such as do clauses of routines, but keeping interface information and in particular assertions. Here is the Contract Form of the above class: +Documentation tools in EiffelStudio use assertions to produce information for client programmers, describing classes in terms of observable behavior, not implementation. In particular the '''Contract Form''' of a class, also called its "short form", which serves as its interface documentation, is obtained from the full text by removing all non-exported features and all implementation information such as do clauses of routines, but keeping interface information and in particular assertions. Here is the Contract Form of the above class: class interface ACCOUNT create - make + make feature - balance: INTEGER - ... + balance: INTEGER + ... - deposit (sum: INTEGER) - -- Deposit sum into the account. - require - sum >= 0 - ensure - balance = old balance + sum + deposit (sum: INTEGER) + -- Deposit sum into the account. + require + sum >= 0 + ensure + balance = old balance + sum - withdraw (sum: INTEGER) - -- Withdraw sum from the account. - require - sum >= 0 - sum <= balance - minimum_balance - ensure - balance = old balance - sum + withdraw (sum: INTEGER) + -- Withdraw sum from the account. + require + sum >= 0 + sum <= balance - minimum_balance + ensure + balance = old balance - sum - may_withdraw ... + may_withdraw ... end -- ACCOUNT -This is not actual Eiffel, only documentation of Eiffel classes, hence the use of slightly different syntax to avoid any confusion ( interface class rather than class ). In accordance with the Uniform Access Principle (page [[4 Classes|7]] ), the output for balance would be the same if this feature were a function rather than an attribute. +This is not actual Eiffel, only documentation of Eiffel classes, hence the use of slightly different syntax to avoid any confusion ( interface class rather than class). In accordance with the Uniform Access Principle (page [[4 Classes|7]] ), the output for balance would be the same if this feature were a function rather than an attribute. You will find in EiffelStudio automatic tools to produce the Contract Form of a class. You can also get the '''Flat Contract''' form, based on the same ideas but including inherited features along with those introduced in the class itself. EiffelStudio can produce these forms, and other documentation views of a class, in a variety of output formats including HTML, so that collaborative projects can automatically post the latest versions of their class interfaces on the Internet or an Intranet. diff --git a/documentation/current/method/invitation-eiffel-i2e/i2e-event-driven-programming-and-agents.wiki b/documentation/current/method/invitation-eiffel-i2e/i2e-event-driven-programming-and-agents.wiki index 2500a93c..193fa6b5 100644 --- a/documentation/current/method/invitation-eiffel-i2e/i2e-event-driven-programming-and-agents.wiki +++ b/documentation/current/method/invitation-eiffel-i2e/i2e-event-driven-programming-and-agents.wiki @@ -9,32 +9,32 @@ In some circumstances it is useful to define an object that denotes an operation "When the user clicks this OK button, the system must update the file" -each involves a '''control''' (here the OK button), an '''event''' (mouse click) and an '''operation''' (update the file). This can be programmed by having an "event loop", triggered for each event, which performs massive decision-making ( if "The latest event was `left mouse click on button 23'" then "Appropriate instructions" else if ... and so on with many branches); but this leads to bulky software architectures where introducing any new control or event requires updating a central part of the code. It's preferable to let any element of the system that encounters a new control-event-operation association +each involves a '''control''' (here the OK button), an '''event''' (mouse click) and an '''operation''' (update the file). This can be programmed by having an "event loop", triggered for each event, which performs massive decision-making (if "The latest event was `left mouse click on button 23'" then "Appropriate instructions" else if ... and so on with many branches); but this leads to bulky software architectures where introducing any new control or event requires updating a central part of the code. It's preferable to let any element of the system that encounters a new control-event-operation association - [control, event, operation] +[control, event, operation] -store it as a triple of objects into an object structure, such as an array or a list. Triples in that structure may come from different parts of the system; there is no central know-it-all structure. The only central element is a simple mechanism which can explore the object structure to execute each operation associated with a certain control and a certain event . The mechanism is not just simple; it's also independent of your application, since it doesn't need to know about any particular control, event or operation (it will find them in the object structure). So it can be programmed once and for all, as part of a library such as EiffelVision 2 for platform-independent graphics. +store it as a triple of objects into an object structure, such as an array or a list. Triples in that structure may come from different parts of the system; there is no central know-it-all structure. The only central element is a simple mechanism which can explore the object structure to execute each operation associated with a certain control and a certain event. The mechanism is not just simple; it's also independent of your application, since it doesn't need to know about any particular control, event or operation (it will find them in the object structure). So it can be programmed once and for all, as part of a library such as EiffelVision 2 for platform-independent graphics. To build an object structure, we need objects. A control, an event are indeed objects. But an operation is not: it's program code -- a routine of a certain class. -Agents address this issue. An agent is an object that represents a routine, which can then be kept in an object structure. The simplest form of agent is written agent r , where r is a routine. This denotes an object. If your_agent is such an agent object, the call +Agents address this issue. An agent is an object that represents a routine, which can then be kept in an object structure. The simplest form of agent is written agent r, where r is a routine. This denotes an object. If your_agent is such an agent object, the call - your_agent.call ([a, b]) + your_agent.call ([a, b]) -where a and b are valid arguments for r, will have the same effect as a direct call to r with arguments a and b. Of course, if you know that you want to call r with those arguments, you don't need any agents; just use the direct call r (a, b) . The benefit of using an agent is that you can store it into an object structure to be called '''later''', for example when an event-driven mechanism finds the agent in the object structure, associated with a certain control and a certain event. For this reason agents are also called '''delayed calls'''. +where a and b are valid arguments for r, will have the same effect as a direct call to r with arguments a and b. Of course, if you know that you want to call r with those arguments, you don't need any agents; just use the direct call r (a, b). The benefit of using an agent is that you can store it into an object structure to be called '''later''', for example when an event-driven mechanism finds the agent in the object structure, associated with a certain control and a certain event. For this reason agents are also called '''delayed calls'''. -{{info|The notation [a, b] denotes a sequence of elements, or '''tuple'''. The reason call needs a tuple as argument, whereas the direct call r (a, b) doesn't, is that call is a general routine (from the EiffelBase class ROUTINE, representing agents) applicable to any agent, whereas the direct call refers explicitly to r and hence requires arguments a and b of specific types. The agent mechanism, however, is statically typed like the rest of the language; when you call call , the type checking mechanism ensures that the tuple you pass as argument contains elements a and b of the appropriate types. }} +{{info|The notation [a, b] denotes a sequence of elements, or '''tuple'''. The reason call needs a tuple as argument, whereas the direct call r (a, b) doesn't, is that call is a general routine (from the EiffelBase class ROUTINE, representing agents) applicable to any agent, whereas the direct call refers explicitly to r and hence requires arguments a and b of specific types. The agent mechanism, however, is statically typed like the rest of the language; when you call call, the type checking mechanism ensures that the tuple you pass as argument contains elements a and b of the appropriate types. }} A typical use of agents with EiffelVision 2 is - ok_button.select_actions.extend (agent your_routine) + ok_button.select_actions.extend (agent your_routine) -which says: "add your_routine to the list of operations to be performed whenever a select event (left click) happens on ok_button". ok_button.select_actions is the list of agents associated with the button and the event; in list classes, procedure extend adds an item at the end of a list. Here, the object to be added is the agent. +which says: "add your_routine to the list of operations to be performed whenever a select event (left click) happens on ok_button". ok_button.select_actions is the list of agents associated with the button and the event; in list classes, procedure extend adds an item at the end of a list. Here, the object to be added is the agent. -This enables the EiffelVision event-handling mechanism to find the appropriate agent when it processes an event, and call call on that agent to trigger the appropriate routine. EiffelVision doesn't know that it's your_routine ; in fact, it doesn't know anything about your application. It simply finds an agent in the list, and calls call on it. For your part, as the author of a graphical application, you don't need to know how EiffelVision handles events; you simply associate the desired agents with the desired controls and events, and let EiffelVision 2 do the rest. +This enables the EiffelVision 2 event-handling mechanism to find the appropriate agent when it processes an event, and call call on that agent to trigger the appropriate routine. EiffelVision 2 doesn't know that it's your_routine; in fact, it doesn't know anything about your application. It simply finds an agent in the list, and calls call on it. For your part, as the author of a graphical application, you don't need to know how EiffelVision 2 handles events; you simply associate the desired agents with the desired controls and events, and let EiffelVision 2 do the rest. Agents extend to many areas beyond GUIs. In '''numerical computation''', you may use an agent to pass to an "integrator" object a numerical function to be integrated over a certain interval. In yet another area, you can use agents (as in the iteration library of EiffelBase) to program '''iterators''' : mechanisms that repetitively apply an arbitrary operation -- represented by an agent -- to every element of a list, tree or other object structure. More generally, agent embody properties of the associated routines, opening the way to mechanism for '''reflection''', also called "introspection": the ability, during software execution, to discover properties of the software itself. diff --git a/documentation/current/method/invitation-eiffel-i2e/i2e-exceptions.wiki b/documentation/current/method/invitation-eiffel-i2e/i2e-exceptions.wiki index 57d2eb14..b671cfb5 100644 --- a/documentation/current/method/invitation-eiffel-i2e/i2e-exceptions.wiki +++ b/documentation/current/method/invitation-eiffel-i2e/i2e-exceptions.wiki @@ -8,38 +8,38 @@ Exceptions -- contract violations -- may arise from several causes. One is an as Unless a routine has made specific provision to handle exceptions, it will '''fail''' if an exception arises during its execution. This in turn provides one more source of exceptions: a routine that fails triggers an exception in its caller. -A routine may, however, handle an exception through a rescue clause. This optional clause attempts to "patch things up" by bringing the current object to a stable state (one satisfying the class invariant). Then it can terminate in either of two ways:
-* The rescue clause may execute a retry instruction, which causes the routine to restart its execution from the beginning, attempting again to fulfill its contract, usually through another strategy. This assumes that the instructions of the rescue clause, before the retry , have attempted to correct the cause of the exception. -* If the rescue clause does not end with retry , then the routine fails: it returns to its caller, immediately triggering an exception. (The caller's rescue clause will be executed according to the same rules.) +A routine may, however, handle an exception through a rescue clause. This optional clause attempts to "patch things up" by bringing the current object to a stable state (one satisfying the class invariant). Then it can terminate in either of two ways:
+* The rescue clause may execute a retry instruction, which causes the routine to restart its execution from the beginning, attempting again to fulfill its contract, usually through another strategy. This assumes that the instructions of the rescue clause, before the retry, have attempted to correct the cause of the exception. +* If the rescue clause does not end with retry, then the routine fails: it returns to its caller, immediately triggering an exception. (The caller's rescue clause will be executed according to the same rules.) The principle is that '''a routine must either succeed or fail''': it either fulfills its contract, or not; in the latter case it must notify its caller by triggering an exception. -Usually, only a few routines of a system will explicitly include a rescue clause. A routine that doesn't have an explicit rescue is considered to have an implicit one, which calls a routine default_rescue that by default does nothing, so that an exception will cause the routine to fail immediately, propagating the exception to the caller. +Usually, only a few routines of a system will explicitly include a rescue clause. A routine that doesn't have an explicit rescue is considered to have an implicit one, which calls a routine default_rescue that by default does nothing, so that an exception will cause the routine to fail immediately, propagating the exception to the caller. -An example using the exception mechanism is a routine attempt_transmission that tries to transmit a message over a phone line. The actual transmission is performed by an external, low-level routine transmit ; once started, however, transmit may abruptly fail, triggering an exception, if the line is disconnected. Routine attempt_transmission tries the transmission at most 50 times; before returning to its caller, it sets a boolean attribute successful to True or False depending on the outcome. Here is the text of the routine: +An example using the exception mechanism is a routine attempt_transmission that tries to transmit a message over a phone line. The actual transmission is performed by an external, low-level routine transmit; once started, however, transmit may abruptly fail, triggering an exception, if the line is disconnected. Routine attempt_transmission tries the transmission at most 50 times; before returning to its caller, it sets a boolean attribute successful to True or False depending on the outcome. Here is the text of the routine: attempt_transmission (message: STRING) - -- Try to transmit message, at most 50 times. - -- Set successful accordingly. - local - failures: INTEGER - do - if failures < 50 then - transmit (message) - successful := True - else - successful := False - end - rescue - failures := failures + 1 - retry - end + -- Try to transmit message, at most 50 times. + -- Set successful accordingly. + local + failures: INTEGER + do + if failures < 50 then + transmit (message) + successful := True + else + successful := False + end + rescue + failures := failures + 1 + retry + end -Initialization rules ensure that failures , a local entity, is set to zero on entry. +Initialization rules ensure that failures, a local entity, is set to zero on entry. -This example illustrates the simplicity of the mechanism: the rescue clause never attempts to achieve the routine's original intent; this is the sole responsibility of the body (the do clause). The only role of the rescue clause is to clean up the objects involved, and then either to fail or to retry. +This example illustrates the simplicity of the mechanism: the rescue clause never attempts to achieve the routine's original intent; this is the sole responsibility of the body (the do clause). The only role of the rescue clause is to clean up the objects involved, and then either to fail or to retry. This disciplined exception mechanism is essential for software developers, who need protection against unexpected events, but cannot be expected to sacrifice safety and simplicity to pay for this protection. diff --git a/documentation/current/method/invitation-eiffel-i2e/i2e-genericity.wiki b/documentation/current/method/invitation-eiffel-i2e/i2e-genericity.wiki index 4b9015c8..c1527196 100644 --- a/documentation/current/method/invitation-eiffel-i2e/i2e-genericity.wiki +++ b/documentation/current/method/invitation-eiffel-i2e/i2e-genericity.wiki @@ -5,16 +5,18 @@ Building software components (classes) as implementations of abstract data types yields systems with a solid architecture but does not in itself ensure reusability and extendibility. Two key techniques address the problem: generosity (unconstrained or constrained) and inheritance. Let us look first at the unconstrained form. To make a class generic is to give it '''formal generic parameters''' representing as unknown types, as in these examples from EiffelBase, an open-source library covering basic data structures and algorithms: -ARRAY [G] -LIST [G] -LINKED_LIST [G] + + ARRAY [G] + LIST [G] + LINKED_LIST [G] -These classes describe data structures -- arrays, lists without commitment to a specific representation, lists in linked representation -- containing objects of a certain type. The formal generic parameter G denotes this type. +These classes describe data structures -- arrays, lists without commitment to a specific representation, lists in linked representation -- containing objects of a certain type. The formal generic parameter G denotes this type. -A class such as these doesn't quite yet describe a type, but a type template, since G itself denotes an unknown type. To derive a directly usable list or array type, you must provide a type corresponding to G , called an '''actual generic parameter'''; this may be either an expanded type, including basic types such as INTEGER , or a reference type. Here are some possible generic derivations: -il: LIST [INTEGER] -aa: ARRAY [ACCOUNT] -aal: LIST [ARRAY [ACCOUNT]] +A class such as these doesn't quite yet describe a type, but a type template, since G itself denotes an unknown type. To derive a directly usable list or array type, you must provide a type corresponding to G, called an '''actual generic parameter'''; this may be either an expanded type, including basic types such as INTEGER, or a reference type. Here are some possible generic derivations: + + il: LIST [INTEGER] + aa: ARRAY [ACCOUNT] + aal: LIST [ARRAY [ACCOUNT]] As the last example indicates, an actual generic parameter may itself be generically derived. diff --git a/documentation/current/method/invitation-eiffel-i2e/i2e-inheritance.wiki b/documentation/current/method/invitation-eiffel-i2e/i2e-inheritance.wiki index 09bcb18b..925d0190 100644 --- a/documentation/current/method/invitation-eiffel-i2e/i2e-inheritance.wiki +++ b/documentation/current/method/invitation-eiffel-i2e/i2e-inheritance.wiki @@ -4,7 +4,7 @@ [[Property:uuid|acf84989-0e7c-f2f7-427a-19e7fce404ce]] Inheritance, the other fundamental generalization mechanism, makes it possible to define a new class by combination and specialization of existing classes rather than from scratch. -The following simple example, from the Data Structure Library in EiffelBase, is typical. LIST , as noted, describes lists in any representation. One such representation if the lists have a fixed number of elements uses an array. We may define the corresponding class by combination of LIST and ARRAY , as follows: +The following simple example, from the Data Structure Library in EiffelBase, is typical. LIST, as noted, describes lists in any representation. One such representation if the lists have a fixed number of elements uses an array. We may define the corresponding class by combination of LIST and ARRAY, as follows: class ARRAYED_LIST [G] inherit @@ -19,7 +19,7 @@ feature end -- ARRAYED_LIST -The inherit ... clause lists all the "parents" of the new class, which is said to be their "heir". (The "ancestors" of a class include the class itself, its parents, grandparents etc.; the reverse term is "descendant".) Declaring ARRAYED_LIST as shown ensures that all the features and properties of lists and arrays are applicable to arrayed lists as well. Since the class has more than one parent, this is a case of multiple inheritance. +The inherit ... clause lists all the "parents" of the new class, which is said to be their "heir". (The "ancestors" of a class include the class itself, its parents, grandparents etc.; the reverse term is "descendant".) Declaring ARRAYED_LIST as shown ensures that all the features and properties of lists and arrays are applicable to arrayed lists as well. Since the class has more than one parent, this is a case of multiple inheritance. Standard graphical conventions -- drawn from the Business Object Notation or BON, a graphical object-oriented notation based on concepts close to those of Eiffel, and directly supported by EiffelStudio -- illustrate such inheritance structures: @@ -27,7 +27,7 @@ Standard graphical conventions -- drawn from the Business Object Notation or BON [[Image:invitation-4]] -An heir class such as ARRAYED_LIST needs the ability to define its own export policy. By default, inherited features keep their export status (publicly available, secret, available to selected classes only); but this may be changed in the heir. Here, for example, ARRAYED_LIST will export only the exported features of LIST , making those of ARRAY unavailable directly to ARRAYED_LIST 's clients. The syntax to achieve this is straightforward: +An heir class such as ARRAYED_LIST needs the ability to define its own export policy. By default, inherited features keep their export status (publicly available, secret, available to selected classes only); but this may be changed in the heir. Here, for example, ARRAYED_LIST will export only the exported features of LIST, making those of ARRAY unavailable directly to ARRAYED_LIST 's clients. The syntax to achieve this is straightforward: class ARRAYED_LIST [G] inherit @@ -39,7 +39,7 @@ class ARRAYED_LIST [G] ... The rest as above ... -Another example of multiple inheritance comes from a windowing system based on a class WINDOW , close to actual classes in EiffelVision 2. Windows have '''graphical''' features: a height, a width, a position, routines to scale windows, move them, and other graphical operations. The system permits windows to be nested, so that a window also has '''hierarchical''' features: access to sub windows and the parent window, adding a sub window, deleting a sub window, attaching to another parent and so on. Rather than writing complex class that would contain specific implementations for all of these features, it is preferable to inherit all hierarchical features from TREE (a class in EiffelBase describing trees), and all graphical features from a class RECTANGLE . +Another example of multiple inheritance comes from a windowing system based on a class WINDOW, close to actual classes in EiffelVision 2. Windows have '''graphical''' features: a height, a width, a position, routines to scale windows, move them, and other graphical operations. The system permits windows to be nested, so that a window also has '''hierarchical''' features: access to sub windows and the parent window, adding a sub window, deleting a sub window, attaching to another parent and so on. Rather than writing complex class that would contain specific implementations for all of these features, it is preferable to inherit all hierarchical features from TREE (a class in EiffelBase describing trees), and all graphical features from a class RECTANGLE. Inheritance complements the "client" relation by providing another form of reuse that yields remarkable economies of effort -- for analysis, design, implementation, evolution -- and has a profound effect on the entire software development process. @@ -62,9 +62,9 @@ class C feature ... -Here, if both A and B have features named x and y , class C would be invalid without the renaming. +Here, if both A and B have features named x and y, class C would be invalid without the renaming. -Renaming also serves to provide more appropriate feature names in descendants. For example, class WINDOW may inherit a routine insert_subtree from TREE . For clients of WINDOW , however, such a routine name is no longer appropriate. An application that uses this class needs coherent window terminology, and should have to concern itself with the inheritance structure that led to the class. So you may wish to rename insert_subtree as add_subwindow in the inheritance clause of WINDOW . +Renaming also serves to provide more appropriate feature names in descendants. For example, class WINDOW may inherit a routine insert_subtree from TREE. For clients of WINDOW, however, such a routine name is no longer appropriate. An application that uses this class needs coherent window terminology, and should have to concern itself with the inheritance structure that led to the class. So you may wish to rename insert_subtree as add_subwindow in the inheritance clause of WINDOW. As a further protection against misusing multiple inheritance, the invariants of all parent classes automatically apply to a newly defined class. So classes may not be combined if their invariants are incompatible. diff --git a/documentation/current/method/invitation-eiffel-i2e/i2e-polymorphism-and-dynamic-binding.wiki b/documentation/current/method/invitation-eiffel-i2e/i2e-polymorphism-and-dynamic-binding.wiki index 5f2399f1..3f2c0be6 100644 --- a/documentation/current/method/invitation-eiffel-i2e/i2e-polymorphism-and-dynamic-binding.wiki +++ b/documentation/current/method/invitation-eiffel-i2e/i2e-polymorphism-and-dynamic-binding.wiki @@ -4,13 +4,13 @@ [[Property:uuid|1c3221be-0237-1c9a-407d-652a4084de12]] Inheritance is not just a module combination and enrichment mechanism. It also enables the definition of flexible entities that may become attached to objects of various forms at run time, a property known as polymorphism. -This remarkable facility must be reconciled with static typing. The language convention is simple: an assignment of the form a : = b is permitted not only if a and b are of the same type, but more generally if a and b are of reference types A and B , based on classes A and B such that B is a descendant of A . +This remarkable facility must be reconciled with static typing. The language convention is simple: an assignment of the form a : = b is permitted not only if a and b are of the same type, but more generally if a and b are of reference types A and B, based on classes A and B such that B is a descendant of A. This corresponds to the intuitive idea that a value of a more specialized type may be assigned to an entity of a less specialized type -- but not the reverse. (As an analogy, consider that if you request vegetables, getting green vegetables is fine, but if you ask for green vegetables, receiving a dish labeled just "vegetables" is not acceptable, as it could include, say, carrots.) What makes this possibility particularly powerful is the complementary facility: '''feature redefinition'''. A class may redefine some or all of the features which it inherits from its parents. For an attribute or function, the redefinition may affect the type, replacing the original by a descendant; for a routine it may also affect the implementation, replacing the original routine body by a new one. -Assume for example a class POLYGON , describing polygons, whose features include an array of points representing the vertices and a function perimeter which computes a polygon's perimeter by summing the successive distances between adjacent vertices. An heir of POLYGON may begin as: +Assume for example a class POLYGON, describing polygons, whose features include an array of points representing the vertices and a function perimeter which computes a polygon's perimeter by summing the successive distances between adjacent vertices. An heir of POLYGON may begin as: class RECTANGLE inherit @@ -33,39 +33,39 @@ feature -- Specific features of rectangles, such as: ... Other RECTANGLE features ... -Here it is appropriate to redefine perimeter for rectangles as there is a simpler and more efficient algorithm. Note the explicit redefine sub clause (which would come after the rename if present). +Here it is appropriate to redefine perimeter for rectangles as there is a simpler and more efficient algorithm. Note the explicit redefine sub clause (which would come after the rename if present). -Other descendants of POLYGON may also have their own redefinitions of perimeter . The version to use in any call is determined by the run-time form of the target. Consider the following class fragment: +Other descendants of POLYGON may also have their own redefinitions of perimeter. The version to use in any call is determined by the run-time form of the target. Consider the following class fragment: - p: POLYGON - r: RECTANGLE + p: POLYGON + r: RECTANGLE ... - create p - create r + create p + create r ... - if c then - p := r - end - print (p.perimeter) + if c then + p := r + end + print (p.perimeter) -The polymorphic assignment p := r is valid because of the above rule. If condition c is false, p will be attached to an object of type POLYGON for the computation of p . perimeter , which will thus use the polygon algorithm. In the opposite case, however, p will be attached to a rectangle; then the computation will use the version redefined for RECTANGLE . This is known as '''dynamic binding'''. +The polymorphic assignment p := r is valid because of the above rule. If condition c is false, p will be attached to an object of type POLYGON for the computation of p. perimeter, which will thus use the polygon algorithm. In the opposite case, however, p will be attached to a rectangle; then the computation will use the version redefined for RECTANGLE. This is known as '''dynamic binding'''. Dynamic binding provides a high degree of flexibility. The advantage for clients is the ability to request an operation (such as perimeter computation) without explicitly selecting one of its variants; the choice only occurs at run-time. This is essential in large systems, where many variants may be available; dynamic binding protects each component against changes in other components. -This technique is particularly attractive when compared to its closest equivalent in traditional approaches, where you would need records with variant components, or union types (C), together with case (switch) instructions to discriminate between variants. This means that every client must know about every possible case, and that any extension may invalidate a large body of existing software. +This technique is particularly attractive when compared to its closest equivalent in traditional approaches, where you would need records with variant components, or union types (C), together with case (switch) instructions to discriminate between variants. This means that every client must know about every possible case, and that any extension may invalidate a large body of existing software. The combination of inheritance, feature redefinition, polymorphism and dynamic binding supports a development mode in which every module is open and incremental. When you want to reuse an existing class but need to adapt it to a new context, you can define a new descendant of that class (with new features, redefined ones, or both) without any change to the original. This facility is of great importance in software development, an activity that -- by design or circumstance -- is invariably incremental. -The power of these techniques demands adequate controls. First, feature redefinition, as seen above, is explicit. Second, because the language is typed, a compiler can check statically whether a feature application a.f is correct. In contrast, dynamically typed object-oriented languages defer checks until run-time and hope for the best: if an object "sends a message" to another (that is to say, calls one of its routines) one just expects that the corresponding class, or one of its ancestors, will happen to include an appropriate routine; if not, a run-time error will occur. Such errors will not happen during the execution of a type-checked Eiffel system. +The power of these techniques demands adequate controls. First, feature redefinition, as seen above, is explicit. Second, because the language is typed, a compiler can check statically whether a feature application a.f is correct. In contrast, dynamically typed object-oriented languages defer checks until run-time and hope for the best: if an object "sends a message" to another (that is to say, calls one of its routines) one just expects that the corresponding class, or one of its ancestors, will happen to include an appropriate routine; if not, a run-time error will occur. Such errors will not happen during the execution of a type-checked Eiffel system. In other words, the language reconciles dynamic binding with static typing. Dynamic binding guarantees that whenever more than one version of a routine is applicable the right version (the one most directly adapted to the target object) will be selected. Static typing means that the compiler makes sure there is at least one such version. This policy also yields an important performance benefit: in contrast with the costly run-time searches that may be needed with dynamic typing (since a requested routine may not be defined in the class of the target object but inherited from a possibly remote ancestor), the EiffelStudio implementation always finds the appropriate routine in constant-bounded time. -Assertions provide a further mechanism for controlling the power of redefinition. In the absence of specific precautions, redefinition may be dangerous: how can a client be sure that evaluation of p.perimeter will not in some cases return, say, the area? Preconditions and postconditions provide the answer by limiting the amount of freedom granted to eventual redefiners. The rule is that any redefined version must satisfy a weaker or equal precondition and ensure a stronger or equal postcondition than in the original. This means that it must stay within the semantic boundaries set by the original assertions. +Assertions provide a further mechanism for controlling the power of redefinition. In the absence of specific precautions, redefinition may be dangerous: how can a client be sure that evaluation of p.perimeter will not in some cases return, say, the area? Preconditions and postconditions provide the answer by limiting the amount of freedom granted to eventual redefiners. The rule is that any redefined version must satisfy a weaker or equal precondition and ensure a stronger or equal postcondition than in the original. This means that it must stay within the semantic boundaries set by the original assertions. -The rules on redefinition and assertions are part of the Design by Contract™ theory, where redefinition and dynamic binding introduce subcontracting. POLYGON , for example, subcontracts the implementation of perimeter to RECTANGLE when applied to any entity that is attached at run-time to a rectangle object. An honest subcontractor is bound to honor the contract accepted by the prime contractor. This means that it may not impose stronger requirements on the clients, but may accept more general requests: weaker precondition; and that it must achieve at least as much as promised by the prime contractor, but may achieve more: stronger postcondition. +The rules on redefinition and assertions are part of the Design by Contract™ theory, where redefinition and dynamic binding introduce subcontracting. POLYGON, for example, subcontracts the implementation of perimeter to RECTANGLE when applied to any entity that is attached at run-time to a rectangle object. An honest subcontractor is bound to honor the contract accepted by the prime contractor. This means that it may not impose stronger requirements on the clients, but may accept more general requests: weaker precondition; and that it must achieve at least as much as promised by the prime contractor, but may achieve more: stronger postcondition. diff --git a/documentation/current/method/invitation-eiffel-i2e/i2e-putting-system-together.wiki b/documentation/current/method/invitation-eiffel-i2e/i2e-putting-system-together.wiki index be28b047..01e0560b 100644 --- a/documentation/current/method/invitation-eiffel-i2e/i2e-putting-system-together.wiki +++ b/documentation/current/method/invitation-eiffel-i2e/i2e-putting-system-together.wiki @@ -18,13 +18,12 @@ For the system to be valid, it must include all the classes which the root '''ne For a library we don't need to specify a root. If we want to make sure that every class in a library compiles fine we can specify that we want all classes to be the root. -The Eiffel method suggests grouping related classes (typically 5 to 40 classes) into collections called '''clusters'''. A universe is then a set of clusters. For example the EiffelBase library is divided into clusters corresponding each to a major category of data structure: lists , tables , iteration and so on. You can nest clusters, using for example EiffelBase, with its own subclusters as listed, as a cluster of your system. +The Eiffel method suggests grouping related classes (typically 5 to 40 classes) into collections called '''clusters'''. A universe is then a set of clusters. For example the EiffelBase library is divided into clusters corresponding each to a major category of data structure: lists, tables, iteration, and so on. You can nest clusters, using for example EiffelBase, with its own subclusters as listed, as a cluster of your system. How will you specify a universe? Any Eiffel implementation can use its own conventions. EiffelStudio applies a simple policy:
-* Store each class in a single file, called its class file, with a name of the form name . e . For clarity, name should be the lower-case version of the class name, although this is a style rule, not a requirement. +* Store each class in a single file, called its class file, with a name of the form name.e . For clarity, name should be the lower-case version of the class name, although this is a style rule, not a requirement. * Put all the class files of a cluster into a single directory (folder on Windows), called its cluster directory. {{note|It is desirable for clarity, as a style rule, to separate clusters that directly contain classes ("terminal clusters") from those that have subclusters. Cluster directories will then contain class files or cluster subdirectories, but not both. }} - * To specify a system, it suffices to provide a list of cluster directories, along with the name of the root class and root procedure. The universe consists of the classes contained in all the class files in the listed cluster directories. diff --git a/documentation/current/method/invitation-eiffel-i2e/i2e-types.wiki b/documentation/current/method/invitation-eiffel-i2e/i2e-types.wiki index 6edab91a..6ba332da 100644 --- a/documentation/current/method/invitation-eiffel-i2e/i2e-types.wiki +++ b/documentation/current/method/invitation-eiffel-i2e/i2e-types.wiki @@ -4,17 +4,17 @@ [[Property:uuid|344a9fdc-3346-5e2d-5fdd-77464e92f72f]] Eiffel is strongly typed for readability and reliability. Every entity is declared of a certain type, which may be either a reference type or an expanded type. -Any type T is based on a class, which defines the operations that will be applicable to instances of T . The difference between the two categories of type affects the semantics of an entity x declared of type T : for a reference type, the most common case, possible values for x are references to objects; for an expanded type, the values are objects. In both cases, the type rules guarantee that the objects will be instances of T . +Any type T is based on a class, which defines the operations that will be applicable to instances of T. The difference between the two categories of type affects the semantics of an entity x declared of type T: for a reference type, the most common case, possible values for x are references to objects; for an expanded type, the values are objects. In both cases, the type rules guarantee that the objects will be instances of T. -A non-expanded class such as ACCOUNT yields a reference type. As a result, an entity of type ACCOUNT , such as acc in the earlier client example (see the declaration of acc and the accompanying picture as given on page [[Invitation to Eiffel|6]] ), denotes possible run-time references to objects of type ACCOUNT . +A non-expanded class such as ACCOUNT yields a reference type. As a result, an entity of type ACCOUNT, such as acc in the earlier client example (see the declaration of acc and the accompanying picture as given in [[4 Classes|section 4]] ), denotes possible run-time references to objects of type ACCOUNT. -In contrast, the value of an entity acc declared of type expanded ACCOUNT is an object such as the one shown on the figure below, with no reference. The only difference with the earlier figure is that the value of acc is now an ACCOUNT object, not a reference to such an object. No creation instruction is needed in this case. (The figure does not show the PERSON object to which the owner field of the ACCOUNT object -- itself a reference -- is attached.) +In contrast, the value of an entity acc declared of type expanded ACCOUNT is an object such as the one shown on the figure below, with no reference. The only difference with the earlier figure is that the value of acc is now an ACCOUNT object, not a reference to such an object. No creation instruction is needed in this case. (The figure does not show the PERSON object to which the owner field of the ACCOUNT object -- itself a reference -- is attached.) [[Image:invitation-3]] -An important group of expanded types, based on library classes, includes the basic types CHARACTER, DOUBLE, REAL, INTEGER and BOOLEAN. Clearly, the value of an entity declared of type INTEGER should be an integer, not a reference to an object containing an integer value. Operations on these types are defined by prefix and infix operators such as "+" and "<". +An important group of expanded types, based on library classes, includes the basic types CHARACTER, DOUBLE, REAL, INTEGER, and BOOLEAN. Clearly, the value of an entity declared of type INTEGER should be an integer, not a reference to an object containing an integer value. Operations on these types are defined by prefix and infix operators such as "+" and "<". As a result of these conventions, the type system is uniform and consistent: all types, including the basic types, are defined from classes, either as reference types or as expanded types.