\chapter{Related Work} \label{s:RelatedWork} \begin{comment} An algebraic data type (ADT) can be viewed as a recursive sum of product types. A sum type lists values as members. A member in a sum type definition is known as a data constructor. For example, C supports sum types union and enumeration (enum). An enumeration in C can be viewed as the creation of a list of zero-arity data constructors. A union instance holds a value of one of its member types. Defining a union does not generate new constructors. The definition of member types and their constructors are from the outer lexical scope. In general, an \newterm{algebraic data type} (ADT) is a composite type, \ie, a type formed by combining other types. Three common classes of algebraic types are \newterm{array type}, \ie homogeneous types, \newterm{product type}, \ie heterogeneous tuples and records (structures), and \newterm{sum type}, \ie tagged product-types (unions). Enumerated types are a special case of product/sum types with non-mutable fields, \ie initialized (constructed) once at the type's declaration, possible restricted to compile-time initialization. Values of algebraic types are access by subscripting, field qualification, or type (pattern) matching. \end{comment} Enumeration-like features exist in many popular programming languages, both past and present, \eg Pascal~\cite{Pascal}, Ada~\cite{Ada}, \Csharp~\cite{Csharp}, OCaml~\cite{OCaml} \CC, Go~\cite{Go}, Haskell~\cite{Haskell}, Java~\cite{Java}, Rust~\cite{Rust}, Swift~\cite{Swift}, Python~\cite{Python}. Among theses languages, there are a large set of overlapping features, but each language has its own unique extensions and restrictions. \section{Pascal} \label{s:Pascal} Classic Pascal introduced the \lstinline[language=Pascal]{const} aliasing declaration binding a name to a constant literal/expression. \begin{pascal} const one = 0 + 1; Vowels = set of (A,E,I,O,U); NULL = NIL; PI = 3.14159; Plus = '+'; Fred = 'Fred'; \end{pascal} As stated, this mechanism is not an enumeration because there is no specific type (pseudo enumeration). Hence, there is no notion of a (possibly ordered) set, modulo the \lstinline[language=pascal]{set of} type. The type of each constant name (enumerator) is inferred from the constant-expression type. Free Pascal~\cite[\S~3.1.1]{FreePascal} is a modern, object-oriented version of classic Pascal, with a C-style enumeration type. Enumerators must be assigned in ascending numerical order with a constant expression and the range can be non-consecutive. \begin{pascal} Type EnumType = ( one, two, three, forty @= 40@, fortyone ); \end{pascal} Pseudo-functions @Pred@ and @Succ@ can only be used if the range is consecutive. The underlying type is an implementation-defined integral-type large enough to hold all enumerated values; it does not have to be the smallest possible type. The integral size can be explicitly specified using compiler directive @$PACKENUM@~$N$, where $N$ is the number of bytes, \eg: \begin{pascal} Type @{$\color{red}\$$PACKENUM 1}@ SmallEnum = ( one, two, three ); @{$\color{red}\$$PACKENUM 4}@ LargeEnum = ( BigOne, BigTwo, BigThree ); Var S : SmallEnum; { 1 byte } L : LargeEnum; { 4 bytes} \end{pascal} \section{Ada} An Ada enumeration type is a set of ordered unscoped identifiers (enumerators) bound to \emph{unique} \newterm{literals}.\footnote{% Ada is \emph{case-insensitive} so identifiers may appear in multiple forms and still be the same, \eg \lstinline{Mon}, \lstinline{moN}, and \lstinline{MON} (a questionable design decision).} \begin{ada} type Week is ( Mon, Tue, Wed, Thu, Fri, Sat, Sun ); -- literals (enumerators) \end{ada} Object initialization and assignment are restricted to the enumerators of this type. While Ada enumerators are unscoped, like C, Ada enumerators are overloadable. \begin{ada} type RGB is ( @Red@, @Green@, Blue ); type Traffic_Light is ( @Red@, Yellow, @Green@ ); \end{ada} Like \CFA, Ada uses a type-resolution algorithm including the left-hand side of assignmente to disambiguate among overloaded identifiers. \VRef[Figure]{f:AdaEnumeration} shows how ambiguity is handled using a cast, \ie \lstinline[language=ada]{RGB'(Red)}. \begin{figure} \begin{ada} with Ada.Text_IO; use Ada.Text_IO; procedure test is type RGB is ( @Red@, Green, Blue ); type Traffic_Light is ( @Red@, Yellow, Green ); -- overload procedure @Red@( Colour : RGB ) is begin -- overload Put_Line( "Colour is " & RGB'Image( Colour ) ); end Red; procedure @Red@( TL : Traffic_Light ) is begin -- overload Put_Line( "Light is " & Traffic_Light'Image( TL ) ); end Red; begin @Red@( Blue ); -- RGB @Red@( Yellow ); -- Traffic_Light @Red@( @RGB'(Red)@ ); -- ambiguous without cast end test; \end{ada} \caption{Ada Enumeration Overload Resolution} \label{f:AdaEnumeration} \end{figure} Enumerators without initialization are auto-initialized from left to right, starting at zero, incrementing by 1. Enumerators with initialization must set \emph{all} enumerators in \emph{ascending} order, \ie there is no auto-initialization. \begin{ada} type Week is ( Mon, Tue, Wed, Thu, Fri, Sat, Sun ); for Week use ( Mon => 0, Tue => 1, Wed => 2, Thu => @10@, Fri => 11, Sat => 14, Sun => 15 ); \end{ada} The enumeration operators are the equality and relational operators, @=@, @/=@, @<@, @<=@, @=@, @/=@, @>=@, @>@, where the ordering relationship is given implicitly by the sequence of acsending enumerators. Ada provides an alias mechanism, \lstinline[language=ada]{renames}, for aliasing types, which is useful to shorten package identifiers. \begin{ada} @OtherRed@ : RGB renames Red; \end{ada} which suggests a possible \CFA extension to @typedef@. \begin{cfa} typedef RGB.Red OtherRed; \end{cfa} There are three pairs of inverse enumeration pseudo-functions (attributes): @'Pos@ and @'Val@, @'Enum_Rep@ and @'Enum_Val@, and @'Image@ and @'Value@, \begin{cquote} \setlength{\tabcolsep}{15pt} \begin{tabular}{@{}ll@{}} \begin{ada} RGB'Pos( Red ) = 0; RGB'Enum_Rep( Red ) = 10; RGB'Image( Red ) = "RED"; \end{ada} & \begin{ada} RGB'Val( 0 ) = Red RGB'Enum_Val( 10 ) = Red RGB'Value( "Red" ) = Red \end{ada} \end{tabular} \end{cquote} These attributes are important for IO. An enumeration type @T@ also has the following attributes: @T'First@, @T'Last@, @T'Range@, @T'Pred@, @T'Succ@, @T'Min@, and @T'Max@, producing an intuitive result based on the attribute name. Ada allows the enumerator label to be a character constant. \begin{ada} type Operator is ( '+', '-', '*', '/' ); \end{ada} which is syntactic sugar for the label and not character literals from the predefined type @Character@. The purpose is strictly readability using character literals rather than identifiers. \begin{ada} Op : Operator := '+'; if Op = '+' or else Op = '-' then ... ; elsif Op = '*' or else Op = '/' then ... ; end if; \end{ada} Interestingly, arrays of character enumerators can be treated as strings. \begin{ada} Ops : array( 0..3 ) of Operator; Ops := @"+-*/"@; -- string assignment to array elements Ops := "+-" @&@ "*/"; -- string concatenation and assignment \end{ada} Ada's @Character@ type is defined as a character enumeration across all Latin-1 characters. Ada's boolean type is also a special enumeration, which can be used in conditions. \begin{ada} type Boolean is (False, True); -- False / True not keywords @Flag@ : Boolean; if @Flag@ then ... -- conditional \end{ada} Since only types derived from @Boolean@ can be a conditional, @Boolean@ is essentially a builtin type. Ada provides \emph{consecutive} subtyping of an enumeration using \lstinline[language=ada]{range}. \begin{ada} type Week is ( Mon, Tue, Wed, Thu, Fri, Sat, Sun ); subtype Weekday is Week @range Mon .. Fri@; subtype Weekend is Week @range Sat .. Sun@; Day : Week; \end{ada} Hence, the ordering of the enumerators is crucial to provide the necessary ranges. An enumeration type can be used in the Ada \lstinline[language=ada]{case} (all enumerators must appear or a @default@) or iterating constructs. \begin{cquote} \setlength{\tabcolsep}{15pt} \begin{tabular}{@{}ll@{}} \begin{ada} case Day is when @Mon .. Fri@ => ... ; when @Sat .. Sun@ => ... ; end case; \end{ada} & \begin{ada} case Day is when @Weekday@ => ... ; -- subtype ranges when @Weekend@ => ... ; end case; \end{ada} \end{tabular} \end{cquote} \begin{cquote} \setlength{\tabcolsep}{12pt} \begin{tabular}{@{}lll@{}} \begin{ada} for Day in @Mon .. Sun@ loop ... end loop; \end{ada} & \begin{ada} for Day in @Weekday@ loop ... end loop; \end{ada} & \begin{ada} for Day in @Weekend@ loop ... end loop; \end{ada} \end{tabular} \end{cquote} An enumeration type can be used as an array dimension and subscript. \begin{ada} Lunch : array( @Week@ ) of Time; for Day in Week loop Lunch( @Day@ ) := ... ; -- set lunch time end loop; \end{ada} \section{\CC} \label{s:C++RelatedWork} \CC enumeration is largely backwards compatible with C, so it inherited C's enumerations with some modifications and additions. \CC has aliasing using @const@ declarations, like C \see{\VRef{s:Cconst}}, with type inferencing, plus static/dynamic initialization. (Note, a \CC @constexpr@ declaration is the same as @const@ with the restriction that the initialization is a compile-time expression.) \begin{c++} const @auto@ one = 0 + 1; $\C{// static initialization}$ const @auto@ NIL = nullptr; const @auto@ PI = 3.14159; const @auto@ Plus = '+'; const @auto@ Fred = "Fred"; const @auto@ Mon = 0, Tue = Mon + 1, Wed = Tue + 1, Thu = Wed + 1, Fri = Thu + 1, Sat = Fri + 1, Sun = Sat + 1; void foo() { const @auto@ r = random(); $\C{// dynamic initialization}$ int va[r]; $\C{// VLA, auto scope only}$ } \end{c++} Statically initialized identifiers may appear in any constant-expression context, \eg @case@. Dynamically initialized identifiers may appear as array dimensions in @g++@, which allows variable-sized arrays. Interestingly, global \CC @const@ declarations are implicitly marked @static@ (@r@, read-only local, rather than @R@, read-only external) \begin{c++} $\$$ nm test.o 0000000000000018 @r@ Mon \end{c++} whereas C @const@ declarations without @static@ are marked @R@. The following \CC non-backwards compatible changes are made \see{\cite[\S~7.2]{ANSI98:c++}}. \begin{cquote} Change: \CC objects of enumeration type can only be assigned values of the same enumeration type. In C, objects of enumeration type can be assigned values of any integral type. \\ Example: \begin{c++} enum color { red, blue, green }; color c = 1; $\C{// valid C, invalid c++}$ \end{c++} \textbf{Rationale}: The type-safe nature of \CC. \\ \textbf{Effect on original feature}: Deletion of semantically well-defined feature. \\ \textbf{Difficulty of converting}: Syntactic transformation. (The type error produced by the assignment can be automatically corrected by applying an explicit cast.) \\ \textbf{How widely used}: Common. \end{cquote} \begin{cquote} Change: In \CC, the type of an enumerator is its enumeration. In C, the type of an enumerator is @int@. \\ Example: \begin{c++} enum e { A }; sizeof(A) == sizeof(int) $\C{// in C}$ sizeof(A) == sizeof(e) $\C{// in c++}$ /* and sizeof(int) is not necessary equal to sizeof(e) */ \end{c++} \textbf{Rationale}: In \CC, an enumeration is a distinct type. \\ \textbf{Effect on original feature}: Change to semantics of well-defined feature. \\ \textbf{Difficulty of converting}: Semantic transformation. \\ \textbf{How widely used}: Seldom. The only time this affects existing C code is when the size of an enumerator is taken. Taking the size of an enumerator is not a common C coding practice. \end{cquote} Hence, the values in a \CC enumeration can only be its enumerators (without a cast). While the storage size of an enumerator is up to the compiler, there is still an implicit cast to @int@. \begin{c++} enum E { A, B, C }; E e = A; int i = A; i = e; $\C{// implicit casts to int}$ \end{c++} \CC{11} added a scoped enumeration, \lstinline[language=c++]{enum class} (or \lstinline[language=c++]{enum struct})\footnote{ The use of keyword \lstinline[language=c++]{class} is resonable because default visibility is \lstinline[language=c++]{private} (scoped). However, default visibility for \lstinline[language=c++]{struct} is \lstinline[language=c++]{public} (unscoped) making it an odd choice.}, where the enumerators are accessed using type qualification. \begin{c++} enum class E { A, B, C }; E e = @E::@A; $\C{// qualified enumerator}$ e = B; $\C{// error: B not in scope}$ \end{c++} \CC{20} supports explicit unscoping with a \lstinline[language=c++]{using enum} declaration. \begin{c++} enum class E { A, B, C }; @using enum E;@ E e = A; e = B; $\C{// direct access}$ \end{c++} \CC{11} added the ability to explicitly declare only an underlying \emph{integral} type for \lstinline[language=c++]{enum class}. \begin{c++} enum class RGB @: long@ { Red, Green, Blue }; enum class rgb @: char@ { Red = 'r', Green = 'g', Blue = 'b' }; enum class srgb @: signed char@ { Red = -1, Green = 0, Blue = 1 }; \end{c++} There is no implicit conversion from the \lstinline[language=c++]{enum class} type to its declared type. \begin{c++} rgb crgb = rgb::Red; char ch = rgb::Red; ch = crgb; $\C{// error}$ \end{c++} An enumeration can be used in the @if@ and @switch@ statements. \begin{cquote} \setlength{\tabcolsep}{15pt} \begin{tabular}{@{}ll@{}} \begin{c++} if ( @day@ <= Fri ) cout << "weekday" << endl; \end{c++} & \begin{c++} switch ( @day@ ) { case Mon: case Tue: case Wed: case Thu: case Fri: cout << "weekday" << endl; break; case Sat: case Sun: cout << "weekend" << endl; break; } \end{c++} \end{tabular} \end{cquote} However, there is no mechanism to iterate through an enumeration without an unsafe cast and it does not understand the enumerator values. \begin{c++} enum Week { Mon, Tue, Wed, Thu = 10, Fri, Sat, Sun }; for ( Week d = Mon; d <= Sun; d = @(Week)(d + 1)@ ) cout << d << ' '; 0 1 2 @3 4 5 6 7 8 9@ 10 11 12 13 \end{c++} An enumeration type cannot declare an array dimension but an enumerator can be used as a subscript. There is no mechanism to subtype or inherit from an enumeration. \section{C\raisebox{-0.7ex}{\LARGE$^\sharp$}\xspace} % latex bug: cannot use \relsize{2} so use \LARGE \label{s:Csharp} % https://www.tutorialsteacher.com/codeeditor?cid=cs-mk8Ojx % https://learn.microsoft.com/en-us/dotnet/api/system.enum?view=net-8.0 % https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/enums \Csharp is a dynamically-typed programming-language with a scoped, integral enumeration similar to \CC \lstinline[language=C++]{enum class}. \begin{csharp} enum Week : @long@ { Mon, Tue, Wed, Thu@ = 10@, Fri, Sat, Sun@,@ } // terminating comma enum RGB { Red, Green, Blue } \end{csharp} The default underlying integral type is @int@ (no @char@), with auto-incrementing, implicit/explicit initialization, and terminating comma. A method cannot be defined in an enumeration type (extension methods are possible). There is an explicit bidirectional conversion between an enumeration and its integral type, and an implicit conversion to the enumerator label in display contexts. \begin{csharp} int iday = (int)Week.Fri; $\C{// day == 11}$ Week day = @(Week)@42; $\C{// day == 42, unsafe}$ string mon = Week.Mon.ToString(); $\C{// mon == "Mon"}$ RGB rgb = RGB.Red; $\C{// rgb == "Red"}$ day = @(Week)@rgb; $\C{// day == "Mon", unsafe}$ Console.WriteLine( Week.Fri ); $\C{// print label Fri}$ \end{csharp} The majority of the integral operators (relational and arithmetic) work with enumerations, except @*@ and @/@. \begin{csharp} day = day++ - 5; $\C{// unsafe}$ day = day & day; \end{csharp} An enumeration can be used in the @if@ and @switch@ statements. \begin{cquote} \setlength{\tabcolsep}{15pt} \begin{tabular}{@{}ll@{}} \begin{csharp} if ( @day@ <= Week.Fri ) Console.WriteLine( "weekday" ); \end{csharp} & \begin{csharp} switch ( @day@ ) { case Week.Mon: case Week.Tue: case Week.Wed: case Week.Thu: case Week.Fri: Console.WriteLine( "weekday" ); break; case Week.Sat: case Week.Sun: Console.WriteLine( "weekend" ); break; } \end{csharp} \end{tabular} \end{cquote} However, there is no mechanism to iterate through an enumeration without an unsafe cast to increment and positions versus values is not handled. \begin{csharp} for ( Week d = Mon; d <= Sun; @d += 1@ ) { Console.Write( d + " " ); } Mon Tue Wed @3 4 5 6 7 8 9@ Thu Fri Sat Sun \end{csharp} The @Enum.GetValues@ pseudo-method retrieves an array of the enumeration constants for looping over an enumeration type or variable (expensive operation). \begin{csharp} foreach ( Week d in @Enum.GetValues@( typeof(Week) ) ) { Console.WriteLine( d + " " + (int)d + " " ); // label, position } Mon 0, Tue 1, Wed 2, Thu 10, Fri 11, Sat 12, Sun 13, \end{csharp} Hence, enumerating is not supplied directly by the enumeration, but indirectly through another enumerable type, array. An enumeration type cannot declare an array dimension but an enumerator can be used as a subscript. There is no mechanism to subtype or inherit from an enumeration. The @Flags@ attribute creates a bit-flags enumeration, making bitwise operators @&@, @|@, @~@ (complement), @^@ (xor) sensible. \begin{csharp} @[Flags]@ public enum Week { None = 0x0, Mon = 0x1, Tue = 0x2, Wed = 0x4, Thu = 0x8, Fri = 0x10, Sat = 0x20, Sun = 0x40, Weekdays = @Mon | Tue | Wed | Thu | Fri@ $\C{// Weekdays == 0x1f}$ Weekend = @Sat | Sun@, $\C{// Weekend == 0x60}$ } Week meetings = @Week.Mon | Week.Wed@; $\C{// 0x5}$ \end{csharp} \section{Golang} \label{s:Golang} Golang has a no enumeration. It has @const@ aliasing declarations, similar to \CC \see{\VRef{s:C++RelatedWork}}, for basic types with type inferencing and static initialization (constant expression). \begin{Go} const R @int@ = 0; const G @uint@ = 1; const B = 2; $\C{// explicit typing and type inferencing}$ const Fred = "Fred"; const Mary = "Mary"; const Jane = "Jane"; const S = 0; const T = 0; const USA = "USA"; const U = "USA"; const V = 3.1; const W = 3.1; \end{Go} Since these declarations are unmutable variables, they are unscoped and Golang has no overloading. Golang provides an enumeration-like feature to group together @const@ declaration into a block and introduces a form of auto-initialization. \begin{Go} const ( R = 0; G; B ) $\C{// implicit initialization: 0 0 0}$ const ( Fred = "Fred"; Mary = "Mary"; Jane = "Jane" ) $\C{// explicit initialization: Fred Mary Jane}$ const ( S = 0; T; USA = "USA"; U; V = 3.1; W ) $\C{// type change, implicit/explicit: 0 0 USA USA 3.1 3.1}$ \end{Go} The first identifier \emph{must} be explicitly initialized; subsequent identifiers can be implicitly or explicitly initialized. Implicit initialization is the \emph{previous} (predecessor) identifier value. Each @const@ declaration provides an implicit integer counter starting at zero, called \lstinline[language=Go]{iota}. Using \lstinline[language=Go]{iota} outside of a @const@ block always sets the identifier to zero. \begin{Go} const R = iota; $\C{// 0}$ \end{Go} Inside a @const@ block, \lstinline[language=Go]{iota} is implicitly incremented for each \lstinline[language=golang]{const} identifier and used to initialize the next uninitialized identifier. \begin{Go} const ( R = @iota@; G; B ) $\C{// implicit: 0 1 2}$ const ( C = @iota + B + 1@; G; Y ) $\C{// implicit: 3 4 5}$ \end{Go} An underscore \lstinline[language=golang]{const} identifier advances \lstinline[language=Go]{iota}. \begin{Go} const ( O1 = iota + 1; @_@; O3; @_@; O5 ) // 1, 3, 5 \end{Go} Auto-initialization reverts from \lstinline[language=Go]{iota} to the previous value after an explicit initialization, but auto-incrementing of \lstinline[language=Go]{iota} continues. \begin{Go} const ( Mon = iota; Tue; Wed; // 0, 1, 2 @Thu = 10@; Fri; Sat; Sun = itoa ) // 10, 10, 10, 6 \end{Go} Auto-initialization from \lstinline[language=Go]{iota} is restarted and \lstinline[language=Go]{iota} reinitialized with an expression containing as most \emph{one} \lstinline[language=Go]{iota}. \begin{Go} const ( V1 = iota; V2; @V3 = 7;@ V4 = @iota@ + 1; V5 ) // 0 1 7 4 5 const ( Mon = iota; Tue; Wed; // 0, 1, 2 @Thu = 10;@ Fri = @iota - Wed + Thu - 1@; Sat; Sun ) // 10, 11, 12, 13 \end{Go} Here, @V4@ and @Fri@ restart auto-incrementing from \lstinline[language=Go]{iota} and reset \lstinline[language=Go]{iota} to 4 and 11, respectively, because of the intialization expressions containing \lstinline[language=Go]{iota}. Note, because \lstinline[language=Go]{iota} is incremented for an explicitly initialized identifier or @_@, at @Fri@ \lstinline[language=Go]{iota} is 4 requiring the minus one to compute the value for @Fri@. Basic switch and looping are possible. \begin{cquote} \setlength{\tabcolsep}{20pt} \begin{tabular}{@{}ll@{}} \begin{Go} day := Mon; // := $\(\Rightarrow\)$ type inferencing switch @day@ { case Mon, Tue, Wed, Thu, Fri: fmt.Println( "weekday" ); case Sat, Sun: fmt.Println( "weekend" ); } \end{Go} & \begin{Go} for i := @Mon@; i <= @Sun@; i += 1 { fmt.Println( i ) } \end{Go} \end{tabular} \end{cquote} However, the loop prints the values from 0 to 13 because there is no actual enumeration. A constant variable can be used as an array dimension or a subscript. \begin{Go} var ar[@Sun@] int ar[@Mon@] = 3 \end{Go} \section{Java} Java provides an enumeration using a specialized class. A basic Java enumeration is an opaque enumeration, where the enumerators are constants. \begin{Java} enum Week { Mon, Tue, Wed, Thu, Fri, Sat, Sun; } Week day = Week.Sat; \end{Java} The enumerators members are scoped and cannot be made \lstinline[language=java]{public}, hence require qualification. The value of an enumeration instance is restricted to its enumerators. The position (ordinal) and label are accessible but there is no value. \begin{Java} System.out.println( day.!ordinal()! + " " + !day! + " " + day.!name()! ); 5 Sat Sat \end{Java} Since @day@ has no value, it prints its label (name). The member @valueOf@ is the inverse of @name@ converting a string to enumerator. \begin{Java} day = Week.valueOf( "Wed" ); \end{Java} Extra members can be added to provide specialized operations. \begin{Java} public boolean isWeekday() { return !ordinal()! <= Fri.ordinal(); } public boolean isWeekend() { return Sat.ordinal() <= !ordinal()!; } \end{Java} Notice the unqualified calls to @ordinal@ in the members implying a \lstinline[language=Java]{this} to some implicit implementation variable, likely an @int@. Enumerator values require an enumeration type (any Java type may be used) and implementation member. \begin{Java} enum Week { Mon!(1)!, Tue!(2)!, Wed!(3)!, Thu!(4)!, Fri!(5)!, Sat!(6)!, Sun!(7)!; // must appear first private !long! day; $\C{// enumeration type and implementation member}$ private Week( !long! d ) { day = d; } $\C{// enumerator initialization}$ }; Week day = Week.Sat; \end{Java} The position, value, and label are accessible. \begin{Java} System.out.println( !day.ordinal()! + " " + !day.day! + " " + !day.name()! ); 5 6 Sat \end{Java} If the implementation member is \lstinline[language=Java]{public}, the enumeration is unsafe, as any value of the underlying type can be assigned to it, \eg @day = 42@. The implementation constructor must be private since it is only used internally to initialize the enumerators. Initialization occurs at the enumeration-type declaration for each enumerator in the first line. Enumerations can be used in the @if@ and @switch@ statements but only for equality tests. \begin{cquote} \setlength{\tabcolsep}{15pt} \begin{tabular}{@{}ll@{}} \begin{Java} if ( !day! == Week.Fri ) System.out.println( "Fri" ); \end{Java} & \begin{Java} switch ( !day! ) { case Mon: case Tue: case Wed: case Thu: case Fri: System.out.println( "weekday" ); break; case Sat: case Sun: System.out.println( "weekend" ); break; } \end{Java} \end{tabular} \end{cquote} Notice enumerators in the @switch@ statement do not require qualification. There are no arithemtic operations on enumerations, so there is no arithmetic way to iterate through an enumeration without making the implementation type \lstinline[language=Java]{public}. Like \Csharp, looping over an enumeration is done using method @values@, which returns an array of enumerator values (expensive operation). \begin{Java} for ( Week d : Week.values() ) { System.out.print( d.ordinal() + d.day + " " + d.name() + ", " ); } 0 1 Mon, 1 2 Tue, 2 3 Wed, 3 4 Thu, 4 5 Fri, 5 6 Sat, 6 7 Sun, \end{Java} Like \Csharp, enumerating is supplied indirectly through another enumerable type, not via the enumeration. An enumeration type cannot declare an array dimension nor can an enumerator be used as a subscript. Enumeration inheritence is disallowed because an enumeration is \lstinline[language=Java]{final}. Java provides an @EnumSet@ where the underlying type is an efficient set of bits, one per enumeration \see{\Csharp \lstinline{Flags}, \VRef{s:Csharp}}, providing (logical) operations on groups of enumerators. There is also a specialized version of @HashMap@ with enumerator keys, which has performance benefits. \section{Rust} % https://doc.rust-lang.org/reference/items/enumerations.html Rust @enum@ provides two largely independent mechanisms from a single language feature: an ADT and an enumeration. When @enum@ is an ADT, pattern matching is used to discriminate among the variant types. \begin{cquote} \begin{tabular}{@{}l@{\hspace{30pt}}ll@{}} \begin{rust} struct S { i : isize, j : isize } let mut s = S{ i : 3, j : 4 }; enum @ADT@ { I( isize ), $\C[1in]{// int}$ F( f64 ), $\C{// float}$ S( S ), $\C{// struct}\CRT$ } \end{rust} & \begin{rust} let mut adt : ADT; adt = ADT::I(3); println!( "{:?}", adt ); adt = ADT::F(3.5); println!( "{:?}", adt ); adt = ADT::S(s); println!( "{:?}", adt ); @match@ adt { ADT::I( i ) => println!( "{:}", i ), ADT::F( f ) => println!( "{:}", f ), ADT::S( s ) => println!( "{:} {:}", s.i, s.j ), } \end{rust} & \begin{rust} I(3) F(3.5) S(S { i: 3, j: 4 }) 3 4 \end{rust} \end{tabular} \end{cquote} Even when the variant types are the unit type, the ADT is still not an enumeration because there is no enumerating \see{\VRef{s:AlgebraicDataType}}. \begin{rust} enum Week { Mon, Tues, Wed, Thu, Fri, Sat, Sun@,@ } // terminating comma let mut week : Week = Week::Mon; match week { Week::Mon => println!( "Mon" ), ... Week::Sun => println!( "Sun" ), } \end{rust} However, Rust allows direct setting of the ADT constructor, which means it is actually a tag. \begin{cquote} \setlength{\tabcolsep}{15pt} \begin{tabular}{@{}ll@{}} \begin{rust} enum Week { Mon, Tues, Wed, // start 0 Thu @= 10@, Fri, Sat, Sun, } \end{rust} & \begin{rust} #[repr(u8)] enum ADT { I(isize) @= 5@, // ??? F(f64) @= 10@, S(S) @= 0@, } \end{rust} \end{tabular} \end{cquote} Through this integral tag, it is possible to enumerate, and when all tags represent the unit type, it behaves like \CC \lstinline[language=C++]{enum class}. When tags represent non-unit types, Rust largely precludes accessing the tag because the semantics become meaningless. Hence, the two mechanisms are largely disjoint, and ony the enumeration component is discussed. In detail, the @enum@ type has an implicit integer tag (discriminant), with a unique value for each variant type. Direct initialization is by a compile-time expression generating a constant value. Indirect initialization (without initialization, @Fri@/@Sun@) is auto-initialized: from left to right, starting at zero or the next explicitly initialized constant, incrementing by @1@. There is an explicit cast from the tag to integer. \begin{rust} let mut mon : isize = Week::Mon as isize; \end{rust} An enumeration can be used in the @if@ and \lstinline[language=rust]{match} (@switch@) statements. \begin{cquote} \setlength{\tabcolsep}{8pt} \begin{tabular}{@{}ll@{}} \begin{c++} if @week as isize@ == Week::Mon as isize { println!( "{:?}", week ); } \end{c++} & \begin{c++} match @week@ { Week::Mon | Week:: Tue | Week::Wed | Week::Thu | Week::Fri => println!( "weekday" ), Week::Sat | Week:: Sun => println!( "weekend" ), } \end{c++} \end{tabular} \end{cquote} However, there is no mechanism to iterate through an enumeration without casting to integral and positions versus values is not handled. \begin{c++} for d in Week::Mon as isize ..= Week::Sun as isize { print!( "{:?} ", d ); } 0 1 2 @3 4 5 6 7 8 9@ 10 11 12 13 \end{c++} An enumeration type cannot declare an array dimension nor as a subscript. There is no mechanism to subtype or inherit from an enumeration. \section{Swift} % https://www.programiz.com/swift/online-compiler Like Rust, Swift @enum@ provides two largely independent mechanisms from a single language feature: an ADT and an enumeration. When @enum@ is an ADT, pattern matching is used to discriminate among the variant types. \begin{cquote} \setlength{\tabcolsep}{20pt} \begin{tabular}{@{}l@{\hspace{55pt}}ll@{}} \begin{swift} struct S { var i : Int, j : Int } var s = S( i : 3, j : 5 ) @enum@ ADT { case I(Int) $\C[1.125in]{// int}$ case F(Float) $\C{// float}$ case S(S) $\C{// struct}\CRT$ } \end{swift} & \begin{swift} var adt : ADT adt = .I( 3 ); print( adt ) adt = .F( 3.5 ); print( adt ) adt = .S( s ); print( adt ) @switch@ adt { // pattern matching case .I(let i): print( i ) case .F(let f): print( f ) case .S(let s): print( s.i, s.j ) } \end{swift} & \begin{swift} I(3) F(3.5) S(S(i: 3, j: 5)) 3 5 \end{swift} \end{tabular} \end{cquote} (Note, after an @adt@'s type is know, the enumerator is inferred without qualification, \eg @.I(3)@.) An enumeration is created when \emph{all} the enumerators are unit-type. \begin{swift} enum Week { case Mon, Tue, Wed, Thu, Fri, Sat, Sun // unit-type }; var week : Week = Week.Mon; \end{swift} As well, it is possible to type \emph{all} the enumerators with a common type, and set different values for each enumerator; for integral types, there is auto-incrementing. \begin{cquote} \setlength{\tabcolsep}{15pt} \begin{tabular}{@{}lll@{}} \begin{swift} enum WeekInt: @Int@ { case Mon, Tue, Wed, Thu = 10, Fri, Sat = 4, Sun // auto-incrementing }; \end{swift} & \begin{swift} enum WeekStr: @String@ { case Mon = "MON", Tue, Wed, Thu, Fri, Sat = "SAT", Sun }; \end{swift} \end{tabular} \end{cquote} An enumeration only supports equality comparison between enumerator values, unless it inherits from @Comparable@, adding relational operators @<@, @<=@, @>@, and @>=@. An enumeration can have methods. \begin{swift} enum Week: Comparable { case Mon, Tue, Wed, Thu, Fri, Sat, Sun // unit-type func @isWeekday() -> Bool@ { return self <= .Fri } // method func @isWeekend() -> Bool@ { return .Sat <= self } // method }; \end{swift} An enumeration can be used in the @if@ and @switch@ statements, where @switch@ must be exhaustive or have a @default@. \begin{cquote} \setlength{\tabcolsep}{15pt} \begin{tabular}{@{}ll@{}} \begin{swift} if @week <= .Fri@ { print( "weekday" ); } \end{swift} & \begin{swift} switch @week@ { case .Mon: print( "Mon" ) ... case .Sun: print( "Sun" ) } \end{swift} \end{tabular} \end{cquote} Enumerating is accomplished by inheriting from @CaseIterable@ without any associated values. \begin{swift} enum Week: Comparable, @CaseIterable@ { case Mon, Tue, Wed, Thu, Fri, Sat, Sun // unit-type }; var weeki : Week = Week.Mon; if weeki <= .Fri { print( "weekday" ); } for day in Week@.allCases@ { print( day, terminator:" " ) } weekday Mon Tue Wed Thu Fri Sat Sun \end{swift} The @enum.allCases@ property returns a collection of all the cases for looping over an enumeration type or variable (expensive operation). A typed enumeration is accomplished by inheriting from any Swift type, and accessing the underlying enumerator value is done with attribute @rawValue@. Type @Int@ has auto-incrementing from previous enumerator; type @String@ has auto-incrementing of the enumerator label. \begin{cquote} \setlength{\tabcolsep}{15pt} \begin{tabular}{@{}lll@{}} \begin{swift} enum WeekInt: @Int@, CaseIterable { case Mon, Tue, Wed, Thu = 10, Fri, Sat = 4, Sun // auto-incrementing }; for day in WeekInt.allCases { print( day@.rawValue@, terminator:" " ) } 0 1 2 10 11 4 5 \end{swift} & \begin{swift} enum WeekStr: @String@, CaseIterable { case Mon = "MON", Tue, Wed, Thu, Fri, Sat = "SAT", Sun }; for day in WeekStr.allCases { print( day@.rawValue@, terminator:" " ) } MON Tue Wed Thu Fri SAT Sun \end{swift} \end{tabular} \end{cquote} There is a bidirectional conversion from typed enumerator to @rawValue@ and vise versa. \begin{swift} var weekInt : WeekInt = WeekInt.Mon; if let opt = WeekInt( rawValue: 0 ) { // test optional return value print( weekInt.rawValue, opt ) // 0 Mon } else { print( "invalid weekday lookup" ) } \end{swift} Conversion from @rawValue@ to enumerator may fail (bad lookup), so the result is an optional value. \section{Python 3.13} % https://docs.python.org/3/howto/enum.html Python is a dynamically-typed reflexive programming language with multiple incompatible versions. The generality of the language makes it is possible to extend existing or build new language features. As a result, discussing Python enumerations is a moving target, because if a features does not exist, it can often be created with varying levels of complexity within the language. Therefore, the following discussion is (mostly) restricted to the core enumeration features in Python 3.13. A Python enumeration is not a basic type; it is a @class@ inheriting from the @Enum@ class. The @Enum@ class presents a set of scoped enumerators, where each enumerator is a pair object with a \emph{constant} string name and arbitrary value. Hence, an enumeration instance is a fixed type (enumeration pair), and its value is the type of one of the enumerator pairs. The enumerator value fields must be explicitly initialized and be \emph{unique}. \begin{python} class Week(!Enum!): Mon = 1; Tue = 2; Wed = 3; Thu = 4; Fri = 5; Sat = 6; Sun = 7 \end{python} and/or explicitly auto initialized, \eg: \begin{python} class Week(Enum): Mon = 1; Tue = 2; Wed = 3; Thu = 10; Fri = !auto()!; Sat = 4; Sun = !auto()! Mon : 1 Tue : 2 Wed : 3 Thu : 10 Fri : !11! Sat : 4 Sun : !12! \end{python} where @auto@ increments by 1 from the previous @auto@ value \see{Golang \lstinline[language=Go]{iota}, \VRef{s:Golang}}. @auto@ is controlled by member @_generate_next_value_()@, which can be overridden: \begin{python} @staticmethod def _generate_next_value_( name, start, count, last_values ): return name \end{python} There is no direct concept of restricting the enumerators in an enumeration \emph{instance} because the dynamic typing changes the type. \begin{python} class RGB(Enum): Red = 1; Green = 2; Blue = 3 day : Week = Week.Tue; $\C{\# type is Week}$ !day = RGB.Red! $\C{\# type is RGB}$ !day : Week = RGB.Red! $\C{\# type is RGB}$ \end{python} The enumerators are constants and cannot be reassigned. Hence, while enumerators can be different types, \begin{python} class Diff(Enum): Int = 1; Float = 3.5; Str = "ABC" \end{python} it is not an ADT because the enumerator names are not constructors. An enumerator initialized with the same value is an alias and invisible at the enumeration level, \ie the alias is substituted for its aliasee. \begin{python} class WeekD(Enum): Mon = 1; Tue = 2; Wed = 3; Thu = !10!; Fri = !10!; Sat = !10!; Sun = !10! \end{python} Here, the enumeration has only 4 enumerators and 3 aliases. An alias is only visible by dropping down to the @class@ level and asking for class members. Aliasing is prevented using the @unique@ decorator. \begin{python} !@unique! class DupVal(Enum): One = 1; Two = 2; Three = !3!; Four = !3! ValueError: duplicate values found in : Four -> Three \end{python} \begin{lrbox}{\myboxA} \begin{python} def by_position(enum_type, position): for index, value in enumerate(enum_type): if position == index: return value raise Exception("by_position out of range") \end{python} \end{lrbox} There are bidirectional enumeration pseudo-functions for label and value, but there is no concept of access using ordering (position).\footnote{ There is an $O(N)$ mechanism to access an enumerator's value by position. \newline \usebox\myboxA} \begin{cquote} \setlength{\tabcolsep}{15pt} \begin{tabular}{@{}ll@{}} \begin{python} Week.Thu.value == 4; Week.Thu.name == "Thu"; \end{python} & \begin{python} Week( 4 ) == Week.Thu Week["Thu"].value == 4 \end{python} \end{tabular} \end{cquote} @Enum@ only supports equality comparison between enumerator values. There are multiple library extensions to @Enum@, \eg @OrderedEnum@ recipe class, adding relational operators @<@, @<=@, @>@, and @>=@. An enumeration \lstinline[language=python]{class} can have methods. \begin{python} class Week(!OrderedEnum!): Mon = 1; Tue = 2; Wed = 3; Thu = 4; Fri = 5; Sat = 6; Sun = 7 def !isWeekday(self)!: # method return Week(self.value) !<=! Week.Fri def !isWeekend(self)!: # method return Week.Sat !<=! Week(self.value) \end{python} An enumeration can be used in the @if@ and @switch@ statements but only for equality tests, unless extended to @OrderedEnum@. \begin{cquote} \setlength{\tabcolsep}{12pt} \begin{tabular}{@{}ll@{}} \begin{python} if day <= Week.Fri : print( "weekday" ); \end{python} & \begin{python} match day: case Week.Mon | Week.Tue | Week.Wed | Week.Thu | Week.Fri: print( "weekday" ); case Week.Sat | Week.Sun: print( "weekend" ); \end{python} \end{tabular} \end{cquote} Looping is performed using the enumeration type or @islice@ from @itertools@ based on position. \begin{python} for day in !Week!: $\C[2.25in]{\# Mon : 1 Tue : 2 Wed : 3 Thu : 4 Fri : 5 Sat : 6 Sun : 7}$ print( day.name, ":", day.value, end=" " ) for day in !islice(Week, 0, 5)!: $\C{\# Mon : 1 Tue : 2 Wed : 3 Thu : 4 Fri : 5}$ print( day.name, ":", day.value, end=" " ) for day in !islice(Week, 5, 7)!: $\C{\# Sat : 6 Sun : 7}$ print( day.name, ":", day.value, end=" " ) for day in !islice(Week,0, 7, 2)!: $\C{\# Mon : 1 Wed : 3 Fri : 5 Sun : 7}\CRT$ print( day.name, ":", day.value, end=" " ) \end{python} Iterating that includes alias names only (strings) is done using attribute @__members__@. \begin{python} for day in WeekD.__members__: print( day, ":", end=" " ) Mon : Tue : Wed : Thu : Fri : Sat : Sun \end{python} Enumeration subclassing is allowed only if the enumeration base-class does not define any members. \begin{python} class WeekE(OrderedEnum): !pass!; # no members class WeekDay(WeekE): Mon = 1; Tue = 2; Wed = 3; Thu = 4; Fri = 5; class WeekEnd(WeekE): Sat = 6; Sun = 7 \end{python} Here, type @WeekE@ is an abstract type because the dynamic typing never uses it. \begin{cquote} \setlength{\tabcolsep}{25pt} \begin{tabular}{@{}ll@{}} \begin{python} print( type(WeekE) ) day : WeekE = WeekDay.Fri # set type print( type(day), day ) day = WeekEnd.Sat # set type print( type(day), day ) \end{python} & \begin{python} <$class$ 'enum.EnumType'> WeekDay.Fri WeekEnd.Sat \end{python} \end{tabular} \end{cquote} There are a number of supplied enumeration base-types: @IntEnum@, @StrEnum@, @IntFalg@, @Flag@, which restrict the values in an enum using multi-inheritance. @IntEnum@ is a subclass of @int@ and @Enum@, allowing enumerator comparison to @int@ and other enumerators of this type (like C enumerators). @StrEnum@ is the same as @IntEnum@ but a subclass of the string type \lstinline[language=python]{str}. @IntFlag@, is a restricted subclass of @int@ where the enumerators can be combined using the bitwise operators (@&@, @|@, @^@, @~@) and the result is an @IntFlag@ member. @Flag@ is the same as @IntFlag@ but cannot be combined with, nor compared against, any other @Flag@ enumeration, nor @int@. Auto increment for @IntFlag@ and @Flag@ is by powers of 2. Enumerators that are a combinations of single bit enumerators are aliases, and hence, invisible. The following is an example for @Flag@. \begin{python} class WeekF(Flag): Mon = 1; Tue = 2; Wed = 4; Thu = !auto()!; Fri = 16; Sat = 32; Sun = 64; \ Weekday = Mon | Tue | Wed | Thu | Fri; \ Weekend = Sat | Sun print( f"0x{repr(WeekF.Weekday.value)} 0x{repr(WeekF.Weekend.value)}" ) 0x31 0x96 \end{python} It is possible to enumerate through a @Flag@ enumerator (no aliases): \begin{python} for day in WeekF: print( f"{day.name}: {day.value}", end=" ") Mon: 1 Tue: 2 Wed: 4 Thu: 8 Fri: 16 Sat: 32 Sun: 64 \end{python} and a combined alias enumerator for @Flag@. \begin{cquote} \setlength{\tabcolsep}{15pt} \begin{tabular}{@{}ll@{}} \begin{python} weekday = WeekF.Weekday for day in weekday: print( f"{day.name}:" f" {day.value}", end=" " ) Mon: 1 Tue: 2 Wed: 4 Thu: 8 Fri: 16 \end{python} & \begin{python} weekend = WeekF.Weekend for day in weekend: print( f"{day.name}:" f" {day.value}", end=" " ) Sat: 32 Sun: 64 \end{python} \end{tabular} \end{cquote} \section{OCaml} % https://ocaml.org/docs/basic-data-types#enumerated-data-types % https://dev.realworldocaml.org/runtime-memory-layout.html OCaml provides a variant (union) type, where multiple heterogeneously-typed objects share the same storage. The simplest form of the variant type is a list of nullary datatype constructors, which is like an unscoped, opaque enumeration. OCaml provides a variant (union) type, which is an aggregation of heterogeneous types. A basic variant is a list of nullary datatype constructors, which is like an unscoped, opaque enumeration. \begin{ocaml} type week = Mon | Tue | Wed | Thu | Fri | Sat | Sun let day : week @= Mon@ $\C{(* bind *)}$ let take_class( d : week ) = @match@ d with $\C{(* matching *)}$ Mon | Wed -> Printf.printf "CS442\n" | Tue | Thu -> Printf.printf "CS343\n" | Fri -> Printf.printf "Tutorial\n" | _ -> Printf.printf "Take a break\n" let _ = take_class( Mon ); @CS442@ \end{ocaml} The only operations are binding and pattern matching (equality), where the variant name is logically the implementation tag stored in the union for discriminating the value in the object storage. After compilation, variant names are mapped to an opague ascending intergral type discriminants, starting from 0. Here, function @take_class@ has a @week@ parameter, and returns @"CS442"@, if the week value is @Mon@ or @Wed@, @"CS343"@, if the value is @Tue@ or @Thu@, and @"Tutorial"@ for @Fri@. The ``@_@'' is a wildcard matching any @week@ value, so the function returns @"Take a break"@ for values @Sat@ or @Sun@, which are not matched by the previous cases. Since the variant has no type, it has a \newterm{0-arity constructor}, \ie no parameters. Because @week@ is a union of values @Mon@ to @Sun@, it is a \newterm{union type} in turns of the functional-programming paradigm. Each variant can have an associated heterogeneous type, with an n-ary constructor for creating a corresponding value. \begin{ocaml} type colour = Red | Green of @string@ | Blue of @int * float@ \end{ocaml} A variant with parameter is stored in a memory block, prefixed by an int tag and has its parameters stores as words in the block. @colour@ is a summation of a nullary type, a unary product type of @string@, and a cross product of @int@ and @float@. (Mathematically, a @Blue@ value is a Cartesian product of the types @int@ type and @float@.) Hence, a variant type creates a sum of product of different types. \begin{ocaml} let c = Red $\C{(* 0-ary constructor, set tag *)}$ let _ = match c with Red -> Printf.printf "Red, " let c = Green( "abc" ) $\C{(* 1-ary constructor, set tag *)}$ let _ = match c with Green g -> Printf.printf "%s, " g let c = Blue( 1, 1.5 ) $\C{(* 2-ary constructor, set tag *)}$ let _ = match c with Blue( i, f ) -> Printf.printf "%d %g\n" i f @Red, abc, 1 1.5@ \end{ocaml} A variant type can have a recursive definition. \begin{ocaml} type @stringList@ = Empty | Pair of string * @stringList@ \end{ocaml} which is a recursive sum of product of types, called an \newterm{algebraic data-type}. A recursive function is often used to pattern match against a recursive variant type. \begin{ocaml} let rec @len_of_string_list@( list : stringList ): int = match list with Empty -> 0 | Pair( _ , r ) -> 1 + @len_of_string_list@ r \end{ocaml} Here, the head of the recursive type is removed and the remainder is processed until the type is empty. Each recursion is counted to obtain the number of elements in the type. Note, the compiler statically guarantees that only the correct kind of type is used in the \lstinline[language=OCaml]{match} statement. However, the union tag is dynamically set on binding (and possible reset on assignment), so a \lstinline[language=OCaml]{match} statement is effectively doing RTTI to select the matching case clause. In summary, an OCaml variant is a singleton value rather than a set of possibly ordered values, and hence, has no notion of enumerabilty. Therefore it is not an enumeration, except for the simple opaque (nullary) case. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \begin{comment} Date: Wed, 13 Mar 2024 10:52:34 -0400 Subject: Re: OCaml To: "Peter A. Buhr" From: Gregor Richards On 3/12/24 18:34, Peter A. Buhr wrote: > Gregor, attached is a section Jiada wrote on OCaml (1-page). > Does it reflect our discussion about functional languages and enumerations? Yeah, I think so. The most important part, i.e., that once they're parameterized they're not really enumerations at all, is covered clearly enough. A couple quibbles: <> This is true, but leaking implementation details. These are nullary datatype constructors. Indeed, you later talk about "tagged variants", which are really just parameterized variants, using the term "tag" differently, confusing the term "tag" further. <> It is a *union* of values and is a *union* type. With valediction, - Gregor Richards Date: Thu, 14 Mar 2024 21:45:52 -0400 Subject: Re: OCaml "enums" do come with ordering To: "Peter A. Buhr" From: Gregor Richards On 3/14/24 21:30, Peter A. Buhr wrote: > I've marked 3 places with your name to shows places with enum ordering. > > type week = Mon | Tue | Wed | Thu | Fri | Sat | Sun > let day : week = Mon > let take_class( d : week ) = > if d <= Fri then (* Gregor *) > Printf.printf "week\n" > else if d >= Sat then (* Gregor *) > Printf.printf "weekend\n"; > match d with > Mon | Wed -> Printf.printf "CS442\n" | > Tue | Thu -> Printf.printf "CS343\n" | > Fri -> Printf.printf "Tutorial\n" | > _ -> Printf.printf "Take a break\n" > > let _ = take_class( Mon ); take_class( Sat ); > > type colour = Red | Green of string | Blue of int * float > let c = Red > let _ = match c with Red -> Printf.printf "Red, " > let c = Green( "abc" ) > let _ = match c with Green g -> Printf.printf "%s, " g > let c = Blue( 1, 1.5 ) > let _ = match c with Blue( i, f ) -> Printf.printf "%d %g\n" i f > > let check_colour(c: colour): string = > if c < Green( "xyz" ) then (* Gregor *) > Printf.printf "green\n"; > match c with > Red -> "Red" | > Green g -> g | > Blue(i, f) -> string_of_int i ^ string_of_float f > let _ = check_colour( Red ); check_colour( Green( "xyz" ) ); > > type stringList = Empty | Pair of string * stringList > let rec len_of_string_list(l: stringList): int = > match l with > Empty -> 0 | > Pair(_ , r) -> 1 + len_of_string_list r > > let _ = for i = 1 to 10 do > Printf.printf "%d, " i > done > > (* Local Variables: *) > (* tab-width: 4 *) > (* compile-command: "ocaml test.ml" *) > (* End: *) My functional-language familiarity is far more with Haskell than OCaml. I mostly view OCaml through a lens of "it's Haskell but with cheating". Haskell "enums" (ADTs) aren't ordered unless you specifically and manually put them in the Ord typeclass by defining the comparators. Apparently, OCaml has some other rule, which I would guess is something like "sort by tag then by order of parameter". Having a default behavior for comparators is *bizarre*; my guess would be that it gained this behavior in its flirtation with object orientation, but that's just a guess (and irrelevant). This gives a total order, but not enumerability (which would still be effectively impossible or even meaningless since enums are just a special case of ADTs). With valediction, - Gregor Richards Date: Wed, 20 Mar 2024 18:16:44 -0400 Subject: Re: To: "Peter A. Buhr" From: Gregor Richards On 3/20/24 17:26, Peter A. Buhr wrote: > Gregor, everyone at this end would like a definition of "enumerability". Can > you formulate one? According to the OED (emphasis added to the meaning I'm after): enumerate (verb, transitive). To count, ascertain the number of; **more usually, to mention (a number of things or persons) separately, as if for the purpose of counting**; to specify as in a list or catalogue. With C enums, if you know the lowest and highest value, you can simply loop over them in a for loop (this is, of course, why so many enums come with an ENUM_WHATEVER_LAST value). But, I would be hesitant to use the word "loop" to describe enumerability, since in functional languages, you would recurse for such a purpose. In Haskell, in order to do something with every member of an "enumeration", you would have to explicitly list them all. The type system will help a bit since it knows if you haven't listed them all, but you would have to statically have every element in the enumeration. If somebody added new elements to the enumeration later, your code to enumerate over them would no longer work correctly, because you can't simply say "for each member of this enumeration do X". In Haskell that's because there aren't actually enumerations; what they use as enumerations are a degenerate form of algebraic datatypes, and ADTs are certainly not enumerable. In OCaml, you've demonstrated that they impose comparability, but I would still assume that you can't make a loop over every member of an enumeration. (But, who knows!) Since that's literally what "enumerate" means, it seems like a rather important property for enumerations to have ;) With valediction, - Gregor Richards From: Andrew James Beach To: Gregor Richards , Peter Buhr CC: Michael Leslie Brooks , Fangren Yu , Jiada Liang Subject: Re: Re: Date: Thu, 21 Mar 2024 14:26:36 +0000 Does this mean that not all enum declarations in C create enumerations? If you declare an enumeration like: enum Example { Label, Name = 10, Tag = 3, }; I don't think there is any way to enumerate (iterate, loop, recurse) over these values without listing all of them. Date: Thu, 21 Mar 2024 10:31:49 -0400 Subject: Re: To: Andrew James Beach , Peter Buhr CC: Michael Leslie Brooks , Fangren Yu , Jiada Liang From: Gregor Richards I consider this conclusion reasonable. C enums can be nothing more than const ints, and if used in that way, I personally wouldn't consider them as enumerations in any meaningful sense, particularly since the type checker essentially does nothing for you there. Then they're a way of writing consts repeatedly with some textual indicator that these definitions are related; more namespace, less enum. When somebody writes bitfield members as an enum, is that *really* an enumeration, or just a use of the syntax for enums to keep related definitions together? With valediction, - Gregor Richards Date: Tue, 16 Apr 2024 11:04:51 -0400 Subject: Re: C unnamed enumeration To: "Peter A. Buhr" CC: , , , From: Gregor Richards On 4/16/24 09:55, Peter A. Buhr wrote: > So what is a variant? Is it a set of tag names, which might be a union or is it > a union, which might have tag names? Your tagless variant bears no resemblance to variants in any functional programming language. A variant is a tag AND a union. You might not need to put anything in the union, in which case it's a pointless union, but the named tag is absolutely mandatory. That's the thing that varies. I was unaware of std::variant. As far as functional languages are concerned, std::variant IS NOT A VARIANT. Perhaps it would be best to use the term ADT for the functional language concept, because that term has no other meanings. An ADT cannot not have a named tag. That's meaningless. The tag is the data constructor, which is the thing you actually define when you define an ADT. It is strictly the union that's optional. With valediction, - Gregor Richards \end{comment} \section{Comparison} \VRef[Table]{t:FeatureLanguageComparison} shows a comparison of enumeration features and programming languages. The features are high level and may not capture nuances within a particular language The @const@ feature is simple macros substitution and not a typed enumeration. \begin{table} \caption{Enumeration Feature / Language Comparison} \label{t:FeatureLanguageComparison} \small \setlength{\tabcolsep}{3pt} \newcommand{\CM}{\checkmark} \begin{tabular}{r|c|c|c|c|c|c|c|c|c|c|c|c|c} &Pascal & Ada &\Csharp& OCaml & Java &Modula-3&Golang& Rust & Swift & Python& C & \CC & \CFA \\ \hline @const@ & \CM & & & & & & \CM & & & & & \CM & \\ \hline \hline opaque & & & & & & & & & & & & & \CM \\ \hline typed & & & & & & & & & & & @int@ & integral & @T@ \\ \hline safe & & & & & & & & & & & & \CM & \CM \\ \hline ordered & & & & & & & & & & & \CM & \CM & \CM \\ \hline dup. values & & & & & & & & & & alias & \CM & \CM & \CM \\ \hline setable & & & & & & & & & & & \CM & \CM & \CM \\ \hline auto-init & & & & & & & & & & & \CM & \CM & \CM \\ \hline (Un)Scoped & & & & & & & & & & & U & U/S & U/S \\ \hline overload & & \CM & & & & & & & & & & \CM & \CM \\ \hline switch & & & & & & & & & & & \CM & \CM & \CM \\ \hline loop & & & & & & & & & & & & & \CM \\ \hline array/subscript & & & & & & & & & & & \CM & & \CM \\ \hline subtype & & & & & & & & & & & & & \CM \\ \hline inheritance & & & & & & & & & & & & & \CM \\ \end{tabular} \end{table}