Enumeration Type Proposals ========================== With Jiada's recent work on enumerations (see doc/theses/jiada_liang_MMath/), this is a collection point for some remaining issues with and ideas to further improve enumerations. Fixed Encoding -------------- Because Cforall enumerations are encoded using their position, it can be difficult to give them a stable encoding, this is important in seperate compilation. The example (provided by Gregor Richards), is a system header that defines any type that has to be stable across versions. Let's say error codes. ```cfa enum() BigLibError! { BadArgument, ... MissingConfig, LastStartupError = MissingConfig, NoMemory, Timeout, ... }; ``` The actual errors are not important, but note that "LastStartupError" has to be in a particular location relative to some others. If a new version of the header wants to add a new startup error, it should go before the LastStartupError, but that will change the position, and hence the encoding, of all the remaining The most obvious example in an existing lanuage I could find is that Rust usually treats its enum types as opaques algebraic data types, but in certain cases allows you to fix the encoding of enumerations. (Although the motivation seems to be optimization of enumerations that have a lot of common options.) Enumerated Arrays ----------------- Arrays that use an enumeration as their index. The entire enumeration type (instead of a subset of int) is used in the index operation. ```cfa enum() Colour { Red, Violet, Blue, Green Yellow, Orange, }; // Declare an array with an index of an enumeration: int jarsOfPaint[Colour] = {0}; // Index the array: printf("I have %d jars of blue paint.\n", jarsOfPaint[Blue]); jarsOfPaint[Green] = 3; jarsOfPaint[Red] += 1; // Use the function for higher order programming: int (*lookup)(int collection[Colour], Colour key) = ?[?]; // ERROR! Use the enumeration index for safety: jarsOfPaint[0] = 0; ``` Although described differently, this is actually a generalization of typed enumerations, as it can be used to safely represent a constant of any type for each possible enumeration value. ```cfa extern string colourNames[Colour]; ``` This example is a forward declaration that declares the symbol but does not give the values or allocate any storage. This is used in header files. The type of colourNames would be a new type `string[Colour]`. In implementation tiles it is safe to give the array's values; whether it the array has been previously forward declared or not. ```cfa string colourNames[Colour] = { "red", "violet", "blue", // Or without worrying about ordering: [Green] = "green", [Orange] = "orange", [Yellow] = "yellow", }; ``` The forward declaration and full definition variants allow the user to manage memory themselves, following the same rules as `extern` variables. The user can use `const` to fix the values in the array. These arrays can also be nested `BlendInfo blend[Colour][Colour]` or used locally. Except for the index type (and that the size of the array is fixed per index type, as it always covers the whole enumeration) it should be the same as a traditional array. Or one of the new safer Cforall arrays, as the features could be combined. (Previously, a combined declaration to declare both an enumeration and an enumerated array was proposed. That only covers the simple case that typed enumerations already cover.) Enumeration Ranges ------------------ We have the simplest iterate over a range of enumerations (can only be used directly in a for loop, always covers the entire type) but it could be generalized to work with the other features of ranges, such as going over just part of the enumeration (see Ranges in doc/proposals/iterators.md). This will work best with some alias labels that mark out the beginning of ranges. That is the ranges within the enum will often have to be an intended part of the interface. ```cfa for ( kind : DataKind.BeginIntegers +~ DataKind.EndIntegers ) { ... } ``` Writing the declaration is a bit tricker, because of the lack of aliasing, but this should echo a common C pattern. Flag Set Enumerations --------------------- Another common use of enumerations is as a named bitset. This doesn't actually follow from the logical definition of enumerations, but is something that various implementation of "enum" have commonly been used to recreate. This would formalize that, providing an easy way to create typesafe implementations of this pattern. ```cfa enum Directions flag { Up, Down, Left, Right, Upwards = Up, Vertical = Up | Down, }; ``` Some example usages: ```cfa // If it is exactly Up/Upwards, then set exactly Down if ( Upwards == dirs ) { dirs = Down // Otherwise, if a vertical is set, unset them: } else if ( Vertical & dirs ) { dirs = dirs & ~Vertical; // Otherwise, if any direction is set then also set Up: } else if ( dirs ) { dirs |= Up; } ``` Uses the existing enumeration syntax, except that all initializers must be bitwise expressions, using only the operators |, & and ~ and, as leaf values, other labels from the enumeration (no cycles) and 0. Each uninitialized label creates a new flag. Every instance of the enumeration will have each flag be set or unset. The labels act as instances of the enumeration with only that flag set. A type created this way automatically supports: default construction, from zero_t construction, copy construction, copy assignment, destruction, equality, inequality and bitwise and (&), or (|) and not (~). Default construction and from zero_t construction create an instance with no flags set. Two instances are the same if the same flags are set. Bitwise operations act on the individual flags in the set. In addition the type can be converted to a Boolean. An flag set is truthy if any flags are set and falsy if no flags are set. This is not a primitive operation, but comes from combining the zero_t constructor and inequality. Note: Scoping rules are also waiting on the namespacing and module system. Feature (and Storage) Control ----------------------------- Right now features are very coursely grouped. You have exactly three options for your enumeration. However since there are more than two features this means there are some combinations you cannot have. For instance, labels (which are mostly useful for generating debug output) are not available for C style enum, but for both of the new Cforall enums, opaque and typed. However, there is no innate connection between the additional type safety of the opaque enum or the associated values/payloads of the typed enums. Enumerations do interact with on feature that shows this orthagonality, and that is the scoping "no export" marker, that can be applied to any enumeration to change the visibility rules of the enumeration and does not change anything else. This is not urgent, just not using the features you don't want is almost as clear and the compile-time, binary-size and runtime costs are all good enough for now (and some day all of those may have to be improved even when the feature is being used). Isolating independent features is just good design.