Hello all,
I've just discovered that in
EiffelStudio 25.12, FORMAT_DOUBLE
has been changed to inherit non-conformingly from FORMAT_INTEGER.
I'd like to understand the reasoning behind this decision, because
it has broken a class of mine that compiled and worked correctly
under 16.05.
My
class EL_FORMAT_ROUTINES
maintains a cache table typed as:
format_table: EL_AGENT_CACHE_TABLE [FORMAT_INTEGER, INTEGER]
with a factory feature returning FORMAT_INTEGER:
new_format (key: INTEGER): FORMAT_INTEGER do ... if is_real then create {FORMAT_DOUBLE} Result.make (width, decimal_count) else create Result.make (width) end end
and a retrieval site that uses an object test downcast:
if attached {FORMAT_DOUBLE} format_table.item (key) as format then Result := format.formatted (d) end
Both of these relied on FORMAT_DOUBLE
conforming to FORMAT_INTEGER.
With non-conforming inheritance that conformance is gone, so:
create
{FORMAT_DOUBLE} Result is no longer valid when Result
is typed FORMAT_INTEGER.attached
{FORMAT_DOUBLE} object test can never succeed — FORMAT_DOUBLE
objects will never be stored in a FORMAT_INTEGER-typed
container.It is worth noting that the unified
table is not merely a convenience — it serves a concrete
optimisation purpose. Format objects are expensive to create
repeatedly, so EL_FORMAT_ROUTINES
caches them by a packed integer key encoding width, decimal count,
zero-padding, and type (integer vs real). A single EL_AGENT_CACHE_TABLE
[FORMAT_INTEGER, INTEGER] handles both kinds
transparently: on first access the appropriate object is created
and stored; on subsequent calls with the same parameters the
cached instance is returned directly. The polymorphic subtype
relationship was what made this unified cache possible. Severing
conformance forces either two separate tables with duplicated
keying logic, or a wrapper type to re-unify them — both of which
add complexity to compensate for a change whose motivation remains
unclear.
Non-conforming inheritance — and its C++ cousin, private inheritance — has a well-established reputation as a design smell in the OO literature. The core criticism is that it conflates two distinct concerns: subtype polymorphism ("is-a") and implementation reuse ("is-implemented-in-terms-of"). The latter is more honestly and transparently expressed through composition. Scott Meyers' Effective C++ (Item 39) puts it plainly: prefer composition whenever you can, and reserve private inheritance for the narrow case where the empty base class optimisation is genuinely needed. Most modern languages — Java, C#, Go, Rust, Swift — omit private inheritance entirely, a deliberate signal that the pattern solves a problem better solved another way.
Eiffel's inherit
{NONE} is a more principled version of the same idea: the
syntax is explicit, feature adaptation is fine-grained, and
multiple inheritance is properly supported. But the fundamental
tension remains. When non-conforming inheritance is applied to a
class that already has a natural and well-used subtype
relationship — as FORMAT_DOUBLE
did with FORMAT_INTEGER
— it doesn't just suppress conformance in the abstract; it
actively breaks client code that depended on that relationship
being there.
Back to the specific case
This is a straightforward and natural
use of polymorphism: a table of formatters keyed by integer,
holding either FORMAT_INTEGER
or FORMAT_DOUBLE
instances, with the appropriate subtype retrieved at the call
site. It's hard to imagine this pattern being intentional breakage
rather than an oversight.
Could someone from Eiffel Software explain:
FORMAT_DOUBLE
conforming to FORMAT_INTEGER
intentional?The workaround isn't difficult
(separate tables, or wrapping FORMAT_DOUBLE),
but the change feels like it sacrifices a well-established and
useful subtype relationship without obvious gain. If the concern
was preventing misuse of FORMAT_INTEGER
features on DOUBLE
values, that could equally have been addressed through
preconditions or a refined interface rather than severing
conformance entirely.
Fortunately, Eiffel's library
override mechanism means this kind of mis-step doesn't have to be
a permanent imposition on library users. By placing a corrected
version of FORMAT_DOUBLE
in an ECF override cluster, conforming inheritance can be restored
without touching the EiffelBase sources. That is precisely what I
intend to do for Eiffel-Loop — but it would of course be
preferable not to have to.
Finnian Reilly
https://github.com/finnianr/Eiffel-Loop/blob/master/library/base/text/format/el_format_routines.e
-- SmartDevelopersUseUnderScoresInTheirIdentifiersBecause_it_is_much_easier_to_read
To unsubscribe from this group and stop receiving emails from it, send an email to eiffel-users...@googlegroups.com.
To view this discussion visit https://groups.google.com/d/msgid/eiffel-users/819858a5-25ec-432a-bcf8-ebcf47cd6792%40gobosoft.com.
The core idea of object-oriented programming, the brilliant intuition of Nygaard and Dahl (although they did not explain it in so many words), is the merging of two programming concepts: module and type. A module is a structural (syntactic) unit of decomposition, affecting the static view (program text). A type is a semantic unit, affecting the dynamic view (program execution): the description of a set of possible values. It is initially quite counter-intuitive to treat such seemingly disconnected notions as one and the same, which is why it took two decades between the start of modern programming and the design of OO technology. The result of the merge is the class, the distinctive concept of OO programming (and OO design, database, requirements etc.).
The way the counter-intuitive merge works is that the services offered by the class, viewed as a module, are precisely the operations on instances of the class, viewed as a type. It takes a while to drill this idea into one’s mind but once you have operationalized it you understand object technology.
Strangely, as far as I know no one else explains OO this way. I do, starting even before my OOSC book. Just as strangely, many people (outside of this group) who claim to have read the book also do not understand the idea, even though that is really all that the book says. Maybe that is my fault for taking 1270 pages to explain it. My experience is that people usually stop around page 7.
The beauty of the merge extends to inheritance. You look at the class as a module, and you can extend it; in this sense inheritance subsumes the idea of module reuse, as given for example by “include” or “package use” mechanisms in C, Ada, Java etc. You look at the class as a type, and you can specialize it; in this sense inheritance subsumes the idea of subtype, as you would find it in a functional language. The versatility of the inheritance mechanism, following from the versatility of the class mechanism, comes from the type/module duality. Kind of like wave/particle. It also explains the apparent paradox: inheritance is both extension (we add features to the module) and specialization (we restrict the set of type instances).
There is no reason to restrict this mechanism, for example through the horrendous “interface” facilities of languages such as Java and C#, which put an unacceptable expressive restriction on the programmer, or through the restriction to single inheritance. (For more details, see my 2024 “Right and Wrong: Ten Choices in Language Design”, part of the collective book “The French School of Programming”, preprint at https://arxiv.org/pdf/2211.16597.)
I can think of no obvious case in which -- out of the two views -- you need only type specialization, but it can happen that you need only module reuse. For example you might want to inherit from a facility class which encapsulates math constants. (There are other ways to reuse, such as non-object call in Eiffel, but often inheritance is simpler.) In such cases subtyping would not really be wrong but it causes irrelevant technical problems (the need to disambiguate features in repeated inheritance) and you can avoid them by declaring the inheritance link to be non-conforming. An added level of flexibility, extending the OO programmer’s basic toolset.
-- BM
To view this discussion visit https://groups.google.com/d/msgid/eiffel-users/CALFgaYDBCT1VQpVOWJf8dn%2BKVH%3DWN%2BGgck7Msirs6mcigpu-6g%40mail.gmail.com.
To view this discussion visit https://groups.google.com/d/msgid/eiffel-users/CALFgaYDBCT1VQpVOWJf8dn%2BKVH%3DWN%2BGgck7Msirs6mcigpu-6g%40mail.gmail.com.
On 8 Jun 2026, at 11:03 AM, Ian Joyner <joyne...@gmail.com> wrote:
Bertrand gives an excellent and simple explanation. Class = module.
To view this discussion visit https://groups.google.com/d/msgid/eiffel-users/76849609-BD1C-45D0-A5D4-92AB3352F1EB%40gmail.com.
Hi Eric,
Thank you for digging into the repository history, that's very helpful. The commit comment itself rather makes the case: "where possible and appropriate" is precisely the question, and as you note, the existence of client code relying on the conforming relationship is a clear signal that "appropriate" was not satisfied here.
I'll submit a problem report as you suggest, and will look at preparing a Pull Request with the corrected class once the override is in place in Eiffel-Loop.
As for the 20,000th report, congratulations . Your dedication is admirable. I'd suggest your prize should be 1 euro per report: at that rate you've clearly earned a cruise-ship holiday at minimum. But I expect you will be bringing your dev laptop with you ;-)
regards
Finnian
Hi Finnian,
Looking at the repository history, the modification was done on
February 22nd, 2021 with the following commit comment:
Replaced conforming inheritance with non-conforming one
where possible and appropriate.
So, I would say that the "possible and appropriate" criteria
do not apply here since you showed us that there are existing
code which relies on the conforming inheritance.
I would suggest that you submit a problem report to:
https://support.eiffel.com
to record this issue. BTW, I just submitted the 20,000th problem
report on this platform. Hopefully I will win something :-)
You can also prepare a Pull Request on Github:
https://github.com/EiffelSoftware/es-libraries
with the version of the class that you put in your override cluster.
-- SmartDevelopersUseUnderScoresInTheirIdentifiersBecause_it_is_much_easier_to_read
--
You received this message because you are subscribed to the Google Groups "Eiffel Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to eiffel-users...@googlegroups.com.
To view this discussion visit https://groups.google.com/d/msgid/eiffel-users/54959107-9ff0-41db-9ae6-6e2913937939%40gmail.com.