Lulu — Enums
Introduction
Enums specify that a variable can only take on one of a limited number of choices.
For example, in C++ you might define the Suit
for a card game as:
enum class Suit = { Clubs, Diamonds, Hearts, Spades };
This declaration defines Suit
as an enumerated type, an enumeration or enum for short, whose instances can only be one of the four allowed choices.
The named choices in an enumeration are often called the enumerators.
In effect, an enumeration introduces some named constants that are available for variable assignment. Of course, we need to be able to compare enumerators. Other than that, the memory layout and implementation can be opaque.
Early programming languages did not have enumerated types as part of the language. You would instead define some integer constants for the Suit
choices (probably in a Suit.h
header file):
#define CLUBS 0
#define DIAMONDS 1
#define HEARTS 2
#define SPADES 3
Then later, have code that includes that header and has statements like if(suit == DIAMONDS) { ... }
.
However, using magic numbers like this makes source code hard to read and prone to errors.
It is evident that, even for our simple example, using the enum Suit
type makes the code much more self-documenting. For this reason, most modern programming languages support some form of enumerated type as a core concept in the language itself.
Moreover, languages usually have specific constructs designed to work naturally with their idea of what an enumerated type should be — the switch
statement in C and C++, or various match
statements in other languages. Those constructs may be more generally applicable, but their core design has the language’s enumerated type as their core raison d’etre.
Programming languages have implemented the idea of an enum in various ways.
They can be straightforward, like in C and C++ where every enumerator is an integer. Other languages like Rust and Swift allow enumerators to own arbitrary associated data and methods. There will still be an underlying ordinal somewhere (perhaps a compiler construct) that defines an ordering of the enumerators.
Lua does not have a native enumerated type or anything like a multi-armed switch
or match
block.
It only has one complex native type, the all-powerful table
. So you might have code like:
Suit = { Clubs = 1, Diamonds = 2, Hearts = 3, Spades = 4 }
local function is_black(s)
return s == Suit.Clubs or s == Suit.Spades
end
local s = Suit.Diamonds
print("Suit", s, " is black:", is_black(s))
This outputs Suit 2 is black: false
.
This works but isn’t suitable for anything except the trivial scripts. The Suit
table is not constant and can easily be changed by mistake.
The Enum
Class
The lulu.Enum
module provides a specific class for this common data structure.
Our enums have constant enumerators that cannot easily be changed.
If you have imported the lulu.Enum
module as:
local Enum = require 'lulu.Enum'
Then, you have access to the following methods:
Method | Brief Description |
---|---|
Enum:new |
Create an Enum instance in various ways. |
ENUM |
An alternative syntax for creating Enum instances with a preset type. |
Enum:set_type |
Set the type of an Enum to a string. The default type is Enum . |
Enum:type |
Get the type of an Enum . The default type is Enum . |
Enum:is_instance |
Check if an object is an instance of Enum . |
Enum:count |
Get the number of enumerators in an Enum . |
Enum:iter |
Returns an iterator that traverses an Enums enumerators in sorted order. |
Enum:mt |
Returns the metatable shared by all the enumerators of an Enum . |
Enum:inline |
Returns a one-line string representation of an Enum . |
Enum:pretty |
Returns a pretty-printed string representation of an Enum . |
Enum:__tostring |
A metamethod that connects Lua’s tostring and print functions to the Enum:inline method. |
Enum:add_enumerator |
Add an enumerator to an Enum . |
The Enumerator
Class
The Enumerator
class is used to represent the enumerators of an Enum
and has the following methods:
Method | Brief Description |
---|---|
Enumerator:tostring |
Returns the name of the enumerator. Enumerator names are unique within an Enum . |
Enumerator:tonumber |
Returns the ordinal value of the enumerator. By default, ordinals are consecutive integers starting at 1. However, they can be set to other values and need not be unique. |
Enumerator:is_a |
Returns true if the argument is an Enumerator that belongs to the argument Enum . |
Enumerator:__eq |
Compares enumerators by their ordinal values. |
Enumerator:__lt |
Compares enumerators by their ordinal values. |
Enumerator:__le |
Compares enumerators by their ordinal values. |
Enumerator:__index |
This metamethod allows direct access to any associated data for the enumerator. |
Enumerator:__newindex |
This metamethod prevents attempts to set values in an enumerator. |
Enumerator:__tostring |
A metamethod that connects Lua’s tostring and print functions to the Enumerator:tostring method. |
Enumerator instances are immutable and generated by the Enum class. They are not usually created directly. |
You access enumerators through an Enum
instance.
For example:
local Suit = Enum{ 'Clubs', 'Diamonds', 'Hearts', 'Spades' }
local s = Suit.Clubs
("Suit %s has ordinal %d", s, s:tonumber()) putln
This outputs Suit Clubs has ordinal 1
.