[Oberon] Why no enumeration types?

Diego Sardina dsar at eml.cc
Wed Jul 28 13:18:25 CEST 2021


On Wed, Jul 28, 2021, at 12:36 AM, Claudio Nieder wrote:
> 
> Similar problems if you declare three INTEGER red=0, green=1, blue=2 and 
> then change them to yellow=2, blue=3
> 

Sorry Claudio, but your arguments don't make sense or maybe you misunderstood the whole point.

Why you are talking about changing the value of a constant?
If you change the value of a (public) constant you are changing the interface of your module. Changing constant values is part of the interface invalidation and compilers will force you a rebuild.

We are talking about *extending the interface* and If you want to extend your program just add *new constants*. In an enumeration type instead adding new entities (also at the end) will invalidate the interface.
Here lies the problem.


>
> If you have VAR table: ARRAY 3 OF BOOLEAN; when you add an additional 
> color it can reslt in table[i] to be outside of the array boundaries 
> resulting in a crash.
>

No, the runtime will protect your program from a crash by halting your program.

Halting the program by the runtime is different from a crash (segmentation fault, segmentation violation, etc). Strong typing languages don't crash (strong typing implies memory safety).

The case with the enumeration types is different and it is *a true crash* (if you don't rebuild all modules).

The client module was compiled with the old definition of the enumeration type and the table was then allocated with a different size, while the modified module has the new definition (that the runtime will use, since it is the business of the modified module to export it).
The runtime can't protect well your program because has different size information, so the table will be iterated outside of the array boundaries and a bad crash will happen.

For this reason a rebuild is required, so they can get the new size information from the interface.


> 
>  From my experience, finding errors that occur at runtime are so much 
> costlier than having the compiler report an error or even forcing a 
> recompilation of modules.
> 

You can use record pointer instances and you will have static typing.

Maybe you are a solo-developer / small team and your libraries are under your full control ( = no clients use it directly), so you don't mind to rebuild your libraries because you ship them with your final product. Maybe you even use static linking, so this is not a true problem at all.
I can understand that, but it's not the whole reality.

In large scale software changes in an interface that require recompilation of modules is always wrong because you are not ensuring binary compatibility. In software engineering literature is an accepted fact that strong interfaces must rely on opaque data types.

In weaker languages like C this is called opaque pointer (that page contains other names used for it):
https://en.wikipedia.org/wiki/Opaque_pointer

Modula-2 is very strong with the concept of opaque data types and helped to create very strong interface (except if you used an enumeration type in your definition that was changed later, sorry but as I said this was a long-standing problem).

Modula-3 designers did a perfect job because the true opaque data type is the OBJECT type (that is only a reference type).
Although enumeration types are supported, they are discouraged (even in Exception).
They indeed introduced an (opaque) Atom.T object:

http://www.opencm3.net/doc/help/gen_html/libm3/src/atom/Atom.i3.html

That was used in place of enumeration types for something that grows over the time (such error messages):

http://www.opencm3.net/doc/help/gen_html/tcp/src/common/IP.i3.html

TYPE EC = AtomList.T;

EXCEPTION Error(EC);
[...]

VAR
  LookupFailure, Unreachable, PortBusy, NoResources: Atom.T;


You can add new errors or exceptions without invalidating your clients.

This is the correct way to program strong interfaces.

--
Diego Sardina


More information about the Oberon mailing list