source: doc/theses/jiada_liang_MMath/relatedwork.tex@ 4da9142

Last change on this file since 4da9142 was 4da9142, checked in by Peter A. Buhr <pabuhr@…>, 19 months ago

more proofreading on enumerations

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