Ephemeral classes
I've recently been toying with the idea of ephemeral classes. An ephemeral class is a class which does not have attributes. The immediate benefits of such classes is that their features can be used for objectless calls, the same way we can access constants or external features. For those not familiar to Eiffel, the features of ephemeral classes can be seen as static methods.
For example:
Another benefit of such classes is that their features can be directly used for callbacks by some external C/C++ code without having the need for creating a C wrapper for calling back the Eiffel code.
The concept is not yet implemented in EiffelStudio as it is not just a matter of checking the absence of attributes but also to adapt the code generation not to rely on a Current object.
This is something that will most likely be submitted for adoption for the ECMA Eiffel language specification. But before that, I'd like to hear what you think about this!
Happy Eiffeling,
Manu
Great idea
I think this is a great idea. Many classes are in this situation, and we have to employ various artificial technicalities to use them more or less elegantly (inheritance mostly).
This would be a great way of not having to abuse inheritance all over the place for things like constants, utility features etc.
Why ephemeral?
I think it is a good idea. But why the name ephemeral? I suppose the connection is with storables. And are the keyword frozen and deferred supposed to be allowed also? The combination of frozen ephemeral sounds particularly odd to me.
The name was suggested during an ECMA meeting. It might not be the final name, but for the moment this is what we will use for testing this.
Stateless classes
(This idea came to mind while reading Alexander's post below.)
Rather than "ephemeral classes", I think a better name would be "stateless classes".
Eiffel already uses too many idiosyncratic keywords, which I suspect may be a barrier to its wider acceptance: "deferred" vs "abstract", "void" vs "null", etc. The word "stateless" has been widely used for a long time.
Shall we introduce new entities?
It looks like I failed to highlight that apart from stateless property of a class that can be used for objectless calls the other one is lack of identity. Indeed stateless class still can be used to program an application with the behaviour that depends on the objects (the example is simplified, compared to real life, but should give an idea):class TRAFFIC_LIGHT_COLOR
inherit
ANY
redefine
out
end
create {TRAFFIC_LIGHT}
default_create
feature -- Output
out: STRING
-- <Precursor>
local
controller: TRAFFIC_LIGHT
do
create controller
if Current = controller.green then
Result := "green"
elseif Current = controller.yellow then
Result := "yellow"
else
Result := "red"
end
end
end
class TRAFFIC_LIGHT
inherit
ANY
redefine
default_create
end
feature -- Creation
default_create
do
light := red
end
feature -- Access
light: TRAFFIC_LIGHT_COLOR
-- Current light
feature -- Modification
turn_green do light := green end
turn_yellow do light := yellow end
turn_red do light := red end
feature -- Allowed values of `light'
green: TRAFFIC_LIGHT_COLOR once ("PROCESS") create Result end
yellow: TRAFFIC_LIGHT_COLOR once ("PROCESS") create Result end
red: TRAFFIC_LIGHT_COLOR once ("PROCESS") create Result end
end
t: TRAFFIC_LIGHT
a, b: TRAFFIC_LIGHT_COLOR
...
create t
a := t.light
t.turn_green
b := t.light
io.put_string (a.out) -- Prints "red"
io.put_string (b.out) -- Prints "green"
As the example demonstrates we need to get rid of object identity to allow objectless calls.
The last property is already present in expanded classes without attributes. If we follow Occam's razor, there is no need to invent a new notion. Otherwise we can quickly get into the situation like "Doesn't Y already provide X? - Yes, it does, but we added Z and use it instead."
However, the classes that are supposed to be used in a specific way are better to be explicitly marked as such. My feeling is that the built-in Eiffel documentation system, based onnote clause, is quite underused for this purpose. In particular, we may have an associated clause
note
state: no
identity: no
storable: no
for the classes like these.
I wonder how compatible the idea of ephemeral is to "exherit"-ing from existing classes; essentially associating additional functionality to objects. The use-case would be, I have some common function I apply to an integer so it should be associated with an integer.
If one were to exherit from multiple classes, it would be similar to how generic conformance works. You can exherit the class as long as the object you're operating on statically conforms to the exheritance clause.
In my opinion this would be a cleaner way to define conforming inheritance.
To your original point, it would be nice to be able to define C callback code within Eiffel.
This mechanism is known in the Eiffel world as reverse inheritance and was proposed many years ago. I don't have the details but this was looked at by the ECMA committee and dismissed for the time being because most likely it was not resolving a problem that users were having.
The mechanism is ready for use
Talking about objectless calls, we can consider how objects are used. There are 2 properties tightly bound to an object:
We do not care for identity of objects of expanded types. In other words we never know if an object of an expanded type is "the same object" referenced by some other entity (or an expression in general) or not.
From the practical point of view, if we consider a boolean expressiona = b where type of a and b is the same stateless type (i.e. has no attributes), we can get different results if the type is reference, for example, because the equality can be preceded bycreate a
create b but we'll always get True if the type is expanded (the equality operator above refers to the standard object equality, not to some special cases, like NaN in floating point math, discussed elsewhere).
In the latter case there is neither state nor identity associated with an object: objects of the same expanded type without attributes cannot be distinguished in any way. Moreover, no special rules for using special entityCurrent are required, because it is safe to use it: there is nothing associated with it. Summarizing the above, Eiffel already has appropriate mechanism for "ephemeral" classes: they are expanded classes without attributes. Only the language specification needs to be updated to allow objectless calls on types, based on such classes.
Clarification
I'd like to clarify the idea of the previous message. When we talk about objectless calls, we mean that out of the 3 properties associated with objects:
we need only one - behaviour. The other two should not be present for the calls to be safe as objectless. And this is what expanded classes without attributes provide: only behaviour without any identity or/and state. Therefore they can be used directly for the purpose raised by this topic:note
identity: no
state: no
behaviour: yes
expanded class MATH_64
feature -- Logarithm
log2 (value: REAL_64): REAL_64
-- Logarithm with base 2
...
ln (value: REAL_64): REAL_64
-- Logarithm with base `e'
...
lg (value: REAL_64): REAL_64
-- Logarithm with base 10
...
end
Free bonus: the objectless features can be used in unqualified calls (if inherited, including secret inheritance), qualified objectless calls and qualified object calls:inherit {NONE} MATH_64 -- Normal inheritance can be used as well if required
...
m: MATH_64 -- Can be declared if we want to avoid inheritance from MATH_64
...
x := ln (y) -- Unqualified call to inherited feature
x := {MATH_64}.ln (y) -- Qualified objectless call
x := m.ln (y) -- Qualified object call (the object `m' is only to make the call, it's not used by itself)
There is one issue with having expanded class without attributes being considered ephemeral it is the one of conformance. In the case of the ephemeral classexpanded class A , and having class B inherit A , you cannot write the following:
but I definitely agree that expanded classes without attributes fit the description of ephemeral classes.
That's correct
When this possibility is required, we can have 2 classes with the expanded one inheriting the reference:class A_REF
feature
...
end
expanded class A inherit A_REF end
Then the code from the example can be rewritten as
a: A_REF
b: B
a := b
Hopefully in practice this will not happen too often, because the idea is to detach any state and identity from an object - allowing the attachments like the above we preserve them instead.
I like this notion of expanded classes without attributes being ephemeral.
Instead of allowing objectless calls to a feature e.g. {CLASS_A}.static_call_one
Could we just make creation of CLASS_A a no-op.
This feature would essentially ignore the "create a" because it's not needed, there are no attributes to create. Then the optimization of not calling the memory allocator could be realized without "adding" the objectless feature call syntax.
No need for creation instruction
Since entities of expanded types are initialized automatically, there is no need for creation instruction (assuming that classCLASS_A is expanded) and your example simplifies to:
f
local
a: CLASS_A
do
a.static_call_one
end
Inheriting from an ephemeral class
What would happen if I write a class that inherits from an ephemeral class?
This would always call{A}.objectless_feature , right? No opportunity for polymorphism? Or would class B be permitted to redefine objectless_feature?
It should not do any harm
I believe it's OK to inherit and redefine features that are initially used as objectless. The issue is that they cannot be any longer used as objectless if the inheriting/redefining class does not ensure this property. But this does not break clients of the original objectless class, because the calls are not associated with any objects, they are bound to the class instead.
(And if one wants to forbid such inheritance or redefinition, the class can be marked as frozen.)
UFOs
Alexander has clarified that ephemeral classes have no identity and no state.
Maybe we could call instances of them UFOs: Unidentified Fleeting Objects.