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
Value enums are declared using < Integer
to indicate that they are effectively
Integer
values. Value enums cannot inherit from any other class.
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.