Enums are a data type composed of a fixed set of inner data types, known as variants. A common use is to define an "optional" or "maybe" type that will either have a "something" of X, or a nothing.
In addition to providing an Option as part of predefined data types, it is
also possible to create new enums.
By default, an enum is flat. The variants of a flat enum are placed into the module's namespace.
enum FlatRgb
{
Red,
Green,
Blue,
define is_blue: Boolean
{
match self: {
case Blue: return true
else: return false
}
}
}
var v = Blue
print(v.is_blue()) # true
print(v) # Blue
The alternative is a scoped enum. Variants of a scoped enum are kept within the enum.
scoped enum ScopedRgb
{
Red,
Green,
Blue,
define is_blue: Boolean
{
match self: {
case ScopedRgb.Blue:
return true
case ScopedRgb.Red,
ScopedRgb.Green:
return false
}
}
}
var v = ScopedRgb.Blue
print(v.is_blue()) # true
print(v) # ScopedRgb.Blue
In both cases, variants are sealed inside of the enum they are declared in. It
is not possible to, for example, declare a variable of type Some.
The above examples have a comma at the end of the last variant declared in the enum. However, the last variant is not required to have a comma at the end:
scoped enum CommaExample
{
One,
Two
}
Similar to classes, enum allow declaration of methods. Enum methods do not
support any qualifiers. All enum methods act as they are public, and receive
self as a hidden first parameter. Enum methods do not allow a scope because
they cannot inherit or be inherited from.
enum Rgb
{
Red,
Green,
Blue,
define is_blue: Boolean
{
with self as Blue: {
return true
else:
return false
}
}
}
var v = Blue
print(v.is_blue()) # true
# An alternative way of calling the above.
print(Rgb.is_blue(v)) # true
print(v) # Blue
It is possible for a variant to take arguments. Those arguments can include the
enum itself. Variants that take values can be called like a Function, but they
are not Function values.
Variants support all argument types, except optional arguments.
enum Tree
{
Leaf(Integer),
Branch(Tree, Tree),
define walk: Integer
{
match self: {
case Leaf(value):
return value
case Branch(left, right):
return left.walk() + right.walk()
}
}
}
var tree =
Branch(
Branch(
Leaf(10),
Leaf(20)
),
Leaf(30)
)
print(tree.walk()) # 60
Variants can also have variable arguments.
enum Tree
{
Leaf(Integer),
Branch(Tree...),
define walk: Integer
{
match self: {
case Leaf(value):
return value
case Branch(targets):
var total = 0
for i in 0...targets.size() - 1: {
total += targets[i].walk()
}
return total
}
}
}
var tree =
Branch(
Branch(
Leaf(10)
),
Leaf(20),
Leaf(30),
Leaf(40)
)
print(tree.walk()) # 100
Keyword arguments are also supported.
enum Color
{
Blue,
Green,
Red,
RGB(:red Integer, :green Integer, :blue Integer),
define to_i: Integer
{
match self: {
case Blue:
return 0x0000ff
case Green:
return 0x00ff00
case Red:
return 0xff0000
case RGB(r, g, b):
return (r << 16) +
(g << 8) +
b
}
}
}
var v = RGB(:red 0xff,
:blue 0,
:green 0)
print(Green.to_i()) # 65280
print(v.to_i()) # 16711680
Variants support call piping.
{
var v = Option.unwrap(Some(1))
print(v) # 1
}
{
var v = 1 |> Some |> Option.unwrap
print(v) # 1
}
In the above examples, variants do not themselves have any underlying value. The
None of an Option is a None. If an enum is declared as inheriting from
Integer, it is created as a value enum. The variants of a value enum are
called value variants.
Here's that color example again:
enum Color < Integer {
Red, # No value given, defaults to 0
Green = 12,
Blue, # 13 (the prior value + 1)
define is_green: Boolean
{
match self: {
case Green:
return true
else:
return false
}
}
}
print(Red) # 0
print(Green) # 12
print(Blue) # 13
print(Blue.is_green()) # false
# Math is allowed, but the result decays to Integer.
var some_int: Integer = Green + Blue
print(some_int) # 25
If the first value isn't given, it defaults to 0. All others default to the prior value + 1. Value variants must be initialized with a literal value, and they must be unique to each other. Each variant must be empty as well.
Value enums are declared using < Integer (no other class is alloweed).
Internally, value enums are implemented as Integer values, instead of as a
separate class.
import introspect
enum Color < Integer {
Red,
Green,
Blue
}
print(Red |> introspect.class_name) # Integer
Because value enums inherit from Integer, they are allowed to use Integer
methods.
enum Color < Integer {
Red,
Green,
Blue
}
print(Green.to_hex()) # 0x1