source: doc/theses/jiada_liang_MMath/relatedwork.tex @ 41c4b5e

Last change on this file since 41c4b5e was 41c4b5e, checked in by Peter A. Buhr <pabuhr@…>, 8 weeks ago

updates related to introduction chapter changes

  • Property mode set to 100644
File size: 97.5 KB
Line 
1\chapter{Related Work}
2\label{s:RelatedWork}
3
4\begin{comment}
5An algebraic data type (ADT) can be viewed as a recursive sum of product types.
6A sum type lists values as members.
7A member in a sum type definition is known as a data constructor.
8For example, C supports sum types union and enumeration (enum).
9An enumeration in C can be viewed as the creation of a list of zero-arity data constructors.
10A union instance holds a value of one of its member types.
11Defining a union does not generate new constructors.
12The definition of member types and their constructors are from the outer lexical scope.
13
14In general, an \newterm{algebraic data type} (ADT) is a composite type, \ie, a type formed by combining other types.
15Three 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).
16Enumerated 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.
17Values of algebraic types are access by subscripting, field qualification, or type (pattern) matching.
18\end{comment}
19
20Enumeration-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}.
21Among theses languages, there are a large set of overlapping features, but each language has its own unique extensions and restrictions.
22
23\section{Pascal}
24\label{s:Pascal}
25
26Classic Pascal introduced the \lstinline[language=Pascal]{const} aliasing declaration binding a name to a constant literal/expression.
27\begin{pascal}
28const one = 0 + 1;   Vowels = set of (A,E,I,O,U);   NULL = NIL;
29                 PI = 3.14159;   Plus = '+';   Fred = 'Fred';
30\end{pascal}
31As stated, this mechanism is not an enumeration because there is no specific type (pseudo enumeration).
32Hence, there is no notion of a (possibly ordered) set, modulo the \lstinline[language=pascal]{set of} type.
33The type of each constant name (enumerator) is inferred from the constant-expression type.
34
35Free Pascal~\cite[\S~3.1.1]{FreePascal} is a modern, object-oriented version of classic Pascal, with a C-style enumeration type.
36Enumerators must be assigned in ascending numerical order with a constant expression and the range can be non-consecutive.
37\begin{pascal}
38Type EnumType = ( one, two, three, forty @= 40@, fortyone );
39\end{pascal}
40Pseudo-functions @Pred@ and @Succ@ can only be used if the range is consecutive.
41The 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.
42The integral size can be explicitly specified using compiler directive @$PACKENUM@~$N$, where $N$ is the number of bytes, \eg:
43\begin{pascal}
44Type @{$\color{red}\$$PACKENUM 1}@ SmallEnum = ( one, two, three );
45            @{$\color{red}\$$PACKENUM 4}@ LargeEnum = ( BigOne, BigTwo, BigThree );
46Var S : SmallEnum; { 1 byte }
47          L : LargeEnum; { 4 bytes}
48\end{pascal}
49
50
51\section{Ada}
52
53An Ada enumeration type is a set of ordered unscoped identifiers (enumerators) bound to \emph{unique} \newterm{literals}.\footnote{%
54Ada 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).}
55\begin{ada}
56type Week is ( Mon, Tue, Wed, Thu, Fri, Sat, Sun ); -- literals (enumerators)
57\end{ada}
58Object initialization and assignment are restricted to the enumerators of this type.
59While Ada enumerators are unscoped, like C, Ada enumerators are overloadable.
60\begin{ada}
61type RGB is ( @Red@, @Green@, Blue );
62type Traffic_Light is ( @Red@, Yellow, @Green@ );
63\end{ada}
64Like \CFA, Ada uses a type-resolution algorithm including the left-hand side of assignmente to disambiguate among overloaded identifiers.
65\VRef[Figure]{f:AdaEnumeration} shows how ambiguity is handled using a cast, \ie \lstinline[language=ada]{RGB'(Red)}.
66
67\begin{figure}
68\begin{ada}
69with Ada.Text_IO; use Ada.Text_IO;
70procedure test is
71        type RGB is ( @Red@, Green, Blue );
72        type Traffic_Light is ( @Red@, Yellow, Green );         -- overload
73        procedure @Red@( Colour : RGB ) is begin            -- overload
74                Put_Line( "Colour is " & RGB'Image( Colour ) );
75        end Red;
76        procedure @Red@( TL : Traffic_Light ) is begin       -- overload
77                Put_Line( "Light is " & Traffic_Light'Image( TL ) );
78        end Red;
79begin
80        @Red@( Blue );                           -- RGB
81        @Red@( Yellow );                                -- Traffic_Light
82        @Red@( @RGB'(Red)@ );           -- ambiguous without cast
83end test;
84\end{ada}
85\caption{Ada Enumeration Overload Resolution}
86\label{f:AdaEnumeration}
87\end{figure}
88
89Enumerators without initialization are auto-initialized from left to right, starting at zero, incrementing by 1.
90Enumerators with initialization must set \emph{all} enumerators in \emph{ascending} order, \ie there is no auto-initialization.
91\begin{ada}
92type Week is ( Mon, Tue, Wed, Thu, Fri, Sat, Sun );
93for Week use ( Mon => 0, Tue => 1, Wed => 2, Thu => @10@, Fri => 11, Sat => 14, Sun => 15 );
94\end{ada}
95The enumeration operators are the equality and relational operators, @=@, @/=@, @<@, @<=@, @=@, @/=@, @>=@, @>@, where the ordering relationship is given implicitly by the sequence of acsending enumerators.
96
97Ada provides an alias mechanism, \lstinline[language=ada]{renames}, for aliasing types, which is useful to shorten package identifiers.
98\begin{ada}
99@OtherRed@ : RGB renames Red;
100\end{ada}
101which suggests a possible \CFA extension to @typedef@.
102\begin{cfa}
103typedef RGB.Red OtherRed;
104\end{cfa}
105
106There are three pairs of inverse enumeration pseudo-functions (attributes): @'Pos@ and @'Val@, @'Enum_Rep@ and @'Enum_Val@, and @'Image@ and @'Value@,
107\begin{cquote}
108\setlength{\tabcolsep}{15pt}
109\begin{tabular}{@{}ll@{}}
110\begin{ada}
111RGB'Pos( Red ) = 0;
112RGB'Enum_Rep( Red ) = 10;
113RGB'Image( Red ) = "RED";
114\end{ada}
115&
116\begin{ada}
117RGB'Val( 0 ) = Red
118RGB'Enum_Val( 10 ) =  Red
119RGB'Value( "Red" ) =  Red
120\end{ada}
121\end{tabular}
122\end{cquote}
123These attributes are important for IO.
124An 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.
125
126Ada allows the enumerator label to be a character constant.
127\begin{ada}
128type Operator is ( '+', '-', '*', '/' );
129\end{ada}
130which is syntactic sugar for the label and not character literals from the predefined type @Character@.
131The purpose is strictly readability using character literals rather than identifiers.
132\begin{ada}
133Op : Operator := '+';
134if Op = '+' or else Op = '-' then ... ;
135elsif Op = '*' or else Op = '/' then ... ; end if;
136\end{ada}
137Interestingly, arrays of character enumerators can be treated as strings.
138\begin{ada}
139Ops : array( 0..3 ) of Operator;
140Ops := @"+-*/"@;            -- string assignment to array elements
141Ops := "+-" @&@ "*/";   -- string concatenation and assignment
142\end{ada}
143Ada's @Character@ type is defined as a character enumeration across all Latin-1 characters.
144
145Ada's boolean type is also a special enumeration, which can be used in conditions.
146\begin{ada}
147type Boolean is (False, True); -- False / True not keywords
148@Flag@ : Boolean;
149if @Flag@ then ...    -- conditional
150\end{ada}
151Since only types derived from @Boolean@ can be a conditional, @Boolean@ is essentially  a builtin type.
152
153Ada provides \emph{consecutive} subtyping of an enumeration using \lstinline[language=ada]{range}.
154\begin{ada}
155type Week is ( Mon, Tue, Wed, Thu, Fri, Sat, Sun );
156subtype Weekday is Week @range Mon .. Fri@;
157subtype Weekend is Week @range Sat .. Sun@;
158Day : Week;
159\end{ada}
160Hence, the ordering of the enumerators is crucial to provide the necessary ranges.
161
162An enumeration type can be used in the Ada \lstinline[language=ada]{case} (all enumerators must appear or a @default@) or iterating constructs.
163\begin{cquote}
164\setlength{\tabcolsep}{15pt}
165\begin{tabular}{@{}ll@{}}
166\begin{ada}
167case Day is
168        when @Mon .. Fri@ => ... ;
169        when @Sat .. Sun@ => ... ;
170end case;
171\end{ada}
172&
173\begin{ada}
174case Day is
175        when @Weekday@ => ... ;  -- subtype ranges
176        when @Weekend@ => ... ;
177end case;
178\end{ada}
179\end{tabular}
180\end{cquote}
181
182\begin{cquote}
183\setlength{\tabcolsep}{12pt}
184\begin{tabular}{@{}lll@{}}
185\begin{ada}
186for Day in @Mon .. Sun@ loop
187        ...
188end loop;
189\end{ada}
190&
191\begin{ada}
192for Day in @Weekday@ loop
193        ...
194end loop;
195\end{ada}
196&
197\begin{ada}
198for Day in @Weekend@ loop
199        ...
200end loop;
201\end{ada}
202\end{tabular}
203\end{cquote}
204
205An enumeration type can be used as an array dimension and subscript.
206\begin{ada}
207Lunch : array( @Week@ ) of Time;
208for Day in Week loop
209        Lunch( @Day@ ) := ... ;       -- set lunch time
210end loop;
211\end{ada}
212
213
214\section{\CC}
215\label{s:C++RelatedWork}
216
217\CC enumeration is largely backwards compatible with C, so it inherited C's enumerations with some modifications and additions.
218
219\CC has aliasing using @const@ declarations, like C \see{\VRef{s:Cconst}}, with type inferencing, plus static/dynamic initialization.
220(Note, a \CC @constexpr@ declaration is the same as @const@ with the restriction that the initialization is a compile-time expression.)
221\begin{c++}
222const @auto@ one = 0 + 1;                               $\C{// static initialization}$
223const @auto@ NIL = nullptr;
224const @auto@ PI = 3.14159;
225const @auto@ Plus = '+';
226const @auto@ Fred = "Fred";
227const @auto@ Mon = 0, Tue = Mon + 1, Wed = Tue + 1, Thu = Wed + 1, Fri = Thu + 1,
228                                Sat = Fri + 1, Sun = Sat + 1;
229void foo() {
230        const @auto@ r = random();                      $\C{// dynamic initialization}$
231        int va[r];                                                      $\C{// VLA, auto scope only}$
232}
233\end{c++}
234Statically initialized identifiers may appear in any constant-expression context, \eg @case@.
235Dynamically initialized identifiers may appear as array dimensions in @g++@, which allows variable-sized arrays.
236Interestingly, global \CC @const@ declarations are implicitly marked @static@ (@r@, read-only local, rather than @R@, read-only external)
237\begin{c++}
238$\$$ nm test.o
2390000000000000018 @r@ Mon
240\end{c++}
241whereas C @const@ declarations without @static@ are marked @R@.
242
243The following \CC non-backwards compatible changes are made \see{\cite[\S~7.2]{ANSI98:c++}}.
244\begin{cquote}
245Change: \CC objects of enumeration type can only be assigned values of the same enumeration type.
246In C, objects of enumeration type can be assigned values of any integral type. \\
247Example:
248\begin{c++}
249enum color { red, blue, green };
250color c = 1;                                                    $\C{// valid C, invalid c++}$
251\end{c++}
252\textbf{Rationale}: The type-safe nature of \CC. \\
253\textbf{Effect on original feature}: Deletion of semantically well-defined feature. \\
254\textbf{Difficulty of converting}: Syntactic transformation. (The type error produced by the assignment can be automatically corrected by applying an explicit cast.) \\
255\textbf{How widely used}: Common.
256\end{cquote}
257
258\begin{cquote}
259Change: In \CC, the type of an enumerator is its enumeration.
260In C, the type of an enumerator is @int@. \\
261Example:
262\begin{c++}
263enum e { A };
264sizeof(A) == sizeof(int)                                $\C{// in C}$
265sizeof(A) == sizeof(e)                                  $\C{// in c++}$
266/* and sizeof(int) is not necessary equal to sizeof(e) */
267\end{c++}
268\textbf{Rationale}: In \CC, an enumeration is a distinct type. \\
269\textbf{Effect on original feature}: Change to semantics of well-defined feature. \\
270\textbf{Difficulty of converting}: Semantic transformation. \\
271\textbf{How widely used}: Seldom. The only time this affects existing C code is when the size of an enumerator is taken.
272Taking the size of an enumerator is not a common C coding practice.
273\end{cquote}
274Hence, the values in a \CC enumeration can only be its enumerators (without a cast).
275While the storage size of an enumerator is up to the compiler, there is still an implicit cast to @int@.
276\begin{c++}
277enum E { A, B, C };
278E e = A;
279int i = A;    i = e;                                    $\C{// implicit casts to int}$
280\end{c++}
281\CC{11} added a scoped enumeration, \lstinline[language=c++]{enum class} (or \lstinline[language=c++]{enum struct})\footnote{
282The use of keyword \lstinline[language=c++]{class} is resonable because default visibility is \lstinline[language=c++]{private} (scoped).
283However, default visibility for \lstinline[language=c++]{struct} is \lstinline[language=c++]{public} (unscoped) making it an odd choice.},
284where the enumerators are accessed using type qualification.
285\begin{c++}
286enum class E { A, B, C };
287E e = @E::@A;                                                   $\C{// qualified enumerator}$
288e = B;                                                                  $\C{// error: B not in scope}$
289\end{c++}
290\CC{20} supports explicit unscoping with a \lstinline[language=c++]{using enum} declaration.
291\begin{c++}
292enum class E { A, B, C };
293@using enum E;@
294E e = A;    e = B;                                              $\C{// direct access}$
295\end{c++}
296\CC{11} added the ability to explicitly declare only an underlying \emph{integral} type for \lstinline[language=c++]{enum class}.
297\begin{c++}
298enum class RGB @: long@ { Red, Green, Blue };
299enum class rgb @: char@ { Red = 'r', Green = 'g', Blue = 'b' };
300enum class srgb @: signed char@ { Red = -1, Green = 0, Blue = 1 };
301\end{c++}
302There is no implicit conversion from the \lstinline[language=c++]{enum class} type to its declared type.
303\begin{c++}
304rgb crgb = rgb::Red;
305char ch = rgb::Red;   ch = crgb;                $\C{// error}$
306\end{c++}
307An enumeration can be used in the @if@ and @switch@ statements.
308\begin{cquote}
309\setlength{\tabcolsep}{15pt}
310\begin{tabular}{@{}ll@{}}
311\begin{c++}
312if ( @day@ <= Fri )
313        cout << "weekday" << endl;
314
315
316
317
318\end{c++}
319&
320\begin{c++}
321switch ( @day@ ) {
322  case Mon: case Tue: case Wed: case Thu: case Fri:
323        cout << "weekday" << endl; break;
324  case Sat: case Sun:
325        cout << "weekend" << endl; break;
326}
327\end{c++}
328\end{tabular}
329\end{cquote}
330However, there is no mechanism to iterate through an enumeration without an unsafe cast and it does not understand the enumerator values.
331\begin{c++}
332enum Week { Mon, Tue, Wed, Thu = 10, Fri, Sat, Sun };
333for ( Week d = Mon; d <= Sun; d = @(Week)(d + 1)@ ) cout << d << ' ';
3340 1 2 @3 4 5 6 7 8 9@ 10 11 12 13
335\end{c++}
336An enumeration type cannot declare an array dimension but an enumerator can be used as a subscript.
337There is no mechanism to subtype or inherit from an enumeration.
338
339
340\section{C\raisebox{-0.7ex}{\LARGE$^\sharp$}\xspace} % latex bug: cannot use \relsize{2} so use \LARGE
341\label{s:Csharp}
342
343% https://www.tutorialsteacher.com/codeeditor?cid=cs-mk8Ojx
344% https://learn.microsoft.com/en-us/dotnet/api/system.enum?view=net-8.0
345% https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/enums
346
347\Csharp is a dynamically-typed programming-language with a scoped, integral enumeration similar to \CC \lstinline[language=C++]{enum class}.
348\begin{csharp}
349enum Week : @long@ { Mon, Tue, Wed, Thu@ = 10@, Fri, Sat, Sun@,@ } // terminating comma
350enum RGB { Red, Green, Blue }
351\end{csharp}
352The default underlying integral type is @int@ (no @char@), with auto-incrementing, implicit/explicit initialization, and terminating comma.
353A method cannot be defined in an enumeration type (extension methods are possible).
354There is an explicit bidirectional conversion between an enumeration and its integral type, and an implicit conversion to the enumerator label in display contexts.
355\begin{csharp}
356int iday = (int)Week.Fri;                       $\C{// day == 11}$
357Week day = @(Week)@42;                          $\C{// day == 42, unsafe}$
358string mon = Week.Mon.ToString();       $\C{// mon == "Mon"}$
359RGB rgb = RGB.Red;                                      $\C{// rgb == "Red"}$
360day = @(Week)@rgb;                                      $\C{// day == "Mon", unsafe}$
361Console.WriteLine( Week.Fri );          $\C{// print label Fri}$
362\end{csharp}
363The majority of the integral operators (relational and arithmetic) work with enumerations, except @*@ and @/@.
364\begin{csharp}
365day = day++ - 5;                                        $\C{// unsafe}$
366day = day & day;
367\end{csharp}
368
369An enumeration can be used in the @if@ and @switch@ statements.
370\begin{cquote}
371\setlength{\tabcolsep}{15pt}
372\begin{tabular}{@{}ll@{}}
373\begin{csharp}
374if ( @day@ <= Week.Fri )
375        Console.WriteLine( "weekday" );
376
377
378
379
380
381\end{csharp}
382&
383\begin{csharp}
384switch ( @day@ ) {
385  case Week.Mon: case Week.Tue: case Week.Wed:
386  case Week.Thu: case Week.Fri:
387        Console.WriteLine( "weekday" ); break;
388  case Week.Sat: case Week.Sun:
389        Console.WriteLine( "weekend" ); break;
390}
391\end{csharp}
392\end{tabular}
393\end{cquote}
394However, there is no mechanism to iterate through an enumeration without an unsafe cast to increment and positions versus values is not handled.
395\begin{csharp}
396for ( Week d = Mon; d <= Sun; @d += 1@ ) {
397        Console.Write( d + " " );
398}
399Mon Tue Wed @3 4 5 6 7 8 9@ Thu Fri Sat Sun
400\end{csharp}
401The @Enum.GetValues@ pseudo-method retrieves an array of the enumeration constants for looping over an enumeration type or variable (expensive operation).
402\begin{csharp}
403foreach ( Week d in @Enum.GetValues@( typeof(Week) ) ) {
404        Console.WriteLine( d + " " + (int)d + " " ); // label, position
405}
406Mon 0, Tue 1, Wed 2, Thu 10, Fri 11, Sat 12, Sun 13,
407\end{csharp}
408
409An enumeration type cannot declare an array dimension but an enumerator can be used as a subscript.
410There is no mechanism to subtype or inherit from an enumeration.
411
412The @Flags@ attribute creates a bit-flags enumeration, making bitwise operators @&@, @|@, @~@ (complement), @^@ (xor) sensible.
413\begin{csharp}
414@[Flags]@ public enum Week {
415        None = 0x0, Mon = 0x1, Tue = 0x2, Wed = 0x4,
416        Thu = 0x8, Fri = 0x10, Sat = 0x20, Sun = 0x40,
417        Weekdays = @Mon | Tue | Wed | Thu | Fri@ $\C{// Weekdays == 0x1f}$
418        Weekend = @Sat | Sun@,                  $\C{// Weekend == 0x60}$
419}
420Week meetings = @Week.Mon | Week.Wed@; $\C{// 0x5}$
421\end{csharp}
422
423
424\section{Golang}
425\label{s:Golang}
426
427Golang has a no enumeration.
428It has @const@ aliasing declarations, similar to \CC \see{\VRef{s:C++RelatedWork}}, for basic types with type inferencing and static initialization (constant expression).
429\begin{Go}
430const R @int@ = 0;  const G @uint@ = 1;  const B = 2; $\C{// explicit typing and type inferencing}$
431const Fred = "Fred";  const Mary = "Mary";  const Jane = "Jane";
432const S = 0;  const T = 0;
433const USA = "USA";  const U = "USA";
434const V = 3.1;  const W = 3.1;
435\end{Go}
436Since these declarations are unmutable variables, they are unscoped and Golang has no overloading.
437
438Golang provides an enumeration-like feature to group together @const@ declaration into a block and introduces a form of auto-initialization.
439\begin{Go}
440const ( R = 0; G; B )                                   $\C{// implicit initialization: 0 0 0}$
441const ( Fred = "Fred"; Mary = "Mary"; Jane = "Jane" ) $\C{// explicit initialization: Fred Mary Jane}$
442const ( S = 0; T; USA = "USA"; U; V = 3.1; W ) $\C{// type change, implicit/explicit: 0 0 USA USA 3.1 3.1}$
443\end{Go}
444The first identifier \emph{must} be explicitly initialized;
445subsequent identifiers can be implicitly or explicitly initialized.
446Implicit initialization is the \emph{previous} (predecessor) identifier value.
447
448Each @const@ declaration provides an implicit integer counter starting at zero, called \lstinline[language=Go]{iota}.
449Using \lstinline[language=Go]{iota} outside of a @const@ block always sets the identifier to zero.
450\begin{Go}
451const R = iota;                                                 $\C{// 0}$
452\end{Go}
453Inside 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.
454\begin{Go}
455const ( R = @iota@; G; B )                              $\C{// implicit: 0 1 2}$
456const ( C = @iota + B + 1@; G; Y )              $\C{// implicit: 3 4 5}$
457\end{Go}
458An underscore \lstinline[language=golang]{const} identifier advances \lstinline[language=Go]{iota}.
459\begin{Go}
460const ( O1 = iota + 1; @_@; O3; @_@; O5 ) // 1, 3, 5 
461\end{Go}
462Auto-initialization reverts from \lstinline[language=Go]{iota} to the previous value after an explicit initialization, but auto-incrementing of \lstinline[language=Go]{iota} continues.
463\begin{Go}
464const ( Mon = iota; Tue; Wed; // 0, 1, 2
465                @Thu = 10@; Fri; Sat; Sun = itoa ) // 10, 10, 10, 6
466\end{Go}
467Auto-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}.
468\begin{Go}
469const ( V1 = iota; V2; @V3 = 7;@ V4 = @iota@ + 1; V5 ) // 0 1 7 4 5
470const ( Mon = iota; Tue; Wed; // 0, 1, 2
471                @Thu = 10;@ Fri = @iota - Wed + Thu - 1@; Sat; Sun ) // 10, 11, 12, 13
472\end{Go}
473Here, @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}.
474Note, because \lstinline[language=Go]{iota} is incremented for an explicitly initialized identifier or @_@,
475at @Fri@ \lstinline[language=Go]{iota} is 4 requiring the minus one to compute the value for @Fri@.
476
477Basic switch and looping are possible.
478\begin{cquote}
479\setlength{\tabcolsep}{20pt}
480\begin{tabular}{@{}ll@{}}
481\begin{Go}
482day := Mon;     // := $\(\Rightarrow\)$ type inferencing
483switch @day@ {
484  case Mon, Tue, Wed, Thu, Fri:
485        fmt.Println( "weekday" );
486  case Sat, Sun:
487        fmt.Println( "weekend" );
488}
489\end{Go}
490&
491\begin{Go}
492
493for i := @Mon@; i <= @Sun@; i += 1 {
494        fmt.Println( i )
495}
496
497
498
499\end{Go}
500\end{tabular}
501\end{cquote}
502However, the loop prints the values from 0 to 13 because there is no actual enumeration.
503
504A constant variable can be used as an array dimension or a subscript.
505\begin{Go}
506var ar[@Sun@] int
507ar[@Mon@] = 3
508\end{Go}
509
510
511\section{Java}
512
513Java provides an enumeration using a specialized class.
514A basic Java enumeration is an opaque enumeration, where the enumerators are constants.
515\begin{Java}
516enum Week {
517        Mon, Tue, Wed, Thu, Fri, Sat, Sun;
518}
519Week day = Week.Sat;
520\end{Java}
521The enumerators members are scoped and cannot be made \lstinline[language=java]{public}, hence require qualification.
522The value of an enumeration instance is restricted to its enumerators.
523
524The position (ordinal) and label are accessible but there is no value.
525\begin{Java}
526System.out.println( day.!ordinal()! + " " + !day! + " " + day.!name()! );
5275 Sat Sat
528\end{Java}
529Since @day@ has no value, it prints its label (name).
530The member @valueOf@ is the inverse of @name@ converting a string to enumerator.
531\begin{Java}
532day = Week.valueOf( "Wed" );
533\end{Java}
534Extra members can be added to provide specialized operations.
535\begin{Java}
536public boolean isWeekday() { return !ordinal()! <= Fri.ordinal(); }
537public boolean isWeekend() { return Fri.ordinal() < !ordinal()!; }
538\end{Java}
539Notice the unqualified calls to @ordinal@ in the members implying a \lstinline[language=Java]{this} to some implicit implementation variable, likely an @int@.
540
541Enumerator values require an enumeration type (any Java type may be used) and implementation member.
542\begin{Java}
543enum Week {
544        Mon!(1)!, Tue!(2)!, Wed!(3)!, Thu!(4)!, Fri!(5)!, Sat!(6)!, Sun!(7)!; // must appear first
545        private !long! day;                                     $\C{// enumeration type and implementation member}$
546        private Week( !long! d ) { day = d; } $\C{// enumerator initialization}$
547};
548Week day = Week.Sat;
549\end{Java}
550The position, value, and label are accessible.
551\begin{Java}
552System.out.println( !day.ordinal()! + " " + !day.day! + " " + !day.name()! );
5535 6 Sat
554\end{Java}
555If 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@.
556The implementation constructor must be private since it is only used internally to initialize the enumerators.
557Initialization occurs at the enumeration-type declaration for each enumerator in the first line.
558
559Enumerations can be used in the @if@ and @switch@ statements but only for equality tests.
560\begin{cquote}
561\setlength{\tabcolsep}{15pt}
562\begin{tabular}{@{}ll@{}}
563\begin{Java}
564if ( !day! == Week.Fri )
565        System.out.println( "Fri" );
566
567
568
569
570\end{Java}
571&
572\begin{Java}
573switch ( !day! ) {
574  case Mon: case Tue: case Wed: case Thu: case Fri:
575        System.out.println( "weekday" );  break;
576  case Sat: case Sun:
577        System.out.println( "weekend" );  break;
578}
579\end{Java}
580\end{tabular}
581\end{cquote}
582Notice enumerators in the @switch@ statement do not require qualification.
583
584There 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}.
585Like \Csharp, looping over an enumeration is done using method @values@, which returns an array of enumerator values (expensive operation).
586\begin{Java}
587for ( Week d : Week.values() ) {
588        System.out.print( d.ordinal() + d.day + " " +  d.name() + ",  " );
589}
5900 1 Mon,  1 2 Tue,  2 3 Wed,  3 4 Thu,  4 5 Fri,  5 6 Sat,  6 7 Sun, 
591\end{Java}
592
593An enumeration type cannot declare an array dimension nor can an enumerator be used as a subscript.
594Enumeration inheritence is disallowed because an enumeration is \lstinline[language=Java]{final}.
595
596Java 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.
597There is also a specialized version of @HashMap@ with enumerator keys, which has performance benefits.
598
599
600\section{Rust}
601
602% https://doc.rust-lang.org/reference/items/enumerations.html
603
604Rust @enum@ provides two largely independent mechanisms: an ADT and an enumeration.
605When @enum@ is an ADT, pattern matching is used to discriminate among the variant types.
606\begin{cquote}
607\sf\setlength{\tabcolsep}{20pt}
608\begin{tabular}{@{}ll@{}}
609\begin{rust}
610struct S {
611        i : isize,  j : isize
612}
613enum @ADT@ {
614        I(isize),   // int
615        F(f64),   // float
616        S(S),     // struct
617}
618\end{rust}
619&
620\begin{rust}
621let mut s = S{ i : 3, j : 4 };
622let mut adt : ADT;
623adt = ADT::I(3);  adt = ADT::F(3.5);  adt = ADT::S(s); // init examples
624@match@ adt {
625        ADT::I(i) => println!( "{:?}", i ),
626        ADT::F(f) => println!( "{:?}", f ),
627        ADT::S(s) => println!( "{:?} {:?}", s.i, s.j ),
628}
629\end{rust}
630\end{tabular}
631\end{cquote}
632When the variant types are the unit type, the ADT is still not an enumeration because there is no enumerating \see{\VRef{s:AlgebraicDataType}}.
633\begin{rust}
634enum Week { Mon, Tues, Wed, Thu, Fri, Sat, Sun@,@ } // terminating comma
635let mut week : Week = Week::Mon;
636match week {
637        Week::Mon => println!( "Mon" ),
638        ...
639        Week::Sun => println!( "Sun" ),
640}
641\end{rust}
642
643However, Rust allows direct setting of the ADT constructor, which means it is actually a tag.
644\begin{cquote}
645\sf\setlength{\tabcolsep}{15pt}
646\begin{tabular}{@{}ll@{}}
647\begin{rust}
648enum Week {
649        Mon, Tues, Wed, // start 0
650        Thu @= 10@, Fri,
651        Sat, Sun,
652}
653
654\end{rust}
655&
656\begin{rust}
657#[repr(u8)]
658enum ADT {
659        I(isize) @= 5@,  // ???
660        F(f64) @= 10@,
661        S(S) @= 0@,
662}
663\end{rust}
664\end{tabular}
665\end{cquote}
666Through 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}.
667When tags represent non-unit types, Rust largely precludes accessing the tag because the semantics become meaningless.
668Hence, the two mechanisms are largely disjoint, and ony the enumeration component is discussed.
669
670In detail, the @enum@ type has an implicit integer tag (discriminant), with a unique value for each variant type.
671Direct initialization is by a compile-time expression generating a constant value.
672Indirect initialization (without initialization, @Fri@/@Sun@) is auto-initialized: from left to right, starting at zero or the next explicitly initialized constant, incrementing by @1@.
673There is an explicit cast from the tag to integer.
674\begin{rust}
675let mut mon : isize = Week::Mon as isize;
676\end{rust}
677An enumeration can be used in the @if@ and \lstinline[language=rust]{match} (@switch@) statements.
678\begin{cquote}
679\setlength{\tabcolsep}{8pt}
680\begin{tabular}{@{}ll@{}}
681\begin{c++}
682if @week as isize@ == Week::Mon as isize {
683        println!( "{:?}", week );
684}
685
686
687\end{c++}
688&
689\begin{c++}
690match @week@ {
691        Week::Mon | Week:: Tue | Week::Wed | Week::Thu
692                | Week::Fri => println!( "weekday" ),
693        Week::Sat | Week:: Sun => println!( "weekend" ),
694}
695\end{c++}
696\end{tabular}
697\end{cquote}
698However, there is no mechanism to iterate through an enumeration without an casting to integral and positions versus values is not handled.
699\begin{c++}
700for d in Week::Mon as isize ..= Week::Sun as isize {
701        print!( "{:?} ", d );
702}
7030 1 2 @3 4 5 6 7 8 9@ 10 11 12 13
704\end{c++}
705An enumeration type cannot declare an array dimension nor as a subscript.
706There is no mechanism to subtype or inherit from an enumeration.
707
708
709\section{Swift}
710
711% https://www.programiz.com/swift/online-compiler
712
713A Swift enumeration provides a heterogenous set of enumerators, like a tagged @union@, where the field name is the enumerator and its list of type parameters form its type.
714\begin{swift}
715enum Many {
716        case Mon, Tue, Wed, Thu, Fri, Sat, Sun // basic enumerator
717        case code( String ) // string enumerator
718        case tuple( Int, Int, Int ) // tuple enumerator
719};
720var day = Many.Sat; // qualification to resolve type
721print( day );
722day = .Wed // no qualification after type resolved
723print( day );
724day = .code( "ABC" );
725print( day );
726day = .tuple( 1, 2, 3 );
727print( day );
728
729Sat
730Wed
731code("ABC")
732tuple(1, 2, 3)
733\end{swift}
734
735
736An enumeration defines a common type for a group of related values and enables you to work with those values in a type-safe way within your code.
737
738If you are familiar with C, you will know that C enumerations assign related names to a set of integer values.
739Enumerations in Swift are much more flexible, and don't have to provide a value for each case of the enumeration.
740If a value (known as a raw value) is provided for each enumeration case, the value can be a string, a character, or a value of any integer or floating-point type.
741
742Alternatively, enumeration cases can specify associated values of any type to be stored along with each different case value, much as unions or variants do in other languages.
743You can define a common set of related cases as part of one enumeration, each of which has a different set of values of appropriate types associated with it.
744
745Enumerations in Swift are first-class types in their own right.
746They adopt many features traditionally supported only by classes, such as computed properties to provide additional information about the enumeration's current value, and instance methods to provide functionality related to the values the enumeration represents.
747Enumerations can also define initializers to provide an initial case value;
748can be extended to expand their functionality beyond their original implementation; and can conform to protocols to provide standard functionality.
749
750For more about these capabilities, see Properties, Methods, Initialization, Extensions, and Protocols.
751
752\paragraph{Enumeration Syntax}
753
754
755Note:
756Swift enumeration cases don't have an integer value set by default, unlike languages like C and Objective-C.
757In the CompassPoint example above, @north@, @south@, @east@ and @west@ don't implicitly equal 0, 1, 2 and 3.
758Instead, the different enumeration cases are values in their own right, with an explicitly defined type of CompassPoint.
759
760Multiple cases can appear on a single line, separated by commas:
761\begin{swift}
762enum Planet {
763        case mercury, venus, earth, mars, jupiter, saturn, uranus, neptune
764}
765\end{swift}
766Each enumeration definition defines a new type.
767Like other types in Swift, their names (such as @CompassPoint@ and @Planet@) start with a capital letter.
768Give enumeration types singular rather than plural names, so that they read as self-evident:
769\begin{swift}
770var directionToHead = CompassPoint.west
771\end{swift}
772The type of @directionToHead@ is inferred when it's initialized with one of the possible values of @CompassPoint@.
773Once @directionToHead@ is declared as a @CompassPoint@, you can set it to a different @CompassPoint@ value using a shorter dot syntax:
774\begin{swift}
775directionToHead = .east
776\end{swift}
777The type of @directionToHead@ is already known, and so you can drop the type when setting its value.
778This makes for highly readable code when working with explicitly typed enumeration values.
779
780\paragraph{Matching Enumeration Values with a Switch Statement}
781
782You can match individual enumeration values with a switch statement:
783\begin{swift}
784directionToHead = .south
785switch directionToHead {
786case .north:
787        print("Lots of planets have a north")
788case .south:
789        print("Watch out for penguins")
790case .east:
791        print("Where the sun rises")
792case .west:
793        print("Where the skies are blue")
794}
795// Prints "Watch out for penguins"
796\end{swift}
797You can read this code as:
798\begin{quote}
799"Consider the value of directionToHead.
800In the case where it equals @.north@, print "Lots of planets have a north".
801In the case where it equals @.south@, print "Watch out for penguins"."
802
803...and so on.
804\end{quote}
805As described in Control Flow, a switch statement must be exhaustive when considering an enumeration's cases.
806If the case for @.west@ is omitted, this code doesn't compile, because it doesn't consider the complete list of @CompassPoint@ cases.
807Requiring exhaustiveness ensures that enumeration cases aren't accidentally omitted.
808
809When it isn't appropriate to provide a case for every enumeration case, you can provide a default case to cover any cases that aren't addressed explicitly:
810\begin{swift}
811let somePlanet = Planet.earth
812switch somePlanet {
813case .earth:
814        print("Mostly harmless")
815default:
816        print("Not a safe place for humans")
817}
818// Prints "Mostly harmless"
819\end{swift}
820
821\paragraph{Iterating over Enumeration Cases}
822
823For some enumerations, it's useful to have a collection of all of that enumeration's cases.
824You enable this by writing @CaseIterable@ after the enumeration's name.
825Swift exposes a collection of all the cases as an allCases property of the enumeration type.
826Here's an example:
827\begin{swift}
828enum Beverage: CaseIterable {
829        case coffee, tea, juice
830}
831let numberOfChoices = Beverage.allCases.count
832print("\(numberOfChoices) beverages available")
833// Prints "3 beverages available"
834\end{swift}
835In the example above, you write @Beverage.allCases@ to access a collection that contains all of the cases of the @Beverage@ enumeration.
836You can use @allCases@ like any other collection -- the collection's elements are instances of the enumeration type, so in this case they're Beverage values.
837The example above counts how many cases there are, and the example below uses a for-in loop to iterate over all the cases.
838\begin{swift}
839for beverage in Beverage.allCases {
840        print(beverage)
841}
842// coffee
843// tea
844// juice
845\end{swift}
846The syntax used in the examples above marks the enumeration as conforming to the @CaseIterable@ protocol.
847For information about protocols, see Protocols.
848
849\paragraph{Associated Values}
850The examples in the previous section show how the cases of an enumeration are a defined (and typed) value in their own right.
851You can set a constant or variable to Planet.earth, and check for this value later.
852However, it's sometimes useful to be able to store values of other types alongside these case values.
853This additional information is called an associated value, and it varies each time you use that case as a value in your code.
854
855You can define Swift enumerations to store associated values of any given type, and the value types can be different for each case of the enumeration if needed.
856Enumerations similar to these are known as discriminated unions, tagged unions, or variants in other programming languages.
857
858For example, suppose an inventory tracking system needs to track products by two different types of barcode.
859Some products are labeled with 1D barcodes in UPC format, which uses the numbers 0 to 9.
860Each barcode has a number system digit, followed by five manufacturer code digits and five product code digits.
861These are followed by a check digit to verify that the code has been scanned correctly:
862
863Other products are labeled with 2D barcodes in QR code format, which can use any ISO 8859-1 character and can encode a string up to 2,953 characters long:
864
865It's convenient for an inventory tracking system to store UPC barcodes as a tuple of four integers, and QR code barcodes as a string of any length.
866
867In Swift, an enumeration to define product barcodes of either type might look like this:
868\begin{swift}
869enum Barcode {
870        case upc(Int, Int, Int, Int)
871        case qrCode(String)
872}
873\end{swift}
874This can be read as:
875\begin{quote}
876"Define an enumeration type called Barcode, which can take either a value of upc with an associated value of type @(Int, Int, Int, Int)@, or a value of @qrCode@ with an associated value of type @String@."
877\end{quote}
878This definition doesn't provide any actual @Int@ or @String@ values -- it just defines the type of associated values that Barcode constants and variables can store when they're equal to @Barcode.upc@ or @Barcode.qrCode@.
879
880You can then create new barcodes using either type:
881\begin{swift}
882var productBarcode = Barcode.upc(8, 85909, 51226, 3)
883\end{swift}
884This example creates a new variable called @productBarcode@ and assigns it a value of @Barcode.upc@ with an associated tuple value of @(8, 85909, 51226, 3)@.
885
886You can assign the same product a different type of barcode:
887\begin{swift}
888productBarcode = .qrCode("ABCDEFGHIJKLMNOP")
889\end{swift}
890At this point, the original @Barcode.upc@ and its integer values are replaced by the new @Barcode.qrCode@ and its string value.
891Constants and variables of type Barcode can store either a @.upc@ or a @.qrCode@ (together with their associated values), but they can store only one of them at any given time.
892
893You can check the different barcode types using a switch statement, similar to the example in Matching Enumeration Values with a Switch Statement.
894This time, however, the associated values are extracted as part of the switch statement.
895You extract each associated value as a constant (with the let prefix) or a variable (with the var prefix) for use within the switch case's body:
896\begin{swift}
897switch productBarcode {
898case .upc(let numberSystem, let manufacturer, let product, let check):
899        print("UPC: \(numberSystem), \(manufacturer), \(product), \(check).")
900case .qrCode(let productCode):
901        print("QR code: \(productCode).")
902}
903// Prints "QR code: ABCDEFGHIJKLMNOP."
904\end{swift}
905If all of the associated values for an enumeration case are extracted as constants, or if all are extracted as variables, you can place a single let or var annotation before the case name, for brevity:
906\begin{swift}
907switch productBarcode {
908case let .upc(numberSystem, manufacturer, product, check):
909        print("UPC : \(numberSystem), \(manufacturer), \(product), \(check).")
910case let .qrCode(productCode):
911        print("QR code: \(productCode).")
912}
913// Prints "QR code: ABCDEFGHIJKLMNOP."
914\end{swift}
915
916\paragraph{Raw Values}
917
918The barcode example in Associated Values shows how cases of an enumeration can declare that they store associated values of different types.
919As an alternative to associated values, enumeration cases can come prepopulated with default values (called raw values), which are all of the same type.
920
921Here's an example that stores raw ASCII values alongside named enumeration cases:
922\begin{swift}
923enum ASCIIControlCharacter: Character {
924        case tab = "\t"
925        case lineFeed = "\n"
926        case carriageReturn = "\r"
927}
928\end{swift}
929Here, the raw values for an enumeration called ASCIIControlCharacter are defined to be of type Character, and are set to some of the more common ASCII control characters.
930Character values are described in Strings and Characters.
931
932Raw values can be strings, characters, or any of the integer or floating-point number types.
933Each raw value must be unique within its enumeration declaration.
934
935Note
936
937Raw values are not the same as associated values.
938Raw values are set to prepopulated values when you first define the enumeration in your code, like the three ASCII codes above.
939The raw value for a particular enumeration case is always the same.
940Associated values are set when you create a new constant or variable based on one of the enumeration's cases, and can be different each time you do so.
941Implicitly Assigned Raw Values
942
943When you're working with enumerations that store integer or string raw values, you don't have to explicitly assign a raw value for each case.
944When you don't, Swift automatically assigns the values for you.
945
946For example, when integers are used for raw values, the implicit value for each case is one more than the previous case.
947If the first case doesn't have a value set, its value is 0.
948
949The enumeration below is a refinement of the earlier Planet enumeration, with integer raw values to represent each planet's order from the sun:
950
951\begin{swift}
952enum Planet: Int {
953        case mercury = 1, venus, earth, mars, jupiter, saturn, uranus, neptune
954}
955\end{swift}
956In the example above, Planet.mercury has an explicit raw value of 1, Planet.venus has an implicit raw value of 2, and so on.
957
958When strings are used for raw values, the implicit value for each case is the text of that case's name.
959
960The enumeration below is a refinement of the earlier CompassPoint enumeration, with string raw values to represent each direction's name:
961\begin{swift}
962enum CompassPoint: String {
963        case north, south, east, west
964}
965\end{swift}
966In the example above, CompassPoint.south has an implicit raw value of "south", and so on.
967
968You access the raw value of an enumeration case with its rawValue property:
969\begin{swift}
970let earthsOrder = Planet.earth.rawValue
971// earthsOrder is 3
972
973let sunsetDirection = CompassPoint.west.rawValue
974// sunsetDirection is "west"
975\end{swift}
976
977\paragraph{Initializing from a Raw Value}
978
979If you define an enumeration with a raw-value type, the enumeration automatically receives an initializer that takes a value of the raw value's type (as a parameter called rawValue) and returns either an enumeration case or nil.
980You can use this initializer to try to create a new instance of the enumeration.
981
982This example identifies Uranus from its raw value of 7:
983\begin{swift}
984let possiblePlanet = Planet(rawValue: 7)
985// possiblePlanet is of type Planet? and equals Planet.uranus
986\end{swift}
987Not all possible Int values will find a matching planet, however.
988Because of this, the raw value initializer always returns an optional enumeration case.
989In the example above, possiblePlanet is of type Planet?, or "optional Planet."
990Note
991
992The raw value initializer is a failable initializer, because not every raw value will return an enumeration case.
993For more information, see Failable Initializers.
994
995If you try to find a planet with a position of 11, the optional Planet value returned by the raw value initializer will be nil:
996\begin{swift}
997let positionToFind = 11
998if let somePlanet = Planet(rawValue: positionToFind) {
999        switch somePlanet {
1000        case .earth:
1001                print("Mostly harmless")
1002        default:
1003                print("Not a safe place for humans")
1004        }
1005} else {
1006        print("There isn't a planet at position \(positionToFind)")
1007}
1008// Prints "There isn't a planet at position 11"
1009\end{swift}
1010This example uses optional binding to try to access a planet with a raw value of 11.
1011The statement if let somePlanet = Planet(rawValue: 11) creates an optional Planet, and sets somePlanet to the value of that optional Planet if it can be retrieved.
1012In this case, it isn't possible to retrieve a planet with a position of 11, and so the else branch is executed instead.
1013
1014\paragraph{Recursive Enumerations}
1015
1016A recursive enumeration is an enumeration that has another instance of the enumeration as the associated value for one or more of the enumeration cases.
1017You indicate that an enumeration case is recursive by writing indirect before it, which tells the compiler to insert the necessary layer of indirection.
1018
1019For example, here is an enumeration that stores simple arithmetic expressions:
1020\begin{swift}
1021enum ArithmeticExpression {
1022        case number(Int)
1023        indirect case addition(ArithmeticExpression, ArithmeticExpression)
1024        indirect case multiplication(ArithmeticExpression, ArithmeticExpression)
1025}
1026\end{swift}
1027You can also write indirect before the beginning of the enumeration to enable indirection for all of the enumeration's cases that have an associated value:
1028\begin{swift}
1029indirect enum ArithmeticExpression {
1030        case number(Int)
1031        case addition(ArithmeticExpression, ArithmeticExpression)
1032        case multiplication(ArithmeticExpression, ArithmeticExpression)
1033}
1034\end{swift}
1035This enumeration can store three kinds of arithmetic expressions: a plain number, the addition of two expressions, and the multiplication of two expressions.
1036The addition and multiplication cases have associated values that are also arithmetic expressions -- these associated values make it possible to nest expressions.
1037For example, the expression (5 + 4) * 2 has a number on the right-hand side of the multiplication and another expression on the left-hand side of the multiplication.
1038Because the data is nested, the enumeration used to store the data also needs to support nesting -- this means the enumeration needs to be recursive.
1039The code below shows the ArithmeticExpression recursive enumeration being created for (5 + 4) * 2:
1040\begin{swift}
1041let five = ArithmeticExpression.number(5)
1042let four = ArithmeticExpression.number(4)
1043let sum = ArithmeticExpression.addition(five, four)
1044let product = ArithmeticExpression.multiplication(sum, ArithmeticExpression.number(2))
1045\end{swift}
1046A recursive function is a straightforward way to work with data that has a recursive structure.
1047For example, here's a function that evaluates an arithmetic expression:
1048\begin{swift}
1049func evaluate(_ expression: ArithmeticExpression) -> Int {
1050        switch expression {
1051        case let .number(value):
1052                return value
1053        case let .addition(left, right):
1054                return evaluate(left) + evaluate(right)
1055        case let .multiplication(left, right):
1056                return evaluate(left) * evaluate(right)
1057        }
1058}
1059
1060print(evaluate(product))
1061// Prints "18"
1062\end{swift}
1063This function evaluates a plain number by simply returning the associated value.
1064It evaluates an addition or multiplication by evaluating the expression on the left-hand side, evaluating the expression on the right-hand side, and then adding them or multiplying them.
1065
1066
1067\section{Python 3.13}
1068% https://docs.python.org/3/howto/enum.html
1069
1070Python is a dynamically-typed reflexive programming language with multiple versions, and hence, it is possible to extend existing or build new language features within the language.
1071As 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.
1072Nevertheless, the following is a discuss of the core enumeration features that come with Python 3.13.
1073
1074A Python enumeration type is a set of ordered scoped identifiers (enumerators) bound to \emph{unique} values.
1075An enumeration is not a basic type;
1076it is a @class@ inheriting from the @Enum@ class, where the enumerators must be explicitly initialized, \eg:
1077\begin{python}
1078class Week(@Enum@): Mon = 1; Tue = 2; Wed = 3; Thu = 4; Fri = 5; Sat = 6; Sun = 7
1079\end{python}
1080and/or explicitly auto initialized, \eg:
1081\begin{python}
1082class Week(Enum): Mon = 1; Tue = 2; Wed = 3; Thu = 10; Fri = @auto()@; Sat = 4; Sun = @auto()@
1083\end{python}
1084where @auto@ increments by 1 from the previous enumerator value \see{Golang \lstinline[language=Go]{iota}, \VRef{s:Golang}}.
1085Object initialization and assignment are restricted to the enumerators of this type.
1086An enumerator initialized with same value is an alias and invisible at the enumeration level, \ie the alias it substituted for its aliasee.
1087\begin{python}
1088class Week(Enum): Mon = 1; Tue = 2; Wed = 3; Thu = 10; Fri = @10@; Sat = @10@; Sun = @10@
1089\end{python}
1090Here, the enumeration has only 4 enumerators and 3 aliases.
1091An alias is only visible by dropping down to the @class@ level and asking for class members.
1092@Enum@ only supports equality comparison between enumerator values;
1093the extended class @OrderedEnum@ adds relational operators @<@, @<=@, @>@, and @>=@.
1094
1095There are bidirectional enumeration pseudo-functions for label and value, but there is no concept of access using ordering (position).
1096\begin{cquote}
1097\setlength{\tabcolsep}{15pt}
1098\begin{tabular}{@{}ll@{}}
1099\begin{python}
1100Week.Thu.value == 10;
1101Week.Thu.name == 'Thu';
1102\end{python}
1103&
1104\begin{python}
1105Week( 10 ) == Thu
1106Week['Thu'].value = 10
1107\end{python}
1108\end{tabular}
1109\end{cquote}
1110
1111An enumeration \lstinline[language=python]{class} can have methods.
1112\begin{python}
1113class Week(Enum):
1114        Mon = 1; Tue = 2; Wed = 3; Thu = 4; Fri = 5; Sat = 6; Sun = 7
1115        $\\@$classmethod
1116        def today(cls, date):
1117                return cls(date.isoweekday())
1118print( "today:", Week.today(date.today()))
1119today: Week.Mon
1120\end{python}
1121The method @today@ retrieves the day of the week and uses it as an index to print out the corresponding label of @Week@.
1122
1123@Flag@ allows combining several members into a single variable:
1124\begin{python}
1125print( repr(WeekF.Sat | WeekF.Sun) )
1126<WeekF.Sun|Sat: 96>
1127\end{python}
1128You can even iterate over a @Flag@ variable:
1129\begin{python}
1130for day in weekend:
1131        print(day)
1132WeekF.Sat
1133WeekF.Sun
1134\end{python}
1135Okay, let's get some chores set up:
1136\begin{python}
1137>>> chores_for_ethan = {
1138...    'feed the cat': Week.MONDAY | Week.WEDNESDAY | Week.FRIDAY,
1139...    'do the dishes': Week.TUESDAY | Week.THURSDAY,
1140...    'answer SO questions': Week.SATURDAY,
1141...    }
1142\end{python}
1143And a function to display the chores for a given day:
1144\begin{python}
1145>>> def show_chores(chores, day):
1146...    for chore, days in chores.items():
1147...        if day in days:
1148...            print(chore)
1149>>> show_chores(chores_for_ethan, Week.SATURDAY)
1150answer SO questions
1151\end{python}
1152Auto incrmenet for @Flag@ is by powers of 2.
1153\begin{python}
1154class WeekF(Flag): Mon = auto(); Tue = auto(); Wed = auto(); Thu = auto(); Fri = auto()\
1155                                                        Sat = auto(); Sun = auto(); Weekend = Sat | Sun
1156for d in WeekF:
1157        print( f"{d.name}: {d.value}", end=" ")
1158Mon: 1 Tue: 2 Wed: 4 Thu: 8 Fri: 16 Sat: 32 Sun: 64 WeekA.Weekend
1159\end{python}
1160
1161\subsection{Programmatic access to enumeration members and their attributes}
1162
1163Sometimes it's useful to access members in enumerations programmatically (i.e. situations where @Color.RED@ won't do because the exact color is not known at program-writing time).
1164@Enum@ allows such access:
1165\begin{python}
1166print(RGB(1), RGB(3), )
1167RGB.RED RGB.GREEN
1168\end{python}
1169If you want to access enum members by name, use item access:
1170\begin{python}
1171print( RGBa['RED'], RGBa['GREEN'] )
1172RGB.RED RGB.GREEN
1173\end{python}
1174If you have an enum member and need its name or value:
1175\begin{python}
1176member = RGBa.RED
1177print( f"{member.name} {member.value}" )
1178RED 1
1179\end{python}
1180
1181
1182\subsection{Ensuring unique enumeration values}
1183
1184By default, enumerations allow multiple names as aliases for the same value.
1185When this behavior isn't desired, you can use the @unique()@ decorator:
1186\begin{python}
1187from enum import Enum, unique
1188$@$unique
1189class DupVal(Enum): ONE = 1; TWO = 2; THREE = 3; FOUR = 3
1190ValueError: duplicate values found in <enum 'Mistake'>: FOUR -> THREE
1191\end{python}
1192
1193\subsection{Using automatic values}
1194
1195If the exact value is unimportant you can use @auto@:
1196\begin{python}
1197from enum import Enum, auto
1198class RGBa(Enum): RED = auto(); BLUE = auto(); GREEN = auto()
1199\end{python}
1200(Like Golang @iota@.)
1201The values are chosen by @_generate_next_value_()@, which can be overridden:
1202\begin{python}
1203>>> class AutoName(Enum):
1204...     $@$staticmethod
1205...     def _generate_next_value_(name, start, count, last_values):
1206...         return name
1207...
1208>>> class Ordinal(AutoName):
1209...     NORTH = auto()
1210...     SOUTH = auto()
1211...     EAST = auto()
1212...     WEST = auto()
1213...
1214>>> [member.value for member in Ordinal]
1215['NORTH', 'SOUTH', 'EAST', 'WEST']
1216\end{python}
1217Note The @_generate_next_value_()@ method must be defined before any members.
1218
1219\subsection{Iteration}
1220
1221Iterating over the members of an enum does not provide the aliases:
1222\begin{python}
1223>>> list(Shape)
1224[<Shape.SQUARE: 2>, <Shape.DIAMOND: 1>, <Shape.CIRCLE: 3>]
1225>>> list(Week)
1226[<Week.MONDAY: 1>, <Week.TUESDAY: 2>, <Week.WEDNESDAY: 4>, <Week.THURSDAY: 8>,
1227<Week.FRIDAY: 16>, <Week.SATURDAY: 32>, <Week.SUNDAY: 64>]
1228\end{python}
1229Note that the aliases @Shape.ALIAS_FOR_SQUARE@ and @Week.WEEKEND@ aren't shown.
1230
1231The special attribute @__members__@ is a read-only ordered mapping of names to members.
1232It includes all names defined in the enumeration, including the aliases:
1233\begin{python}
1234>>> for name, member in Shape.__members__.items():
1235...     name, member
1236...
1237('SQUARE', <Shape.SQUARE: 2>)
1238('DIAMOND', <Shape.DIAMOND: 1>)
1239('CIRCLE', <Shape.CIRCLE: 3>)
1240('ALIAS_FOR_SQUARE', <Shape.SQUARE: 2>)
1241\end{python}
1242The @__members__@ attribute can be used for detailed programmatic access to the enumeration members.
1243For example, finding all the aliases:
1244\begin{python}
1245>>> [name for name, member in Shape.__members__.items() if member.name != name]
1246['ALIAS_FOR_SQUARE']
1247\end{python}
1248Note: Aliases for flags include values with multiple flags set, such as 3, and no flags set, i.e. 0.
1249
1250\subsection{Comparisons}
1251
1252Enumeration members are compared by identity:
1253\begin{python}
1254>>> Color.RED is Color.RED
1255True
1256>>> Color.RED is Color.BLUE
1257False
1258>>> Color.RED is not Color.BLUE
1259True
1260\end{python}
1261Ordered comparisons between enumeration values are not supported.
1262Enum members are not integers (but see @IntEnum@ below):
1263\begin{python}
1264>>> Color.RED < Color.BLUE
1265Traceback (most recent call last):
1266  File "<stdin>", line 1, in <module>
1267TypeError: '<' not supported between instances of 'Color' and 'Color'
1268\end{python}
1269Equality comparisons are defined though:
1270\begin{python}
1271>>> Color.BLUE == Color.RED
1272False
1273>>> Color.BLUE != Color.RED
1274True
1275>>> Color.BLUE == Color.BLUE
1276True
1277\end{python}
1278Comparisons against non-enumeration values will always compare not equal (again, @IntEnum@ was explicitly designed to behave differently, see below):
1279\begin{python}
1280>>> Color.BLUE == 2
1281False
1282\end{python}
1283
1284Warning: It is possible to reload modules -- if a reloaded module contains enums, they will be recreated, and the new members may not compare identical/equal to the original members.
1285
1286\subsection{Allowed members and attributes of enumerations}
1287
1288Most of the examples above use integers for enumeration values.
1289Using integers is short and handy (and provided by default by the Functional API), but not strictly enforced.
1290In the vast majority of use-cases, one doesn't care what the actual value of an enumeration is.
1291But if the value is important, enumerations can have arbitrary values.
1292
1293Enumerations are Python classes, and can have methods and special methods as usual. If we have this enumeration:
1294\begin{python}
1295>>> class Mood(Enum):
1296...     FUNKY = 1
1297...     HAPPY = 3
1298...
1299...     def describe(self):
1300...         # self is the member here
1301...         return self.name, self.value
1302...
1303...     def __str__(self):
1304...         return 'my custom str! {0}'.format(self.value)
1305...
1306...     $@$classmethod
1307...
1308...     def favorite_mood(cls):
1309...         # cls here is the enumeration
1310...         return cls.HAPPY
1311...
1312\end{python}
1313Then:
1314\begin{python}
1315>>> Mood.favorite_mood()
1316<Mood.HAPPY: 3>
1317>>> Mood.HAPPY.describe()
1318('HAPPY', 3)
1319>>> str(Mood.FUNKY)
1320'my custom str! 1'
1321\end{python}
1322The rules for what is allowed are as follows: names that start and end with a single underscore are reserved by enum and cannot be used;
1323all other attributes defined within an enumeration will become members of this enumeration, with the exception of special methods (@__str__()@, @__add__()@, etc.), descriptors (methods are also descriptors), and variable names listed in @_ignore_@.
1324
1325Note: if your enumeration defines @__new__()@ and/or @__init__()@, any value(s) given to the enum member will be passed into those methods.
1326See Planet for an example.
1327
1328Note: The @__new__()@ method, if defined, is used during creation of the Enum members;
1329it is then replaced by Enum's @__new__()@ which is used after class creation for lookup of existing members.
1330See When to use @__new__()@ vs. @__init__()@ for more details.
1331
1332\subsection{Restricted Enum subclassing}
1333
1334A new @Enum@ class must have one base enum class, up to one concrete data type, and as many object-based mixin classes as needed.
1335The order of these base classes is:
1336\begin{python}
1337class EnumName([mix-in, ...,] [data-type,] base-enum):
1338        pass
1339\end{python}
1340Also, subclassing an enumeration is allowed only if the enumeration does not define any members.
1341So this is forbidden:
1342\begin{python}
1343>>> class MoreColor(Color):
1344...     PINK = 17
1345...
1346Traceback (most recent call last):
1347...
1348TypeError: <enum 'MoreColor'> cannot extend <enum 'Color'>
1349\end{python}
1350But this is allowed:
1351\begin{python}
1352>>> class Foo(Enum):
1353...     def some_behavior(self):
1354...         pass
1355...
1356>>> class Bar(Foo):
1357...     HAPPY = 1
1358...     SAD = 2
1359...
1360\end{python}
1361Allowing subclassing of enums that define members would lead to a violation of some important invariants of types and instances.
1362On the other hand, it makes sense to allow sharing some common behavior between a group of enumerations. (See OrderedEnum for an example.)
1363
1364\subsection{Dataclass support}
1365
1366When inheriting from a @dataclass@, the @__repr__()@ omits the inherited class' name.
1367For example:
1368\begin{python}
1369>>> from dataclasses import dataclass, field
1370>>> $@$dataclass
1371... class CreatureDataMixin:
1372...     size: str
1373...     legs: int
1374...     tail: bool = field(repr=False, default=True)
1375...
1376>>> class Creature(CreatureDataMixin, Enum):
1377...     BEETLE = 'small', 6
1378...     DOG = 'medium', 4
1379...
1380>>> Creature.DOG
1381<Creature.DOG: size='medium', legs=4>
1382\end{python}
1383Use the @dataclass()@ argument repr=False to use the standard @repr()@.
1384
1385Changed in version 3.12: Only the dataclass fields are shown in the value area, not the dataclass' name.
1386
1387\subsection{Pickling}
1388
1389Enumerations can be pickled and unpickled:
1390\begin{python}
1391>>> from test.test_enum import Fruit
1392>>> from pickle import dumps, loads
1393>>> Fruit.TOMATO is loads(dumps(Fruit.TOMATO))
1394True
1395\end{python}
1396The usual restrictions for pickling apply: picklable enums must be defined in the top level of a module, since unpickling requires them to be importable from that module.
1397
1398Note: With pickle protocol version 4 it is possible to easily pickle enums nested in other classes.
1399
1400It is possible to modify how enum members are pickled/unpickled by defining @__reduce_ex__()@ in the enumeration class.
1401The default method is by-value, but enums with complicated values may want to use by-name:
1402\begin{python}
1403>>> import enum
1404>>> class MyEnum(enum.Enum):
1405...     __reduce_ex__ = enum.pickle_by_enum_name
1406\end{python}
1407Note: Using by-name for flags is not recommended, as unnamed aliases will not unpickle.
1408
1409\subsection{Functional API}
1410
1411The @Enum@ class is callable, providing the following functional API:
1412\begin{python}
1413>>> Animal = Enum('Animal', 'ANT BEE CAT DOG')
1414>>> Animal
1415<enum 'Animal'>
1416>>> Animal.ANT
1417<Animal.ANT: 1>
1418>>> list(Animal)
1419[<Animal.ANT: 1>, <Animal.BEE: 2>, <Animal.CAT: 3>, <Animal.DOG: 4>]
1420\end{python}
1421The semantics of this API resemble @namedtuple@.
1422The first argument of the call to @Enum@ is the name of the enumeration.
1423
1424The second argument is the source of enumeration member names.
1425It can be a whitespace-separated string of names, a sequence of names, a sequence of 2-tuples with key/value pairs, or a mapping (e.g. dictionary) of names to values.
1426The last two options enable assigning arbitrary values to enumerations;
1427the others auto-assign increasing integers starting with 1 (use the @start@ parameter to specify a different starting value).
1428A new class derived from @Enum@ is returned.
1429In other words, the above assignment to Animal is equivalent to:
1430\begin{python}
1431>>> class Animal(Enum):
1432...     ANT = 1
1433...     BEE = 2
1434...     CAT = 3
1435...     DOG = 4
1436...
1437\end{python}
1438The reason for defaulting to 1 as the starting number and not 0 is that 0 is @False@ in a boolean sense, but by default enum members all evaluate to @True@.
1439
1440Pickling enums created with the functional API can be tricky as frame stack implementation details are used to try and figure out which module the enumeration is being created in (e.g. it will fail if you use a utility function in a separate module, and also may not work on IronPython or Jython).
1441The solution is to specify the module name explicitly as follows:
1442\begin{python}
1443>>> Animal = Enum('Animal', 'ANT BEE CAT DOG', module=__name__)
1444\end{python}
1445Warning: If module is not supplied, and @Enum@ cannot determine what it is, the new @Enum@ members will not be unpicklable; to keep errors closer to the source, pickling will be disabled.
1446
1447The new pickle protocol 4 also, in some circumstances, relies on @__qualname__@ being set to the location where pickle will be able to find the class.
1448For example, if the class was made available in class SomeData in the global scope:
1449\begin{python}
1450>>> Animal = Enum('Animal', 'ANT BEE CAT DOG', qualname='SomeData.Animal')
1451\end{python}
1452The complete signature is:
1453\begin{python}
1454Enum(
1455        value='NewEnumName',
1456        names=<...>,
1457        *,
1458        module='...',
1459        qualname='...',
1460        type=<mixed-in class>,
1461        start=1,
1462        )
1463\end{python}
1464\begin{itemize}
1465\item
1466@value@: What the new enum class will record as its name.
1467\item
1468@names@: The enum members.
1469This can be a whitespace- or comma-separated string (values will start at 1 unless otherwise specified):
1470\begin{python}
1471'RED GREEN BLUE' | 'RED,GREEN,BLUE' | 'RED, GREEN, BLUE'
1472\end{python}
1473or an iterator of names:
1474\begin{python}
1475['RED', 'GREEN', 'BLUE']
1476\end{python}
1477or an iterator of (name, value) pairs:
1478\begin{python}
1479[('CYAN', 4), ('MAGENTA', 5), ('YELLOW', 6)]
1480\end{python}
1481or a mapping:
1482\begin{python}
1483{'CHARTREUSE': 7, 'SEA_GREEN': 11, 'ROSEMARY': 42}
1484\end{python}
1485\item
1486module: name of module where new enum class can be found.
1487\item
1488@qualname@: where in module new enum class can be found.
1489\item
1490@type@: type to mix in to new enum class.
1491\item
1492@start@: number to start counting at if only names are passed in.
1493\end{itemize}
1494Changed in version 3.5: The start parameter was added.
1495
1496\subsection{Derived Enumerations}
1497
1498\subsection{IntEnum}
1499
1500The first variation of @Enum@ that is provided is also a subclass of @int@.
1501Members of an @IntEnum@ can be compared to integers;
1502by extension, integer enumerations of different types can also be compared to each other:
1503\begin{python}
1504>>> from enum import IntEnum
1505>>> class Shape(IntEnum):
1506...     CIRCLE = 1
1507...     SQUARE = 2
1508...
1509>>> class Request(IntEnum):
1510...     POST = 1
1511...     GET = 2
1512...
1513>>> Shape == 1
1514False
1515>>> Shape.CIRCLE == 1
1516True
1517>>> Shape.CIRCLE == Request.POST
1518True
1519\end{python}
1520However, they still can't be compared to standard @Enum@ enumerations:
1521\begin{python}
1522>>> class Shape(IntEnum):
1523...     CIRCLE = 1
1524...     SQUARE = 2
1525...
1526>>> class Color(Enum):
1527...     RED = 1
1528...     GREEN = 2
1529...
1530>>> Shape.CIRCLE == Color.RED
1531False
1532\end{python}
1533@IntEnum@ values behave like integers in other ways you'd expect:
1534\begin{python}
1535>>> int(Shape.CIRCLE)
15361
1537>>> ['a', 'b', 'c'][Shape.CIRCLE]
1538'b'
1539>>> [i for i in range(Shape.SQUARE)]
1540[0, 1]
1541\end{python}
1542
1543\subsection{StrEnum}
1544
1545The second variation of @Enum@ that is provided is also a subclass of @str@.
1546Members of a @StrEnum@ can be compared to strings;
1547by extension, string enumerations of different types can also be compared to each other.
1548
1549New in version 3.11.
1550
1551\subsection{IntFlag}
1552
1553The next variation of @Enum@ provided, @IntFlag@, is also based on @int@.
1554The difference being @IntFlag@ members can be combined using the bitwise operators (@&, |, ^, ~@) and the result is still an @IntFlag@ member, if possible.
1555Like @IntEnum@, @IntFlag@ members are also integers and can be used wherever an int is used.
1556
1557Note: Any operation on an IntFlag member besides the bit-wise operations will lose the @IntFlag@ membership.
1558
1559Bit-wise operations that result in invalid @IntFlag@ values will lose the @IntFlag@ membership.
1560See @FlagBoundary@ for details.
1561
1562New in version 3.6.
1563
1564Changed in version 3.11.
1565
1566Sample @IntFlag@ class:
1567\begin{python}
1568>>> from enum import IntFlag
1569>>> class Perm(IntFlag):
1570...     R = 4
1571...     W = 2
1572...     X = 1
1573...
1574>>> Perm.R | Perm.W
1575<Perm.R|W: 6>
1576>>> Perm.R + Perm.W
15776
1578>>> RW = Perm.R | Perm.W
1579>>> Perm.R in RW
1580True
1581\end{python}
1582It is also possible to name the combinations:
1583\begin{python}
1584>>> class Perm(IntFlag):
1585...     R = 4
1586...     W = 2
1587...     X = 1
1588...     RWX = 7
1589...
1590>>> Perm.RWX
1591<Perm.RWX: 7>
1592>>> ~Perm.RWX
1593<Perm: 0>
1594>>> Perm(7)
1595<Perm.RWX: 7>
1596\end{python}
1597Note: Named combinations are considered aliases. Aliases do not show up during iteration, but can be returned from by-value lookups.
1598
1599Changed in version 3.11.
1600
1601Another important difference between @IntFlag@ and @Enum@ is that if no flags are set (the value is 0), its boolean evaluation is @False@:
1602\begin{python}
1603>>> Perm.R & Perm.X
1604<Perm: 0>
1605>>> bool(Perm.R & Perm.X)
1606False
1607\end{python}
1608Because @IntFlag@ members are also subclasses of int they can be combined with them (but may lose @IntFlag@ membership:
1609\begin{python}
1610>>> Perm.X | 4
1611<Perm.R|X: 5>
1612
1613>>> Perm.X + 8
16149
1615\end{python}
1616Note: The negation operator, @~@, always returns an @IntFlag@ member with a positive value:
1617\begin{python}
1618>>> (~Perm.X).value == (Perm.R|Perm.W).value == 6
1619True
1620\end{python}
1621@IntFlag@ members can also be iterated over:
1622\begin{python}
1623>>> list(RW)
1624[<Perm.R: 4>, <Perm.W: 2>]
1625\end{python}
1626New in version 3.11.
1627
1628\subsection{Flag}
1629
1630The last variation is @Flag@.
1631Like @IntFlag@, @Flag@ members can be combined using the bitwise operators (@&, |, ^, ~@).
1632Unlike @IntFlag@, they cannot be combined with, nor compared against, any other @Flag@ enumeration, nor @int@.
1633While it is possible to specify the values directly it is recommended to use @auto@ as the value and let @Flag@ select an appropriate value.
1634
1635New in version 3.6.
1636
1637Like @IntFlag@, if a combination of @Flag@ members results in no flags being set, the boolean evaluation is @False@:
1638\begin{python}
1639>>> from enum import Flag, auto
1640>>> class Color(Flag):
1641...     RED = auto()
1642...     BLUE = auto()
1643...     GREEN = auto()
1644...
1645>>> Color.RED & Color.GREEN
1646<Color: 0>
1647>>> bool(Color.RED & Color.GREEN)
1648False
1649\end{python}
1650Individual flags should have values that are powers of two (1, 2, 4, 8, ...), while combinations of flags will not:
1651\begin{python}
1652>>> class Color(Flag):
1653...     RED = auto()
1654...     BLUE = auto()
1655...     GREEN = auto()
1656...     WHITE = RED | BLUE | GREEN
1657...
1658>>> Color.WHITE
1659<Color.WHITE: 7>
1660\end{python}
1661Giving a name to the ``no flags set'' condition does not change its boolean value:
1662\begin{python}
1663>>> class Color(Flag):
1664...     BLACK = 0
1665...     RED = auto()
1666...     BLUE = auto()
1667...     GREEN = auto()
1668...
1669>>> Color.BLACK
1670<Color.BLACK: 0>
1671>>> bool(Color.BLACK)
1672False
1673\end{python}
1674@Flag@ members can also be iterated over:
1675\begin{python}
1676>>> purple = Color.RED | Color.BLUE
1677>>> list(purple)
1678[<Color.RED: 1>, <Color.BLUE: 2>]
1679\end{python}
1680New in version 3.11.
1681
1682Note: For the majority of new code, @Enum@ and @Flag@ are strongly recommended, since @IntEnum@ and @IntFlag@ break some semantic promises of an enumeration (by being comparable to integers, and thus by transitivity to other unrelated enumerations).
1683@IntEnum@ and @IntFlag@ should be used only in cases where @Enum@ and @Flag@ will not do;
1684for example, when integer constants are replaced with enumerations, or for interoperability with other systems.
1685
1686\subsection{Others}
1687
1688While @IntEnum@ is part of the enum module, it would be very simple to implement independently:
1689\begin{python}
1690class IntEnum(int, Enum):
1691        pass
1692\end{python}
1693This demonstrates how similar derived enumerations can be defined;
1694for example a @FloatEnum@ that mixes in float instead of @int@.
1695
1696Some rules:
1697\begin{itemize}
1698\item
1699When subclassing @Enum@, mix-in types must appear before @Enum@ itself in the sequence of bases, as in the @IntEnum@ example above.
1700\item
1701Mix-in types must be subclassable.
1702For example, @bool@ and @range@ are not subclassable and will throw an error during Enum creation if used as the mix-in type.
1703\item
1704While @Enum@ can have members of any type, once you mix in an additional type, all the members must have values of that type, e.g. @int@ above.
1705This restriction does not apply to mix-ins which only add methods and don't specify another type.
1706\item
1707When another data type is mixed in, the value attribute is not the same as the enum member itself, although it is equivalent and will compare equal.
1708\item
1709A data type is a mixin that defines @__new__()@, or a @dataclass@
1710\item
1711\%-style formatting: @%s@ and @%r@ call the @Enum@ class's @__str__()@ and @__repr__()@ respectively; other codes (such as @%i@ or @%h@ for @IntEnum@) treat the enum member as its mixed-in type.
1712\item
1713Formatted string literals, @str.format()@, and format() will use the enum's @__str__()@ method.
1714\end{itemize}
1715Note: Because @IntEnum@, @IntFlag@, and @StrEnum@ are designed to be drop-in replacements for existing constants, their @__str__()@ method has been reset to their data types' @__str__()@ method.
1716
1717\subsection{When to use \lstinline{__new__()} vs. \lstinline{__init__()}}
1718
1719@__new__()@ must be used whenever you want to customize the actual value of the @Enum@ member.
1720Any other modifications may go in either @__new__()@ or @__init__()@, with @__init__()@ being preferred.
1721
1722For example, if you want to pass several items to the constructor, but only want one of them to be the value:
1723\begin{python}
1724>>> class Coordinate(bytes, Enum):
1725...     """
1726...     Coordinate with binary codes that can be indexed by the int code.
1727...     """
1728...     def __new__(cls, value, label, unit):
1729...         obj = bytes.__new__(cls, [value])
1730...         obj._value_ = value
1731...         obj.label = label
1732...         obj.unit = unit
1733...         return obj
1734...     PX = (0, 'P.X', 'km')
1735...     PY = (1, 'P.Y', 'km')
1736...     VX = (2, 'V.X', 'km/s')
1737...     VY = (3, 'V.Y', 'km/s')
1738
1739>>> print(Coordinate['PY'])
1740Coordinate.PY
1741
1742>>> print(Coordinate(3))
1743Coordinate.VY
1744\end{python}
1745Warning: Do not call @super().__new__()@, as the lookup-only @__new__@ is the one that is found; instead, use the data type directly.
1746
1747\subsection{Finer Points}
1748
1749Supported @__dunder__@ names
1750
1751@__members__@ is a read-only ordered mapping of member\_name:member items. It is only available on the class.
1752
1753@__new__()@, if specified, must create and return the enum members; it is also a very good idea to set the member's @_value_@ appropriately. Once all the members are created it is no longer used.
1754Supported @_sunder_@ names
1755\begin{itemize}
1756\item
1757@_name_@ -- name of the member
1758\item
1759@_value_@ -- value of the member; can be set / modified in @__new__@
1760\item
1761@_missing_@ -- a lookup function used when a value is not found; may be overridden
1762\item
1763@_ignore_@ -- a list of names, either as a @list@ or a @str@, that will not be transformed into members, and will be removed from the final class
1764\item
1765@_order_@ -- used in Python 2/3 code to ensure member order is consistent (class attribute, removed during class creation)
1766\item
1767@_generate_@next@_value_@ -- used by the Functional API and by @auto@ to get an appropriate value for an enum member; may be overridden
1768\end{itemize}
1769Note: For standard @Enum@ classes the next value chosen is the last value seen incremented by one.
1770
1771For @Flag@ classes the next value chosen will be the next highest power-of-two, regardless of the last value seen.
1772
1773New in version 3.6: @_missing_@, @_order_@, @_generate_@next@_value_@
1774
1775New in version 3.7: @_ignore_@
1776
1777To help keep Python 2 / Python 3 code in sync an @_order_@ attribute can be provided.
1778It will be checked against the actual order of the enumeration and raise an error if the two do not match:
1779\begin{python}
1780>>> class Color(Enum):
1781...     _order_ = 'RED GREEN BLUE'
1782...     RED = 1
1783...     BLUE = 3
1784...     GREEN = 2
1785...
1786Traceback (most recent call last):
1787...
1788TypeError: member order does not match _order_:
1789  ['RED', 'BLUE', 'GREEN']
1790  ['RED', 'GREEN', 'BLUE']
1791\end{python}
1792Note: In Python 2 code the @_order_@ attribute is necessary as definition order is lost before it can be recorded.
1793
1794\subsection{\lstinline{_Private__names}}
1795
1796Private names are not converted to enum members, but remain normal attributes.
1797
1798Changed in version 3.11.
1799
1800\subsection{\lstinline{Enum} member type}
1801
1802@Enum@ members are instances of their enum class, and are normally accessed as @EnumClass.member@.
1803In certain situations, such as writing custom enum behavior, being able to access one member directly from another is useful, and is supported;
1804however, in order to avoid name clashes between member names and attributes/methods from mixed-in classes, upper-case names are strongly recommended.
1805
1806Changed in version 3.5.
1807
1808\subsection{Creating members that are mixed with other data types}
1809
1810When subclassing other data types, such as @int@ or @str@, with an @Enum@, all values after the = @are@ passed to that data type's constructor. For example:
1811\begin{python}
1812>>> class MyEnum(IntEnum):      # help(int) -> int(x, base=10) -> integer
1813...     example = '11', 16      # so x='11' and base=16
1814...
1815MyEnum.example.value        # and hex(11) is...
181617
1817\end{python}
1818
1819\subsection{\lstinline{Boolean} value of \lstinline{Enum} classes and members}
1820
1821Enum classes that are mixed with non-@Enum@ types (such as @int@, @str@, etc.) are evaluated according to the mixed-in type's rules;
1822otherwise, all members evaluate as @True@.
1823To make your own enum's boolean evaluation depend on the member's value add the following to your class:
1824\begin{python}
1825def __bool__(self):
1826        return bool(self.value)
1827\end{python}
1828Plain @Enum@ classes always evaluate as @True@.
1829
1830\subsection{\lstinline{Enum} classes with methods}
1831
1832If you give your enum subclass extra methods, like the Planet class below, those methods will show up in a dir() of the member, but not of the class:
1833\begin{python}
1834>>> dir(Planet)                         
1835['EARTH', 'JUPITER', 'MARS', 'MERCURY', 'NEPTUNE', 'SATURN', 'URANUS', 'VENUS',
1836 '__class__', '__doc__', '__members__', '__module__']
1837>>> dir(Planet.EARTH)                   
1838['__class__', '__doc__', '__module__', 'mass', 'name', 'radius', 'surface_gravity', 'value']
1839\end{python}
1840
1841\subsection{Combining members of \lstinline{Flag}}
1842
1843Iterating over a combination of @Flag@ members will only return the members that are comprised of a single bit:
1844\begin{python}
1845>>> class Color(Flag):
1846...     RED = auto()
1847...     GREEN = auto()
1848...     BLUE = auto()
1849...     MAGENTA = RED | BLUE
1850...     YELLOW = RED | GREEN
1851...     CYAN = GREEN | BLUE
1852...
1853>>> Color(3)  # named combination
1854<Color.YELLOW: 3>
1855>>> Color(7)      # not named combination
1856<Color.RED|GREEN|BLUE: 7>
1857\end{python}
1858
1859\subsection{\lstinline{Flag} and \lstinline{IntFlag} minutia}
1860
1861Using the following snippet for our examples:
1862\begin{python}
1863>>> class Color(IntFlag):
1864...     BLACK = 0
1865...     RED = 1
1866...     GREEN = 2
1867...     BLUE = 4
1868...     PURPLE = RED | BLUE
1869...     WHITE = RED | GREEN | BLUE
1870...
1871\end{python}
1872the following are true:
1873\begin{itemize}
1874\item
1875single-bit flags are canonical
1876\item
1877multi-bit and zero-bit flags are aliases
1878\item
1879only canonical flags are returned during iteration:
1880\begin{python}
1881>>> list(Color.WHITE)
1882[<Color.RED: 1>, <Color.GREEN: 2>, <Color.BLUE: 4>]
1883\end{python}
1884negating a flag or flag set returns a new flag/flag set with the corresponding positive integer value:
1885\begin{python}
1886>>> Color.BLUE
1887<Color.BLUE: 4>
1888
1889>>> ~Color.BLUE
1890<Color.RED|GREEN: 3>
1891\end{python}
1892\item
1893names of pseudo-flags are constructed from their members' names:
1894\begin{python}
1895>>> (Color.RED | Color.GREEN).name
1896'RED|GREEN'
1897\end{python}
1898\item
1899multi-bit flags, aka aliases, can be returned from operations:
1900\begin{python}
1901>>> Color.RED | Color.BLUE
1902<Color.PURPLE: 5>
1903
1904>>> Color(7)  # or Color(-1)
1905<Color.WHITE: 7>
1906
1907>>> Color(0)
1908<Color.BLACK: 0>
1909\end{python}
1910\item
1911membership / containment checking: zero-valued flags are always considered to be contained:
1912\begin{python}
1913>>> Color.BLACK in Color.WHITE
1914True
1915\end{python}
1916otherwise, only if all bits of one flag are in the other flag will True be returned:
1917\begin{python}
1918>>> Color.PURPLE in Color.WHITE
1919True
1920
1921>>> Color.GREEN in Color.PURPLE
1922False
1923\end{python}
1924\end{itemize}
1925There is a new boundary mechanism that controls how out-of-range / invalid bits are handled: @STRICT@, @CONFORM@, @EJECT@, and @KEEP@:
1926\begin{itemize}
1927\item
1928@STRICT@ --> raises an exception when presented with invalid values
1929\item
1930@CONFORM@ --> discards any invalid bits
1931\item
1932@EJECT@ --> lose Flag status and become a normal int with the given value
1933\item
1934@KEEP@ --> keep the extra bits
1935\begin{itemize}
1936\item
1937keeps Flag status and extra bits
1938\item
1939extra bits do not show up in iteration
1940\item
1941extra bits do show up in repr() and str()
1942\end{itemize}
1943\end{itemize}
1944The default for @Flag@ is @STRICT@, the default for @IntFlag@ is @EJECT@, and the default for @_convert_@ is @KEEP@ (see @ssl.Options@ for an example of when @KEEP@ is needed).
1945
1946\section{How are Enums and Flags different?}
1947
1948Enums have a custom metaclass that affects many aspects of both derived @Enum@ classes and their instances (members).
1949
1950\subsection{Enum Classes}
1951
1952The @EnumType@ metaclass is responsible for providing the @__contains__()@, @__dir__()@, @__iter__()@ and other methods that allow one to do things with an @Enum@ class that fail on a typical class, such as @list(Color)@ or @some_enum_var@ in @Color@.
1953@EnumType@ is responsible for ensuring that various other methods on the final @Enum@ class are correct (such as @__new__()@, @__getnewargs__()@, @__str__()@ and @__repr__()@).
1954
1955\subsection{Flag Classes}
1956
1957Flags have an expanded view of aliasing: to be canonical, the value of a flag needs to be a power-of-two value, and not a duplicate name.
1958So, in addition to the @Enum@ definition of alias, a flag with no value (a.k.a. 0) or with more than one power-of-two value (e.g. 3) is considered an alias.
1959
1960\subsection{Enum Members (aka instances)}
1961
1962The most interesting thing about enum members is that they are singletons.
1963@EnumType@ creates them all while it is creating the enum class itself, and then puts a custom @__new__()@ in place to ensure that no new ones are ever instantiated by returning only the existing member instances.
1964
1965\subsection{Flag Members}
1966
1967Flag members can be iterated over just like the @Flag@ class, and only the canonical members will be returned.
1968For example:
1969\begin{python}
1970>>> list(Color)
1971[<Color.RED: 1>, <Color.GREEN: 2>, <Color.BLUE: 4>]
1972\end{python}
1973(Note that BLACK, PURPLE, and WHITE do not show up.)
1974
1975Inverting a flag member returns the corresponding positive value, rather than a negative value -- for example:
1976\begin{python}
1977>>> ~Color.RED
1978<Color.GREEN|BLUE: 6>
1979\end{python}
1980Flag members have a length corresponding to the number of power-of-two values they contain. For example:
1981\begin{python}
1982>>> len(Color.PURPLE)
19832
1984\end{python}
1985
1986\subsection{Enum Cookbook}
1987
1988While @Enum@, @IntEnum@, @StrEnum@, @Flag@, and @IntFlag@ are expected to cover the majority of use-cases, they cannot cover them all. Here are recipes for some different types of enumerations that can be used directly, or as examples for creating one's own.
1989
1990\subsection{Omitting values}
1991
1992In many use-cases, one doesn't care what the actual value of an enumeration is. There are several ways to define this type of simple enumeration:
1993\begin{itemize}
1994\item
1995use instances of auto for the value
1996\item
1997use instances of object as the value
1998\item
1999use a descriptive string as the value
2000\item
2001use a tuple as the value and a custom @__new__()@ to replace the tuple with an @int@ value
2002\end{itemize}
2003Using any of these methods signifies to the user that these values are not important, and also enables one to add, remove, or reorder members without having to renumber the remaining members.
2004
2005\subsection{Using \lstinline{auto}}
2006
2007Using @auto@ would look like:
2008\begin{python}
2009>>> class Color(Enum):
2010...     RED = auto()
2011...     BLUE = auto()
2012...     GREEN = auto()
2013...
2014>>> Color.GREEN
2015<Color.GREEN: 3>
2016\end{python}
2017
2018\subsection{Using \lstinline{object}}
2019
2020Using @object@ would look like:
2021\begin{python}
2022>>> class Color(Enum):
2023...     RED = object()
2024...     GREEN = object()
2025...     BLUE = object()
2026...
2027>>> Color.GREEN                         
2028<Color.GREEN: <object object at 0x...>>
2029\end{python}
2030This is also a good example of why you might want to write your own @__repr__()@:
2031\begin{python}
2032>>> class Color(Enum):
2033...     RED = object()
2034...     GREEN = object()
2035...     BLUE = object()
2036...     def __repr__(self):
2037...         return "<%s.%s>" % (self.__class__.__name__, self._name_)
2038...
2039>>> Color.GREEN
2040<Color.GREEN>
2041\end{python}
2042
2043\subsection{Using a descriptive string}
2044
2045Using a string as the value would look like:
2046\begin{python}
2047>>> class Color(Enum):
2048...     RED = 'stop'
2049...     GREEN = 'go'
2050...     BLUE = 'too fast!'
2051...
2052>>> Color.GREEN
2053<Color.GREEN: 'go'>
2054\end{python}
2055
2056\subsection{Using a custom \lstinline{__new__()}}
2057
2058Using an auto-numbering @__new__()@ would look like:
2059\begin{python}
2060>>> class AutoNumber(Enum):
2061...     def __new__(cls):
2062...         value = len(cls.__members__) + 1
2063...         obj = object.__new__(cls)
2064...         obj._value_ = value
2065...         return obj
2066...
2067>>> class Color(AutoNumber):
2068...     RED = ()
2069...     GREEN = ()
2070...     BLUE = ()
2071...
2072>>> Color.GREEN
2073<Color.GREEN: 2>
2074\end{python}
2075To make a more general purpose @AutoNumber@, add @*args@ to the signature:
2076\begin{python}
2077>>> class AutoNumber(Enum):
2078...     def __new__(cls, *args):      # this is the only change from above
2079...         value = len(cls.__members__) + 1
2080...         obj = object.__new__(cls)
2081...         obj._value_ = value
2082...         return obj
2083\end{python}
2084Then when you inherit from @AutoNumber@ you can write your own @__init__@ to handle any extra arguments:
2085\begin{python}
2086>>> class Swatch(AutoNumber):
2087...     def __init__(self, pantone='unknown'):
2088...         self.pantone = pantone
2089...     AUBURN = '3497'
2090...     SEA_GREEN = '1246'
2091...     BLEACHED_CORAL = () # New color, no Pantone code yet!
2092...
2093>>> Swatch.SEA_GREEN
2094<Swatch.SEA_GREEN: 2>
2095>>> Swatch.SEA_GREEN.pantone
2096'1246'
2097>>> Swatch.BLEACHED_CORAL.pantone
2098'unknown'
2099\end{python}
2100Note: The @__new__()@ method, if defined, is used during creation of the Enum members;
2101it is then replaced by Enum's @__new__()@ which is used after class creation for lookup of existing members.
2102
2103Warning: Do not call @super().__new__()@, as the lookup-only @__new__@ is the one that is found;
2104instead, use the data type directly -- e.g.:
2105\begin{python}
2106obj = int.__new__(cls, value)
2107\end{python}
2108
2109\subsection{OrderedEnum}
2110
2111An ordered enumeration that is not based on @IntEnum@ and so maintains the normal @Enum@ invariants (such as not being comparable to other enumerations):
2112\begin{python}
2113>>> class OrderedEnum(Enum):
2114...     def __ge__(self, other):
2115...         if self.__class__ is other.__class__:
2116...             return self.value >= other.value
2117...         return NotImplemented
2118...     def __gt__(self, other):
2119...         if self.__class__ is other.__class__:
2120...             return self.value > other.value
2121...         return NotImplemented
2122...     def __le__(self, other):
2123...         if self.__class__ is other.__class__:
2124...             return self.value <= other.value
2125...         return NotImplemented
2126...     def __lt__(self, other):
2127...         if self.__class__ is other.__class__:
2128...             return self.value < other.value
2129...         return NotImplemented
2130...
2131>>> class Grade(OrderedEnum):
2132...     A = 5
2133...     B = 4
2134...     C = 3
2135...     D = 2
2136...     F = 1
2137>>> Grade.C < Grade.A
2138True
2139\end{python}
2140
2141\subsection{DuplicateFreeEnum}
2142
2143Raises an error if a duplicate member value is found instead of creating an alias:
2144\begin{python}
2145>>> class DuplicateFreeEnum(Enum):
2146...     def __init__(self, *args):
2147...         cls = self.__class__
2148...         if any(self.value == e.value for e in cls):
2149...             a = self.name
2150...             e = cls(self.value).name
2151...             raise ValueError(
2152...                 "aliases not allowed in DuplicateFreeEnum:  %r --> %r"
2153...                 % (a, e))
2154>>> class Color(DuplicateFreeEnum):
2155...     RED = 1
2156...     GREEN = 2
2157...     BLUE = 3
2158...     GRENE = 2
2159...
2160Traceback (most recent call last):
2161  ...
2162ValueError: aliases not allowed in DuplicateFreeEnum:  'GRENE' --> 'GREEN'
2163\end{python}
2164Note: This is a useful example for subclassing Enum to add or change other behaviors as well as disallowing aliases.
2165If the only desired change is disallowing aliases, the @unique()@ decorator can be used instead.
2166
2167\subsection{Planet}
2168
2169If @__new__()@ or @__init__()@ is defined, the value of the enum member will be passed to those methods:
2170\begin{figure}
2171\begin{python}
2172from enum import Enum
2173class Planet(Enum):
2174        MERCURY = ( 3.303E23, 2.4397E6 )
2175        VENUS       = ( 4.869E24, 6.0518E6 )
2176        EARTH       = (5.976E24, 6.37814E6)
2177        MARS         = (6.421E23, 3.3972E6)
2178        JUPITER    = (1.9E27,   7.1492E7)
2179        SATURN     = (5.688E26, 6.0268E7)
2180        URANUS    = (8.686E25, 2.5559E7)
2181        NEPTUNE  = (1.024E26, 2.4746E7)
2182        def __init__( self, mass, radius ):
2183                self.mass = mass                # in kilograms
2184                self.radius = radius    # in meters
2185        def surface_gravity( self ):
2186                # universal gravitational constant  (m3 kg-1 s-2)
2187                G = 6.67300E-11
2188                return G * self.mass / (self.radius * self.radius)
2189for p in Planet:
2190        print( f"{p.name}: {p.value}" )
2191
2192MERCURY: (3.303e+23, 2439700.0)
2193VENUS: (4.869e+24, 6051800.0)
2194EARTH: (5.976e+24, 6378140.0)
2195MARS: (6.421e+23, 3397200.0)
2196JUPITER: (1.9e+27, 71492000.0)
2197SATURN: (5.688e+26, 60268000.0)
2198URANUS: (8.686e+25, 25559000.0)
2199NEPTUNE: (1.024e+26, 24746000.0)
2200\end{python}
2201\caption{Python Planet Example}
2202\label{f:PythonPlanetExample}
2203\end{figure}
2204
2205
2206\subsection{TimePeriod}
2207
2208An example to show the @_ignore_@ attribute in use:
2209\begin{python}
2210>>> from datetime import timedelta
2211>>> class Period(timedelta, Enum):
2212...     "different lengths of time"
2213...     _ignore_ = 'Period i'
2214...     Period = vars()
2215...     for i in range(367):
2216...         Period['day_%d' % i] = i
2217...
2218>>> list(Period)[:2]
2219[<Period.day_0: datetime.timedelta(0)>, <Period.day_1: datetime.timedelta(days=1)>]
2220>>> list(Period)[-2:]
2221[<Period.day_365: datetime.timedelta(days=365)>, <Period.day_366: datetime.timedelta(days=366)>]
2222\end{python}
2223
2224\subsection{Subclassing EnumType}
2225
2226While most enum needs can be met by customizing @Enum@ subclasses, either with class decorators or custom functions, @EnumType@ can be subclassed to provide a different Enum experience.
2227
2228
2229\section{OCaml}
2230
2231% https://ocaml.org/docs/basic-data-types#enumerated-data-types
2232% https://dev.realworldocaml.org/runtime-memory-layout.html
2233
2234OCaml provides a variant (union) type, where multiple heterogeneously-typed objects share the same storage.
2235The simplest form of the variant type is a list of nullary datatype constructors, which is like an unscoped, opaque enumeration.
2236
2237OCaml provides a variant (union) type, which is an aggregation of heterogeneous types.
2238A basic variant is a list of nullary datatype constructors, which is like an unscoped, opaque enumeration.
2239\begin{ocaml}
2240type week = Mon | Tue | Wed | Thu | Fri | Sat | Sun
2241let day : week @= Mon@                          $\C{(* bind *)}$
2242let take_class( d : week ) =
2243        @match@ d with                                          $\C{(* matching *)}$
2244                Mon | Wed -> Printf.printf "CS442\n" |
2245                Tue | Thu -> Printf.printf "CS343\n" |
2246                Fri -> Printf.printf "Tutorial\n" |
2247                _ -> Printf.printf "Take a break\n"
2248let _ = take_class( Mon );
2249@CS442@
2250\end{ocaml}
2251The 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.
2252After compilation, variant names are mapped to an opague ascending intergral type discriminants, starting from 0.
2253Here, 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@.
2254The ``@_@'' 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.
2255Since the variant has no type, it has a \newterm{0-arity constructor}, \ie no parameters.
2256Because @week@ is a union of values @Mon@ to @Sun@, it is a \newterm{union type} in turns of the functional-programming paradigm.
2257
2258Each variant can have an associated heterogeneous type, with an n-ary constructor for creating a corresponding value.
2259\begin{ocaml}
2260type colour = Red | Green of @string@ | Blue of @int * float@
2261\end{ocaml}
2262A variant with parameter is stored in a memory block, prefixed by an int tag and has its parameters stores as words in the block.
2263@colour@ is a summation of a nullary type, a unary product type of @string@, and a cross product of @int@ and @float@.
2264(Mathematically, a @Blue@ value is a Cartesian product of the types @int@ type and @float@.)
2265Hence, a variant type creates a sum of product of different types.
2266\begin{ocaml}
2267let c = Red                                                             $\C{(* 0-ary constructor, set tag *)}$
2268let _ = match c with Red -> Printf.printf "Red, "
2269let c = Green( "abc" )                                  $\C{(* 1-ary constructor, set tag *)}$
2270let _ = match c with Green g -> Printf.printf "%s, " g
2271let c = Blue( 1, 1.5 )                                  $\C{(* 2-ary constructor, set tag *)}$
2272let _ = match c with Blue( i, f ) -> Printf.printf "%d %g\n" i f
2273@Red, abc, 1 1.5@
2274\end{ocaml}
2275
2276A variant type can have a recursive definition.
2277\begin{ocaml}
2278type @stringList@ = Empty | Pair of string * @stringList@
2279\end{ocaml}
2280which is a recursive sum of product of types, called an \newterm{algebraic data-type}.
2281A recursive function is often used to pattern match against a recursive variant type.
2282\begin{ocaml}
2283let rec @len_of_string_list@( list : stringList ): int =
2284        match list with
2285                Empty -> 0 |
2286                Pair( _ , r ) -> 1 + @len_of_string_list@ r
2287\end{ocaml}
2288Here, the head of the recursive type is removed and the remainder is processed until the type is empty.
2289Each recursion is counted to obtain the number of elements in the type.
2290
2291Note, the compiler statically guarantees that only the correct kind of type is used in the \lstinline[language=OCaml]{match} statement.
2292However, 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.
2293
2294In summary, an OCaml variant is a singleton value rather than a set of possibly ordered values, and hence, has no notion of enumerabilty.
2295Therefore it is not an enumeration, except for the simple opaque (nullary) case.
2296
2297\begin{comment}
2298Date: Wed, 13 Mar 2024 10:52:34 -0400
2299Subject: Re: OCaml
2300To: "Peter A. Buhr" <pabuhr@uwaterloo.ca>
2301From: Gregor Richards <gregor.richards@uwaterloo.ca>
2302
2303On 3/12/24 18:34, Peter A. Buhr wrote:
2304> Gregor, attached is a section Jiada wrote on OCaml (1-page).
2305> Does it reflect our discussion about functional languages and enumerations?
2306
2307Yeah, I think so. The most important part, i.e., that once they're
2308parameterized they're not really enumerations at all, is covered clearly
2309enough.
2310
2311A couple quibbles:
2312
2313<<a list of untyped tags>>
2314
2315This is true, but leaking implementation details. These are nullary datatype
2316constructors. Indeed, you later talk about "tagged variants", which are really
2317just parameterized variants, using the term "tag" differently, confusing the
2318term "tag" further.
2319
2320<<Because week is a summation of values Mon to Sun, it is a sum type in
2321turns of the functional-programming paradigm>>
2322
2323It is a *union* of values and is a *union* type.
2324
2325With valediction,
2326  - Gregor Richards
2327
2328
2329Date: Thu, 14 Mar 2024 21:45:52 -0400
2330Subject: Re: OCaml "enums" do come with ordering
2331To: "Peter A. Buhr" <pabuhr@uwaterloo.ca>
2332From: Gregor Richards <gregor.richards@uwaterloo.ca>
2333
2334On 3/14/24 21:30, Peter A. Buhr wrote:
2335> I've marked 3 places with your name to shows places with enum ordering.
2336>
2337> type week = Mon | Tue | Wed | Thu | Fri | Sat | Sun
2338> let day : week = Mon
2339> let take_class( d : week ) =
2340>       if d <= Fri then                                (* Gregor *)
2341>               Printf.printf "week\n"
2342>       else if d >= Sat then                   (* Gregor *)
2343>               Printf.printf "weekend\n";
2344>       match d with
2345>               Mon | Wed -> Printf.printf "CS442\n" |
2346>               Tue | Thu -> Printf.printf "CS343\n" |
2347>               Fri -> Printf.printf "Tutorial\n" |
2348>               _ -> Printf.printf "Take a break\n"
2349>
2350> let _ = take_class( Mon ); take_class( Sat );
2351>
2352> type colour = Red | Green of string | Blue of int * float
2353> let c = Red
2354> let _ = match c with Red -> Printf.printf "Red, "
2355> let c = Green( "abc" )
2356> let _ = match c with Green g -> Printf.printf "%s, " g
2357> let c = Blue( 1, 1.5 )
2358> let _ = match c with Blue( i, f ) -> Printf.printf "%d %g\n" i f
2359>
2360> let check_colour(c: colour): string =
2361>       if c < Green( "xyz" ) then              (* Gregor *)
2362>               Printf.printf "green\n";
2363>       match c with
2364>               Red -> "Red" |
2365>               Green g -> g |
2366>               Blue(i, f) -> string_of_int i ^ string_of_float f
2367> let _ = check_colour( Red ); check_colour( Green( "xyz" ) );
2368>
2369> type stringList = Empty | Pair of string * stringList
2370> let rec len_of_string_list(l: stringList): int =
2371>       match l with
2372>               Empty -> 0 |
2373>               Pair(_ , r) -> 1 + len_of_string_list r
2374>
2375> let _ = for i = 1 to 10 do
2376>       Printf.printf "%d, " i
2377> done
2378>
2379> (* Local Variables: *)
2380> (* tab-width: 4 *)
2381> (* compile-command: "ocaml test.ml" *)
2382> (* End: *)
2383
2384My functional-language familiarity is far more with Haskell than OCaml.  I
2385mostly view OCaml through a lens of "it's Haskell but with cheating".  Haskell
2386"enums" (ADTs) aren't ordered unless you specifically and manually put them in
2387the Ord typeclass by defining the comparators.  Apparently, OCaml has some
2388other rule, which I would guess is something like "sort by tag then by order of
2389parameter". Having a default behavior for comparators is *bizarre*; my guess
2390would be that it gained this behavior in its flirtation with object
2391orientation, but that's just a guess (and irrelevant).
2392
2393This gives a total order, but not enumerability (which would still be
2394effectively impossible or even meaningless since enums are just a special case
2395of ADTs).
2396
2397With valediction,
2398  - Gregor Richards
2399
2400Date: Wed, 20 Mar 2024 18:16:44 -0400
2401Subject: Re:
2402To: "Peter A. Buhr" <pabuhr@uwaterloo.ca>
2403From: Gregor Richards <gregor.richards@uwaterloo.ca>
2404
2405
2406On 3/20/24 17:26, Peter A. Buhr wrote:
2407> Gregor, everyone at this end would like a definition of "enumerability". Can
2408> you formulate one?
2409
2410According to the OED (emphasis added to the meaning I'm after):
2411
2412enumerate (verb, transitive). To count, ascertain the number of; **more
2413usually, to mention (a number of things or persons) separately, as if for the
2414purpose of counting**; to specify as in a list or catalogue.
2415
2416With C enums, if you know the lowest and highest value, you can simply loop
2417over them in a for loop (this is, of course, why so many enums come with an
2418ENUM_WHATEVER_LAST value). But, I would be hesitant to use the word "loop" to
2419describe enumerability, since in functional languages, you would recurse for
2420such a purpose.
2421
2422In Haskell, in order to do something with every member of an "enumeration", you
2423would have to explicitly list them all. The type system will help a bit since
2424it knows if you haven't listed them all, but you would have to statically have
2425every element in the enumeration.  If somebody added new elements to the
2426enumeration later, your code to enumerate over them would no longer work
2427correctly, because you can't simply say "for each member of this enumeration do
2428X". In Haskell that's because there aren't actually enumerations; what they use
2429as enumerations are a degenerate form of algebraic datatypes, and ADTs are
2430certainly not enumerable. In OCaml, you've demonstrated that they impose
2431comparability, but I would still assume that you can't make a loop over every
2432member of an enumeration. (But, who knows!)
2433
2434Since that's literally what "enumerate" means, it seems like a rather important
2435property for enumerations to have ;)
2436
2437With valediction,
2438  - Gregor Richards
2439
2440
2441From: Andrew James Beach <ajbeach@uwaterloo.ca>
2442To: Gregor Richards <gregor.richards@uwaterloo.ca>, Peter Buhr <pabuhr@uwaterloo.ca>
2443CC: Michael Leslie Brooks <mlbrooks@uwaterloo.ca>, Fangren Yu <f37yu@uwaterloo.ca>,
2444    Jiada Liang <j82liang@uwaterloo.ca>
2445Subject: Re: Re:
2446Date: Thu, 21 Mar 2024 14:26:36 +0000
2447
2448Does this mean that not all enum declarations in C create enumerations? If you
2449declare an enumeration like:
2450
2451enum Example {
2452    Label,
2453    Name = 10,
2454    Tag = 3,
2455};
2456
2457I don't think there is any way to enumerate (iterate, loop, recurse) over these
2458values without listing all of them.
2459
2460
2461Date: Thu, 21 Mar 2024 10:31:49 -0400
2462Subject: Re:
2463To: Andrew James Beach <ajbeach@uwaterloo.ca>, Peter Buhr <pabuhr@uwaterloo.ca>
2464CC: Michael Leslie Brooks <mlbrooks@uwaterloo.ca>, Fangren Yu <f37yu@uwaterloo.ca>,
2465    Jiada Liang <j82liang@uwaterloo.ca>
2466From: Gregor Richards <gregor.richards@uwaterloo.ca>
2467
2468I consider this conclusion reasonable. C enums can be nothing more than const
2469ints, and if used in that way, I personally wouldn't consider them as
2470enumerations in any meaningful sense, particularly since the type checker
2471essentially does nothing for you there. Then they're a way of writing consts
2472repeatedly with some textual indicator that these definitions are related; more
2473namespace, less enum.
2474
2475When somebody writes bitfield members as an enum, is that *really* an
2476enumeration, or just a use of the syntax for enums to keep related definitions
2477together?
2478
2479With valediction,
2480  - Gregor Richards
2481
2482
2483Date: Tue, 16 Apr 2024 11:04:51 -0400
2484Subject: Re: C unnamed enumeration
2485To: "Peter A. Buhr" <pabuhr@uwaterloo.ca>
2486CC: <ajbeach@uwaterloo.ca>, <j82liang@uwaterloo.ca>, <mlbrooks@uwaterloo.ca>,
2487        <f37yu@uwaterloo.ca>
2488From: Gregor Richards <gregor.richards@uwaterloo.ca>
2489
2490On 4/16/24 09:55, Peter A. Buhr wrote:
2491> So what is a variant? Is it a set of tag names, which might be a union or is it
2492> a union, which might have tag names?
2493
2494Your tagless variant bears no resemblance to variants in any functional
2495programming language. A variant is a tag AND a union. You might not need to put
2496anything in the union, in which case it's a pointless union, but the named tag
2497is absolutely mandatory. That's the thing that varies.
2498
2499I was unaware of std::variant. As far as functional languages are concerned,
2500std::variant IS NOT A VARIANT. Perhaps it would be best to use the term ADT for
2501the functional language concept, because that term has no other meanings.
2502
2503An ADT cannot not have a named tag. That's meaningless. The tag is the data
2504constructor, which is the thing you actually define when you define an ADT. It
2505is strictly the union that's optional.
2506
2507With valediction,
2508  - Gregor Richards
2509\end{comment}
2510
2511
2512\section{Comparison}
2513
2514\VRef[Table]{t:FeatureLanguageComparison} shows a comparison of enumeration features and programming languages.
2515The features are high level and may not capture nuances within a particular language
2516The @const@ feature is simple macros substitution and not a typed enumeration.
2517
2518\begin{table}
2519\caption{Enumeration Feature / Language Comparison}
2520\label{t:FeatureLanguageComparison}
2521\small
2522\setlength{\tabcolsep}{3pt}
2523\newcommand{\CM}{\checkmark}
2524\begin{tabular}{r|c|c|c|c|c|c|c|c|c|c|c|c|c}
2525                                &Pascal & Ada   &\Csharp& OCaml & Java  &Modula-3&Golang& Rust  & Swift & Python& C             & \CC   & \CFA  \\
2526\hline
2527@const@                 & \CM   &               &               &               &               &               & \CM   &               &               &               &               & \CM   &               \\
2528\hline
2529\hline
2530opaque                  &               &               &               &               &               &               &               &               &               &               &               &               & \CM   \\
2531\hline
2532typed                   &               &               &               &               &               &               &               &               &               &               & @int@ & integral      & @T@   \\
2533\hline
2534safe                    &               &               &               &               &               &               &               &               &               &               &               & \CM   & \CM   \\
2535\hline
2536ordered                 &               &               &               &               &               &               &               &               &               &               & \CM   & \CM   & \CM   \\
2537\hline
2538dup. values             &               &               &               &               &               &               &               &               &               & alias & \CM   & \CM   & \CM   \\
2539\hline
2540setable                 &               &               &               &               &               &               &               &               &               &               & \CM   & \CM   & \CM   \\
2541\hline
2542auto-init               &               &               &               &               &               &               &               &               &               &               & \CM   & \CM   & \CM   \\
2543\hline
2544(un)scoped              &               &               &               &               &               &               &               &               &               &               & U             & U/S   & U/S   \\
2545\hline
2546overload                &               & \CM   &               &               &               &               &               &               &               &               &               & \CM   & \CM   \\
2547\hline
2548switch                  &               &               &               &               &               &               &               &               &               &               & \CM   & \CM   & \CM   \\
2549\hline
2550loop                    &               &               &               &               &               &               &               &               &               &               &               &               & \CM   \\
2551\hline
2552array/subscript &               &               &               &               &               &               &               &               &               &               & \CM   &               & \CM   \\
2553\hline
2554subtype                 &               &               &               &               &               &               &               &               &               &               &               &               & \CM   \\
2555\hline
2556inheritance             &               &               &               &               &               &               &               &               &               &               &               &               & \CM   \\
2557\end{tabular}
2558\end{table}
Note: See TracBrowser for help on using the repository browser.