Immutable Interface
Research: This page describes research about Eiffel, not the actual language specification.
Contents
Introduction
Eiffel Base 2 relies on 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.
Definition
An interface is immutable if it exports only queries. The routines exported to no classes are not included in the interface. Note that an immutable class has an immutable interface. However queries must be pures, 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 exports some commands. Therefore no immutble interface is conceivable.
ANY commands:
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 procedur especially interresting for agent uses.
A qualified call of 'do_nothing' on any objects is not usefull. An interresting 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 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' can be called only with unqualified calls. It can be too exported to no classes.
- The commands 'copy' and 'deep_copy' give us a challenge. Indeed they are used in queries ('twin' and 'deep_twin').
We can note that they are mainly used to initialize a freshly created object.
We can imagine that 'twin' and 'deep_twin' create a new object and then apply 'copy' or respectively 'deep_copy'. This process does not folow the Eiffel creation phillosophy. Maybe it is an opportunity to correct it and then to concretise 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}-- 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) 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 developper must declare the creation commands for ecah effective class.
To remove this issue we can introduce the inherited creation commands.
Inherited creation commands
A simple addition to Eiffel could be to allow the create clause in the deferred classes. The creation commands declared in deferred classes would be inherited creation commands.
deferred class A create -- inherited make feature -- Initialization make (a_n: INTEGER) do end end
class B inherit A create -- make is a creation procedure end
This addition does not affect existing code. The compatibility is preserved. And more it can be usefull 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 effetcive class. However it is very usefull to allow the creation of ANY instance?
I have no example 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 <e/> == Conclusion == The proposition allows the design of full immutable interfaces. The inherited creation command is a minimal mechanism compatible with existing software. It introduces no new keyword. it allow to concretise two querise of ANY in Eiffel code and to export to no classes two problematic commands.