Difference between revisions of "Eiffel Breaking Changes"

(Library changes: Added 2 notes for issues involving TUPLE.)
(Strings: remove implicit conversion STRING_32.as_string_8)
 
(6 intermediate revisions by 2 users not shown)
Line 31: Line 31:
 
A lot of code would not compile anymore by this change. Pretty much all libraries would have to be rewritten.
 
A lot of code would not compile anymore by this change. Pretty much all libraries would have to be rewritten.
  
==Void-safe copy==
+
==Strings: remove implicit conversion from XXX_STRING_32 to XXX_STRING_8==
 +
===Before===
 +
XXX_STRING_32 implicitly converts to XXX_STRING_8.
 +
 
 +
===After===
 +
No more XXX_STRING_32 to SSS_STRING_8 implicit conversion.
 +
 
 +
===Rationale===
 +
This implicit conversion was great when the STRING_32 type was introduced in Eiffel and heavily used in EiffelVision2. It enabled existing code that was purely ASCII at the time to compile unchanged. The goal was always to remove that conversion as early as possible as it is a truncation which was ok as long as your were ASCII based.
 +
 
 +
Now this is a recipe for trouble to allow such an implicit conversion.
 +
 
 +
===Issues===
 +
A lot of code would not compile anymore by this change if they use Unicode enabled libraries such as EiffelVision2 and still use STRING_8 in their code.
 +
 
 +
==Void-safe and invariant-safe copy==
 
===Before===
 
===Before===
 
Whenever a user redefined copy, it was the assumption that argument's fields would be all set to their default value (case of '''copy''' called indirectly via '''twin''') or to some tangible value (normal case of doing '''x.copy (y)''').
 
Whenever a user redefined copy, it was the assumption that argument's fields would be all set to their default value (case of '''copy''' called indirectly via '''twin''') or to some tangible value (normal case of doing '''x.copy (y)''').
  
 
===After===
 
===After===
Whenever a user redefines copy, all the fields are properly set to either the fields of the original object
+
Whenever a user redefines copy, all the fields are properly set to the fields of the original object which is either the object on which we call '''twin''' or the source being copied from.
  
 
===Rationale===
 
===Rationale===
 +
This makes the code fully void-safe as we will never try to access fields that are declared attached but actually Void.
 +
 +
This removes the need to disable invariant checking in the implementation of '''twin'''.
  
 
===Issues===
 
===Issues===
 +
Because '''copy''' can be called directly or indirectly, we used to perform the following test on a field that was supposedly attached at runtime:
 +
 +
<e>
 +
if field = Void then
 +
        -- Called from `twin`
 +
    ...
 +
else
 +
        -- Direct call to `copy'
 +
    ...
 +
end
 +
</e>
 +
 +
This kind of code won't work anymore. Instead one has to do
 +
<e>
 +
if Current = other then
 +
        -- Called from `twin`
 +
    ...
 +
else
 +
        -- Direct call to `copy'
 +
    ...
 +
end
 +
</e>
 +
 +
but this is not ideal since we cannot distinguish between <e>a.twin</e> from <e>a.copy (a)</e>. Since calling the later is very rare, the above solution is a good one overall.
  
 
==Restrict export to copy==
 
==Restrict export to copy==
 +
===Before===
 +
You could duplicate objects as you wish.
 +
===After===
 +
You can only duplicate objects if they are marked as `copiable' or `duplicable'. So far there is no definite answer to allow this. What is being proposed is either one of the following:
 +
# Define '''twin''', '''copy''', ... in ANY like today, but exported to NONE.
 +
# Remove '''twin''', '''copy''', ... from ANY and add them to a new abstract class COPIABLE
 +
The second solution is simple as at runtime we simply have to check for conformance to COPIABLE.
 +
===Rationale===
 +
If you duplicate an object graph that has some external memory reference, causing a duplication of the object holding that reference could cause a double-free of that external memory reference when Eiffel objects are disposed by the GC.
  
==Change dynamic type of objects==
+
In addition, some objects are complicated to duplicate such as: Files, Windows, Widgets, databases connections,...
  
==File operations==
+
===Issues===
 +
For client code, it should still compile as before. For user defined code that is known to allow copy/duplication, the code will have to be rewritten to allow copy/duplication.
 +
 
 +
==Change the type describing the dynamic type of objects==
 +
===Before===
 +
In non-void-safe mode and in void-safe mode, the dynamic type of an object was '''detachable X'''.
 +
===After===
 +
The dynamic type of an object would always be '''attached X'''.
 +
===Rationale===
 +
It is common sense that an object presence indicates implicitly that it is attached and it will simplify code that test for the type of an object. Currently one had to write:
 +
<e>
 +
if attached {TYPE [detachable X]} obj.generating_type then
 +
</e>
 +
when really it makes more sense to write:
 +
<e>
 +
if attached {TYPE [X]} obj.generating_type then
 +
</e>
 +
===Issues===
 +
It was initially '''detachable X''' for reasons that are too complex and vague as of this writing (mostly related to backward compatibility, storable, reflection). Changing the type to '''attached''' has some consequences:
 +
* IDs won't be contiguous as they used to be
 +
* Storables won't be retrievable by old systems
 +
 
 +
==I/O and external operations==
 +
===Before===
 +
Any I/O operations could raise an exception upon failure.
 +
===After===
 +
No I/O operations will raise an exception upon failure.
 +
===Rationale===
 +
Writing exception free code for manipulating external resources is difficult at the moment, forcing you to use rescue clauses too many times in code that may have a very complex flow. Performing a check after the operation to  see if it was successful is exception free but allows for a better and more accurate error reporting.
 +
===Issues===
 +
Changing the code would break too much code. The idea is to keep the existing as is, but add a new set of classes from a new I/O library outside of EiffelBase which will be exception free. It has mostly an impact on IO_MEDIUM descendants.
  
 
==Remove explicit creation procedure from <e>TUPLE</e>==
 
==Remove explicit creation procedure from <e>TUPLE</e>==

Latest revision as of 03:31, 29 January 2016

Warning.png Warning: Article under development

Introduction

As of this writing, January 21st 2016, the Eiffel language has slowly evolved over the years and added numerous new facilities. Initially, the changes were done by Eiffel Software while maintaining backward compatibility. Then, a major work was done between 2001 and 2005 to standardize the language which became an ECMA standard in 2005 (ECMA-367) and an ISO standard in 2006 (ISO/IEC 25436:2006). That work established some constructs initially implemented in EiffelStudio and also introduced some new ones. Most of the changes introduced by the standardization have been implemented but there are quite a few remaining. In some cases, it is due to the ration cost/benefit of the implementation, in some others it is a breaking changes. In addition, since the standardization was completed, Eiffel Software kept adding new language features too.

This page is going to list all the changes that needs to be done by Eiffel Software to conform to the standard which cover both the language but also the library facilities required to support the language specification.

Library changes

Strings

Before

STRING maps to STRING_8 And we have STRING_32, IMMUTABLE_STRING_8 and IMMUTABLE_STRING_32

After

STRING should be the equivalent of what IMMUTABLE_STRING_32 was before. STRING_8 should be the equivalent of what IMMUTABLE_STRING_8 was before. MUTABLE_STRING_8 should be the equivalent of what STRING_8 was before. MUTABLE_STRING_32 should be the equivalent of what STRING_32 was before.

Rational

When STRING_32 was added, we did not want to break existing code if it became the default. For example, many Eiffel libraries having a binding with C, could directly use an Eiffel STRING object as argument to a C routines. Of course this is very dangerous and we have changed this over the year to introduce safer mechanism (such as C_STRING).

Now the world is Unicode, not using a Unicode string by default does not make sense. On top of that, string instances are rarely modified and we have seen over the year many bugs because users have a tendency to forget this important fact. Which caused a lot of code to duplicate strings just to be sure, making the code less efficient. This is why we want to map STRING to IMMUTABLE_STRING_32.

The end benefit is that we would have cleaner string manipulations and more importantly much more efficient string operations (search, string sharing, traversals, ...)

Issues

A lot of code would not compile anymore by this change. Pretty much all libraries would have to be rewritten.

Strings: remove implicit conversion from XXX_STRING_32 to XXX_STRING_8

Before

XXX_STRING_32 implicitly converts to XXX_STRING_8.

After

No more XXX_STRING_32 to SSS_STRING_8 implicit conversion.

Rationale

This implicit conversion was great when the STRING_32 type was introduced in Eiffel and heavily used in EiffelVision2. It enabled existing code that was purely ASCII at the time to compile unchanged. The goal was always to remove that conversion as early as possible as it is a truncation which was ok as long as your were ASCII based.

Now this is a recipe for trouble to allow such an implicit conversion.

Issues

A lot of code would not compile anymore by this change if they use Unicode enabled libraries such as EiffelVision2 and still use STRING_8 in their code.

Void-safe and invariant-safe copy

Before

Whenever a user redefined copy, it was the assumption that argument's fields would be all set to their default value (case of copy called indirectly via twin) or to some tangible value (normal case of doing x.copy (y)).

After

Whenever a user redefines copy, all the fields are properly set to the fields of the original object which is either the object on which we call twin or the source being copied from.

Rationale

This makes the code fully void-safe as we will never try to access fields that are declared attached but actually Void.

This removes the need to disable invariant checking in the implementation of twin.

Issues

Because copy can be called directly or indirectly, we used to perform the following test on a field that was supposedly attached at runtime:

if field = Void then
        -- Called from `twin`
    ...
else
        -- Direct call to `copy'
    ...
end

This kind of code won't work anymore. Instead one has to do

if Current = other then
        -- Called from `twin`
    ...
else
        -- Direct call to `copy'
    ...
end

but this is not ideal since we cannot distinguish between a.twin from a.copy (a). Since calling the later is very rare, the above solution is a good one overall.

Restrict export to copy

Before

You could duplicate objects as you wish.

After

You can only duplicate objects if they are marked as `copiable' or `duplicable'. So far there is no definite answer to allow this. What is being proposed is either one of the following:

  1. Define twin, copy, ... in ANY like today, but exported to NONE.
  2. Remove twin, copy, ... from ANY and add them to a new abstract class COPIABLE

The second solution is simple as at runtime we simply have to check for conformance to COPIABLE.

Rationale

If you duplicate an object graph that has some external memory reference, causing a duplication of the object holding that reference could cause a double-free of that external memory reference when Eiffel objects are disposed by the GC.

In addition, some objects are complicated to duplicate such as: Files, Windows, Widgets, databases connections,...

Issues

For client code, it should still compile as before. For user defined code that is known to allow copy/duplication, the code will have to be rewritten to allow copy/duplication.

Change the type describing the dynamic type of objects

Before

In non-void-safe mode and in void-safe mode, the dynamic type of an object was detachable X.

After

The dynamic type of an object would always be attached X.

Rationale

It is common sense that an object presence indicates implicitly that it is attached and it will simplify code that test for the type of an object. Currently one had to write:

if attached {TYPE [detachable X]} obj.generating_type then

when really it makes more sense to write:

if attached {TYPE [X]} obj.generating_type then

Issues

It was initially detachable X for reasons that are too complex and vague as of this writing (mostly related to backward compatibility, storable, reflection). Changing the type to attached has some consequences:

  • IDs won't be contiguous as they used to be
  • Storables won't be retrievable by old systems

I/O and external operations

Before

Any I/O operations could raise an exception upon failure.

After

No I/O operations will raise an exception upon failure.

Rationale

Writing exception free code for manipulating external resources is difficult at the moment, forcing you to use rescue clauses too many times in code that may have a very complex flow. Performing a check after the operation to see if it was successful is exception free but allows for a better and more accurate error reporting.

Issues

Changing the code would break too much code. The idea is to keep the existing as is, but add a new set of classes from a new I/O library outside of EiffelBase which will be exception free. It has mostly an impact on IO_MEDIUM descendants.

Remove explicit creation procedure from TUPLE

Scope: void-safety

Before

After

Rationale

Explicit creation of a TUPLE with attached reference fields introduces objects that break void-safety rules and make a system void-unsafe.

Issues

Some code needs to be redesigned to remove explicit TUPLE creation.

Impact

Low: explicit tuple creation can be flagged as obsolete by using regular Eiffel mechanism. After 1-2 releases the feature can be removed.

Make TUPLE argument of features {ROUTINE}.call and {FUNCTION}.item attached

Scope: void-safety

Before

{ROUTINE}.call and {FUNCTION}.item take argument of a type detachable TUPLE.

After

{ROUTINE}.call and {FUNCTION}.item take argument of a type TUPLE.

Rationale

It is possible that an agent object expects some arguments, but if it is passed Void, the compiler does not warn user about the issue.

Issues

All calls .call (Void) and .item (Void) need to be updated.

Impact

Very low: code can be updated by replacing .call (Void) with .call.

Language Change

Reverse order of inheritance clauses

Dynamic dispatch

Non-conforming inheritance

Frozen/variant annotations