Difference between revisions of "Immutable Interface"
(Page creation) |
m (English corrections) |
||
(2 intermediate revisions by the same user not shown) | |||
Line 3: | Line 3: | ||
{{Research}} | {{Research}} | ||
== Introduction == | == Introduction == | ||
− | Eiffel Base 2 relies on immutable interfaces to design better abstractions and to allow a better use of the library. | + | Eiffel Base 2 relies on partial (see below) immutable interfaces to design better abstractions and to allow a better use of the library. |
− | This page sketch a solution to allow immutable interface designs. | + | This page sketch a solution to allow full immutable interface designs. |
== Definition == | == Definition == | ||
− | An interface is immutable if it exports only queries. The routines exported to no classes are not included in the interface. | + | An interface is immutable if it exports only queries. The routines exported to no classes are not included in the class interface. |
− | Note that an immutable class has an immutable interface | + | Note that an immutable class has an immutable interface and more queries must be pure, i.e. with no side-effect. |
== Issue == | == Issue == | ||
Immutable interfaces are not conceivable in the current state of the Eiffel Standard. | Immutable interfaces are not conceivable in the current state of the Eiffel Standard. | ||
− | Indeed, all classes inherit of ANY. However ANY exports some commands. Therefore no | + | Indeed, all classes inherit of ANY. However ANY has not an immutable interface, it exports some commands. Therefore no immutable interface is conceivable. |
− | ANY | + | ANY class interface (queries are omitted): |
<e> | <e> | ||
class interface | class interface | ||
Line 57: | Line 57: | ||
* The command 'default_rescue' allows to change an internal behavior. This command can be exported to no classes. | * The command 'default_rescue' allows to change an internal behavior. This command can be exported to no classes. | ||
− | * The command 'do_nothing' is a | + | * The command 'do_nothing' is a procedure specially interesting for agent uses. |
− | A qualified call of 'do_nothing' on any objects is not | + | A qualified call of 'do_nothing' on any objects is not useful. An interesting use can be: |
<e> | <e> | ||
class | class | ||
Line 76: | Line 76: | ||
</e> | </e> | ||
− | However the contravariance of the second generic parameter of PROCEDURE can remove this use: | + | However the contravariance (concept not introduced yet) of the second generic parameter of PROCEDURE can remove this use: |
<e> | <e> | ||
class | class | ||
Line 94: | Line 94: | ||
Therefore the command 'do_nothing' can be exported to no classes. | Therefore the command 'do_nothing' can be exported to no classes. | ||
− | * The command 'print' | + | * The command 'print' could be called only with unqualified calls. It can be too exported to no classes. |
− | * The commands 'copy' and 'deep_copy' | + | * The commands 'copy' and 'deep_copy' are probably used mainly through the queries 'twin' and respectively 'deep_twin'. |
− | + | Export this commands to no classes seems have no important disturbance. It is always possible to export its in descendants. | |
− | + | However, we can easily imagine that 'twin' and 'deep_twin' create a new object and then apply 'copy' and respectively 'deep_copy'. | |
− | This process does not | + | This process does not follow the Eiffel creation philosophy. |
− | Maybe it is an opportunity to correct it and then to | + | Maybe it is an opportunity to correct it and then to write the queries 'twin' and 'deep_twin' with Eiffel code. |
A solution would be to make both commands as creation commands. | A solution would be to make both commands as creation commands. | ||
Line 113: | Line 113: | ||
default_create | default_create | ||
− | feature {NONE}-- | + | feature {NONE}-- Initialization |
copy (other: like Current) | copy (other: like Current) | ||
Line 144: | Line 144: | ||
</e> | </e> | ||
The both commands can be exported to no classes and two queries can be writed in Eiffel code. | The both commands can be exported to no classes and two queries can be writed in Eiffel code. | ||
− | However the | + | However the developer must declare the creation commands for each effective class. |
− | To remove this issue we can introduce the inherited creation | + | To remove this issue we can introduce the inherited creation command mechanism. |
− | === Inherited | + | === Inherited Creation Command === |
− | A simple addition to Eiffel could be to allow the create clause in | + | A simple addition to Eiffel could be to allow the create clause in deferred classes. |
− | The creation commands declared in deferred classes would be inherited creation commands. | + | The creation commands declared in deferred classes would be inherited creation commands, i.e. creation commands implicitly declared in each descendants. |
+ | |||
+ | If an effective class declares no new creation commands then all inherited creation commands and the command 'default_create' are creation commands. | ||
+ | If an effective class declares new creation commands then all inherited creation commands and new ones are creation commands. | ||
+ | An inherited creation command can be declared again in an effective class. This possibility allows then all inherited commands as creation commands without 'default_create' (except if 'default_create' is an inherited creation command). | ||
<e> | <e> | ||
Line 162: | Line 166: | ||
make (a_n: INTEGER) | make (a_n: INTEGER) | ||
− | + | deferred end | |
end | end | ||
Line 169: | Line 173: | ||
class | class | ||
B | B | ||
+ | |||
+ | inherit | ||
+ | A | ||
+ | |||
+ | feature | ||
+ | -- 'make' and 'default_create' are creation procedures | ||
+ | |||
+ | end | ||
+ | </e> | ||
+ | <e> | ||
+ | class | ||
+ | C | ||
inherit | inherit | ||
Line 174: | Line 190: | ||
create | create | ||
− | -- make is a creation procedure | + | make |
+ | |||
+ | feature | ||
+ | -- 'make' is a creation procedure | ||
end | end | ||
</e> | </e> | ||
+ | |||
This addition does not affect existing code. The compatibility is preserved. | This addition does not affect existing code. The compatibility is preserved. | ||
− | And more it can be | + | And more it can be useful in constrained generic context. |
=== ANY and inherited creation commands === | === ANY and inherited creation commands === | ||
− | The mechanism described in the previous section is not usable on ANY. Indeed, ANY is an | + | The mechanism described in the previous section is not usable on ANY. Indeed, ANY is an effective class. |
− | However | + | However is it very useful to allow the creation of ANY instance? |
− | I have | + | I have not examples of use of ANY instances. Maybe it is more relevant to make ANY as deferred class and then to use the inherited creation command mechanism. |
<e> | <e> | ||
Line 200: | Line 220: | ||
end | end | ||
− | < | + | </e> |
+ | |||
+ | |||
== Conclusion == | == Conclusion == | ||
− | + | Export all ANY class's commands allows the design of full immutable interfaces. | |
− | + | In addition, the inherited creation command concept is a minimal mechanism compatible with existing software. It introduces no new keyword and allows to write two queries of ANY in Eiffel code. | |
− | + |
Latest revision as of 09:20, 1 February 2014
Research: This page describes research about Eiffel, not the actual language specification.
Contents
Introduction
Eiffel Base 2 relies on partial (see below) immutable interfaces to design better abstractions and to allow a better use of the library. This page sketch a solution to allow full immutable interface designs.
Definition
An interface is immutable if it exports only queries. The routines exported to no classes are not included in the class interface. Note that an immutable class has an immutable interface and more queries must be pure, i.e. with no side-effect.
Issue
Immutable interfaces are not conceivable in the current state of the Eiffel Standard. Indeed, all classes inherit of ANY. However ANY has not an immutable interface, it exports some commands. Therefore no immutable interface is conceivable.
ANY class interface (queries are omitted):
class interface ANY feature -- Duplication copy (other: like Current) -- Update current object using fields of object attached -- to `other', so as to yield equal objects. require type_identity: same_type (other) ensure is_equal: Current ~ other frozen deep_copy (other: like Current) -- Effect equivalent to that of: -- copy (`other' . deep_twin) ensure deep_equal: deep_equal (Current, other) feature -- Basic Operations default_rescue -- Process exception for routines with no Rescue clause. -- (Default: do nothing.) frozen do_nothing -- Execute a null action. feature -- Output print (o: detachable ANY) -- Write terse external representation of `o' -- on standard output. end
Make ANY as immutable interface
Export commands to no classes
- The command 'default_rescue' allows to change an internal behavior. This command can be exported to no classes.
- The command 'do_nothing' is a procedure specially interesting for agent uses.
A qualified call of 'do_nothing' on any objects is not useful. An interesting use can be:
class EXAMPLE feature do_something do do_something_with (agent {A}.do_nothing) end do_something_with (a: PROCEDURE [ANY, TUPLE [A]]) do end end
However the contravariance (concept not introduced yet) of the second generic parameter of PROCEDURE can remove this use:
class EXAMPLE feature do_something do do_something_with (agent do_nothing) end do_something_with (a: PROCEDURE [ANY, TUPLE [A]]) do end
Therefore the command 'do_nothing' can be exported to no classes.
- The command 'print' could be called only with unqualified calls. It can be too exported to no classes.
- The commands 'copy' and 'deep_copy' are probably used mainly through the queries 'twin' and respectively 'deep_twin'.
Export this commands to no classes seems have no important disturbance. It is always possible to export its in descendants.
However, we can easily imagine that 'twin' and 'deep_twin' create a new object and then apply 'copy' and respectively 'deep_copy'. This process does not follow the Eiffel creation philosophy. Maybe it is an opportunity to correct it and then to write the queries 'twin' and 'deep_twin' with Eiffel code.
A solution would be to make both commands as creation commands.
class ANY create copy, deep_copy, default_create feature {NONE}-- Initialization copy (other: like Current) -- Update current object using fields of object attached -- to `other', so as to yield equal objects. require type_identity: same_type (other) external "built_in" ensure is_equal: Current ~ other end -- ... feature -- Duplication frozen twin: like Current -- New object equal to `Current' -- `twin' calls `copy'; to change copying/twinning semantics, redefine `copy'. do create Result.copy (Current) ensure is_equal: Result ~ Current end -- ... end
The both commands can be exported to no classes and two queries can be writed in Eiffel code. However the developer must declare the creation commands for each effective class.
To remove this issue we can introduce the inherited creation command mechanism.
Inherited Creation Command
A simple addition to Eiffel could be to allow the create clause in deferred classes. The creation commands declared in deferred classes would be inherited creation commands, i.e. creation commands implicitly declared in each descendants.
If an effective class declares no new creation commands then all inherited creation commands and the command 'default_create' are creation commands. If an effective class declares new creation commands then all inherited creation commands and new ones are creation commands. An inherited creation command can be declared again in an effective class. This possibility allows then all inherited commands as creation commands without 'default_create' (except if 'default_create' is an inherited creation command).
deferred class A create -- inherited make feature -- Initialization make (a_n: INTEGER) deferred end end
class B inherit A feature -- 'make' and 'default_create' are creation procedures end
class C inherit A create make feature -- 'make' is a creation procedure end
This addition does not affect existing code. The compatibility is preserved.
And more it can be useful in constrained generic context.
ANY and inherited creation commands
The mechanism described in the previous section is not usable on ANY. Indeed, ANY is an effective class. However is it very useful to allow the creation of ANY instance?
I have not examples of use of ANY instances. Maybe it is more relevant to make ANY as deferred class and then to use the inherited creation command mechanism.
deferred class ANY create -- inherited creation commands copy, deep_copy feature -- ... end
Conclusion
Export all ANY class's commands allows the design of full immutable interfaces.
In addition, the inherited creation command concept is a minimal mechanism compatible with existing software. It introduces no new keyword and allows to write two queries of ANY in Eiffel code.