[Oberon] Why no enumeration types?
dsar at eml.cc
Mon Jul 26 07:09:00 CEST 2021
On Sun, Jul 25, 2021, at 11:06 PM, Timothy Pearson wrote:
> The paper is an interesting viewpoint: I suspect many of the arguments
> could be made equally well, almost 'as is', -for- enumerations rather
> than against. I hold my hand up over never having had to professionally
> program in Oberon, and in general I love its simplicity; but I do
> wonder if Oberon takes simplification too far by axing this construct:
> you lose many good things.
> Examples include,
> - having manually to create unique numerical constants for each
> enumerated value… in fact, for a long list of constants being used to
> represent enumerated values, it feels like the safest thing would be to
> generate the source using a script
> - no cohesion of related values — if you were to use an enumeration as
> an error code, a type checker would normally ensure you weren’t trying
> to pass an unrelated value — be that another constant representing a
> colour of the rainbow or the square root of 4
> - no opportunity for compilers to help ensure completeness (eg in CASEs, etc.)
That paper discusses two of the three major points but it doesn't discuss the most important one: the first.
In earlier "From Modula-2 to Oberon", Wirth wrote about enumeration types that "They defy extensibility over module boundaries". This was removed in later papers because it caused too much confusion and was largely misunderstood. I will try to explain why Wirth removed enumeration types (and should have done it since Modula-2, because this is a long-standing problem).
Enumeration types break separate compilation, that is one of the main feature of modularity.
In a programming language with separate compilation every module can be compiled separately once the interfaces between all modules are enstablished. But there are cases where a change of the interface force all clients to be recompiled, this is something that when occurs should be impeded. Indeed in various Oberon compilers once you generate the symbol table file (that is the interface), you can't change it (except with a flag that force regeneration).
Adding new entities to an enumeration types invalidate the interface of a module and force all clients to be recompiled, this is the main reason of its removal.
For example, a module exports this enumeration type:
TYPE Colors = (red, green, blue);
Later in this module you change the enumeration to:
TYPE Colors = (red, green, yellow, blue);
If you don't recompile all modules that import this module, what happens with this code?
FOR i := MIN(Colors) TO MAX(Colors) DO ... END;
VAR table: ARRAY Colors OF BOOLEAN;
Your program will have unexpected behaviours or will simply crash. To solve this, you have to recompile all the clients.
However not all changes to the interface invalidate clients, these cases may be considered an interface extension. In this paper this topic is further discussed: Regis Crelier - Separate Compilation and Module Extension.
The only way to have enumeration types that don't break separate compilation is to remove their ordering and boundaries (no ORD(), MIN() and MAX(), so can't be used in FOR) and can't be used as index type for ARRAY and as base type for SET. But at this point there is no reason to introduce a type since they are no more different than a sequence of constants declaration.
Java didn't have enumeration in earlier versions for exactly this problem. Later it implemented enumeration types but not as lightweight as in Modula-2 because of the considerable runtime impact (they are no more integers).
Golang doesn't have enumeration types for the same reason and they still keep them out of the language (it has a constant generator called iota to ease the generation of sequences).
So I really don't understand why enumeration types should be included, considering that you can obtain the same goal with type extension (just look Lins paper) while having no problems with separate compilation.
Oh, I forgot to talk about generics... that requires a whole day of writing :-)
More information about the Oberon