Enumerations – One Step Back, Next Step Rust

When the Oberon System was released by Profs Wirth and Gutkneckt the time seemed right to decide whether to consider Oberon as a language for implementing future systems. Until then Modula-2 had been the language of choice for real time and safety critical software and systems because of its ease of use, simplicity, strong typing and support for modules. The clear separation of DEFINITION modules and their associated IMPLEMENTATION modules had become a foundation of our development process.

Several practical niggles with Oberon became apparent though.
The Definition Module now been effectively eliminated, or at least subsumed onto the IMPLEMENTATION module by using hieroglyphic text markers added to a subset of exported elements. This no doubt simplified the compiler but made creating multi computer systems which shared modules between systems more difficult to manage. I suspect this was do to meet the seeming holy grail of a smaller and smaller compiler. The second problem was the integration of the compiler as part of the operating system. Again this probably saved code, minimisation was clearly a very strong design goal for Oberon, but for mainstream development using the Oberon System was a ‘no-no’ due to it quirky use of the mouse, and its very limiting GUI design and interaction style.
I know from talking to students at ETH who had to use it at the time that the system was not at all well received by them! Although Swiss etiquette, particularly in Academia, meant that such opinions could not be openly relayed to the ‘management’.

Strangely the final decider for me was the enumeration type, it had been removed from the language. As we say the baby had been thrown out with the bathwater. In Modula-2 and other languages the enumeration type had been used to define a list of distinct values as a type, a variable of that type could only validly have one of the listed values. Statements like the CASE statement could be used to implement choices based on the distinct set of values. We happened to use enumeration types widely in our work to represent the distinct states of the finite state machines as an abstraction for defining our program control structures in a rigorous, clearly visible and stylised way. Once again this design choice looked like a ruse to simplify the compiler, the Holy Grail again. Professor Wirth proposed that enumerations could be implemented by just defining constants and using arbitrary collections of them to represent things like finite state machine states. But of course the compile time checking of the enumerations and their use became impossible (ergo simpler compiler) and it also became harder to export a set of distinct values as a logically cohesive set. So again the compiler was made smaller and simpler by eliminating language features that promoted safety via compile time checking. For me that was the last straw – compromising application program integrity to achieve a smaller faster compiler – bad priorities and bad design. A further nail in the coffin of Oberon for us was Wirth’s assertion that ‘enumerations types’ were not extensible i.e. it wasn’t possible to dynamically add additional distinct values to the previously defined set. It was as Monsieur Poirot would say ” Un Herang Rouge”. Well, as we shall see that is simply not true in general.

So we continued using Modula-2 for as long as possible but commercial pressures (the madness of crowds) forced us in the end to use C sparingly (a boil), C++ cautiously (a wart on a boil) and C# contrarily (cosmetic surgery), JavaScript etc. – the mainstream development languages. Those languages are the software equivalent of American mainstream food brands, perhaps Pizza Hut, Mc Donalds and Subway … just a rather indigestible thought. Fortunately now the Rust language has come along and the fetters of Oberon have been gloriously abandonned. Rust is focused on doing as much checking of a program at compile time as possible. The reason is simple errors are much cheaper and quicker to pick up and fix in the earlier stages of program development. Now the internet hosts vast systems and the cost of an operational failure due to a program crash is huge (in cash and reputation) strongly typed languages are thus now essential for creating reliable software.

Other changes have been the new freedom to express enumerations in namespaces and with values of variables associated with each of the distinct enumerated values. For example in Rust we could have a declaration of an enumeration as enum IpAddrKind { V4, V6, }

enum IpAddrKind { V4, V6, } 

where V4 and V6 are the names of the distinct values defined beforehand or with the option of their own name spaces:

We can create instances of each of the two variants of IpAddrKind like this:

    let four = IpAddrKind::V4;
    let six = IpAddrKind::V6;

Note that the variants of the enum are namespaced under its identifier, a double colon separates the two. The reason this is useful is that now both values IpAddrKind::V4 and IpAddrKind::V6 are of the same type: IpAddrKind. We can then, for instance, define a function that takes any IpAddrKind:

fn route(ip_kind: IpAddrKind) {}

And we can call this function with either variant:

    route(IpAddrKind::V4);
    route(IpAddrKind::V6);

Furthermore variables can be associated with the distinct values

enum IpAddr {
        V4(String),
        V6(String),
    }
    let home = IpAddr::V4(String::from("127.0.0.1"));
    let loopback = IpAddr::V6(String::from("::1"));

Data can be attached to each variant of the enum directly, so there is no need for an extra struct(ure) and transliteration errors. Here it’s also easier to see another detail of how enums work: the name of each enum variant that we define also becomes a function that constructs an instance of the enum. That is, IpAddr::V4() is a function call that takes a String argument and returns an instance of the IpAddr type. We automatically get this constructor function defined as a result of defining the enum.

That’s why I asserted earlier that the baby had been thrown out with the bathwater. The program examples given above are in Rust and taken from The Rust Book which is freely available on the net at
https://doc.rust-lang.org/book/ch06-01-defining-an-enum.html

Some bits of history along the way, first a Paper by Cheryl Lins outlining how enumerations could be ’emulated’ in Oberon programs

The brief Paper by Brian Kirk outlined a scheme for making enumerations extensible, the ideas were first mooted for an extension to Modula-2 in 2009 by B. Kowarsch & R. Sutcliffe. The document shown here is their Copyright.

For practical purposes the Rust language and its excellent documentation, tutorials, forums and development tools is all you need for developing verifiable, reliable and maintainable sequential and concurrent software for modern single and multicore based systems. It is a credit to the Rust Open Source Development Team and Community and also to the Mozilla Corporation who initially sponsored its development and made it widely available at no cost.