Ada's Super Powered Enums
One of the problems of dabbling with the Ada programming language is that some features work so much better than in other languages.
Ada enums are one of those features.
If you're looking for Rust style
enums (sum types), Ada uses variants which are a different concept than that shown here. Ada enums often get used as the "discriminant" (tag) for variants so a lot of concepts get reused.
There is no enum keyword; instead, an enum is a list of values associated with a type.
There are a few things Ada does by default here which are hidden.
Specific "attributes" get provided on Direction values. Ada attributes use tick ' syntax instead of the co-existing and normal . syntax to grab composite information and functions in other languages. Just like . syntax, some attributes are values, others are callable functions. This is where the fun begins.
Let's assume a direction and then see what we can do with it:
Printing
It's often useful to have printable names associated with enums. This helps print something meaningful when debugging. This gets provided by the 'Image attribute.
Ada follows the Uniform Access Principle and parameter-less attributes and functions don't require parentheses, similar to Ruby or F#.
Parsing
Printable names are nice, but what about parsing a name into an enum? For that you use the 'Value attribute.
Values
You can provide values for enums like in other languages.
You can also make the enum a specific bit size using the Size aspect.
Array Indexing
The enum values are fixed. What about using them as array indexes, as if the array were a map with default values?
Ada 2022 uses
[ ... ]for array literals, but previous versions use( ... ). You can still use( ... )if you want though in Ada 2022. In either case, you still use( )for indexing.
We can now directly index, assign and get values from the array using our enum values.
Ada array indexing looks like a function call in other languages.
You can also directly assign an array using the enum values:
Or only assign the values you care about:
Array iteration
How would you iterate over the array? With the enum's 'Range!
Not only is there a range, but there's also the ability to directly move between enum values.
How do you know if you're not going to fall off the end of the enum list? Using 'First and 'Last!
Membership
You can also associate a semantically compatible subtype with an enum. Using Static_Predicate ensures enum conditions at compile-time.
Then you can use these to check for appropriate values at runtime.
You can also use this subtype as a membership test:
Switching
Similar to Rust, we can ensure exhaustive switching off of these values. This also includes testing multiple values at once in a branch. There is no fallthrough between these branches.
You can also use subtypes instead in switches:
If the cases aren't disjoint you'll get a compile error, even when using subtypes with Static_Predicate.
Caveats
Some of these tricks only work when enums have consecutive ranges. Note that is consecutive, not necessarily starting at 0. This is true in general of arrays in Ada... they can start at indices other than 0 or 1!
Where it gets weird
The enum "Values" are technically functions, and are namespaced to the package, not to the type. This means if you have the same name visible multiple times, such as by use of another package, then function overloading actually ensures the correct version of the type gets picked.
That means this is legal:
Summary
Overall, Ada enums provide a powerful way to represent domain-specific values, but also to use them in interesting ways. Being built-in helps avoid a lot of boilerplate while also helping prevent issues like improper indexing and simplifying conditional tests.