Immutable Interface

Revision as of 05:36, 19 August 2013 by Conaclos (Talk | contribs) (tag correction)

Research: This page describes research about Eiffel, not the actual language specification.

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

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.