Enumeration types

Enumeration types define a new type whose values consist of the ones specified. The values are ordered. Example:

  1. type
  2. Direction = enum
  3. north, east, south, west

Now the following holds:

  1. ord(north) == 0
  2. ord(east) == 1
  3. ord(south) == 2
  4. ord(west) == 3
  5. # Also allowed:
  6. ord(Direction.west) == 3

The implied order is: north < east < south < west. The comparison operators can be used with enumeration types. Instead of north etc., the enum value can also be qualified with the enum type that it resides in, Direction.north.

For better interfacing to other programming languages, the fields of enum types can be assigned an explicit ordinal value. However, the ordinal values have to be in ascending order. A field whose ordinal value is not explicitly given is assigned the value of the previous field + 1.

An explicit ordered enum can have holes:

  1. type
  2. TokenType = enum
  3. a = 2, b = 4, c = 89 # holes are valid

However, it is then not ordinal anymore, so it is impossible to use these enums as an index type for arrays. The procedures inc, dec, succ and pred are not available for them either.

The compiler supports the built-in stringify operator $ for enumerations. The stringify’s result can be controlled by explicitly giving the string values to use:

  1. type
  2. MyEnum = enum
  3. valueA = (0, "my value A"),
  4. valueB = "value B",
  5. valueC = 2,
  6. valueD = (3, "abc")

As can be seen from the example, it is possible to both specify a field’s ordinal value and its string value by using a tuple. It is also possible to only specify one of them.

An enum can be marked with the pure pragma so that its fields are added to a special module-specific hidden scope that is only queried as the last attempt. Only non-ambiguous symbols are added to this scope. But one can always access these via type qualification written as MyEnum.value:

  1. type
  2. MyEnum {.pure.} = enum
  3. valueA, valueB, valueC, valueD, amb
  4. OtherEnum {.pure.} = enum
  5. valueX, valueY, valueZ, amb
  6. echo valueA # MyEnum.valueA
  7. echo amb # Error: Unclear whether it's MyEnum.amb or OtherEnum.amb
  8. echo MyEnum.amb # OK.

Enum value names are overloadable, much like routines. If both of the enums T and U have a member named foo, then the identifier foo corresponds to a choice between T.foo and U.foo. During overload resolution, the correct type of foo is decided from the context. If the type of foo is ambiguous, a static error will be produced.

  1. type
  2. E1 = enum
  3. value1,
  4. value2
  5. E2 = enum
  6. value1,
  7. value2 = 4
  8. const
  9. Lookuptable = [
  10. E1.value1: "1",
  11. # no need to qualify value2, known to be E1.value2
  12. value2: "2"
  13. ]
  14. proc p(e: E1) =
  15. # disambiguation in 'case' statements:
  16. case e
  17. of value1: echo "A"
  18. of value2: echo "B"
  19. p value2

In some cases, ambiguity of enums is resolved depending on the relation between the current scope and the scope the enums were defined in.

  1. # a.nim
  2. type Foo* = enum abc
  3. # b.nim
  4. import a
  5. type Bar = enum abc
  6. echo abc is Bar # true
  7. block:
  8. type Baz = enum abc
  9. echo abc is Baz # true

To implement bit fields with enums see Bit fields.