Enum

This section describes the enum type in Cangjie. The enum type provides a way to define a type by enumerating all possible values of the type.

Many programming languages feature an enum type (sometimes referred to as an enumerated type), but the usage and expressive capabilities of the enum type vary across languages. In Cangjie, the enum type may be understood as an algebraic data type in a functional programming language.

The following describes how to define and use enum types, how to use pattern matching to perform different operations when an enum is set to different values, and how to use a common enum type named Option to indicate that an instance of a type may or may not contain a value.

Defining an Enum

An enum type is defined by listing all possible values. These values are called the constructors of the enum type.

enum RGBColor {
    | Red | Green | Blue
}

An enum definition starts with the keyword enum, followed by a type name, and then an enum body in braces. The enum body contains several constructors which are separated by | (the | before the first constructor is optional). In the preceding example, an enum type named RGBColor is defined. It has three constructors: Red, Green, and Blue, representing the colors in the RGB color model.

The constructors in the above enum can also carry one or more parameters, making them parameterized constructors. For example, you can set a parameter of the UInt8 type for Red, Green, and Blue to represent the brightness level of each color.

enum RGBColor {
    | Red(UInt8) | Green(UInt8) | Blue(UInt8)
}

Cangjie allows multiple constructors with the same name within the same enum, provided the number of parameters is different (considering that a constructor with no parameters has a parameter count of 0). For example:

enum RGBColor {
    | Red | Green | Blue
    | Red(UInt8) | Green(UInt8) | Blue(UInt8)
}

enum definitions can be recursive. In the following example, enum is used to define a type of expressions (Expr). An expression can take only three forms: a single number Num (with a parameter of the Int64 type), an addition expression Add (with two parameters of the Expr type), or a subtraction expression Sub (with two parameters of the Expr type). The Add and Sub constructors recursively use Expr itself in their parameters.

enum Expr {
    | Num(Int64)
    | Add(Expr, Expr)
    | Sub(Expr, Expr)
}

In addition, a series of member functions, operator functions (for details, see Operator Overloading), and member properties (for details, see Properties) can be defined in the enum body. However, the names of constructors, member functions, and member properties must be unique. For example, the following example defines a function named printType in RGBColor, which outputs the string RGBColor:

enum RGBColor {
    | Red | Green | Blue

    public static func printType() {
        print("RGBColor")
    }
}

Note:

  • enum definitions can only appear at the top level of a source file.
  • When there is a mutually recursive relationship between enum and struct types, and the enum type is used as a type parameter of Option, compilation errors may occur.

Using an Enum

After defining an enum type, instances of it can be created through one of its constructors, either directly or qualifying it with the type name as TypeName.Constructor. Unlike struct and class types, an enum type name cannot be used as a constructor. For parameterized constructors, all the parameters must be provided.

In the following example, RGBColor defines three constructors: two without parameters (Red and Green) and one with a parameter (Blue(UInt8)). main defines three variables r, g, and b of the RGBColor type. The value of r is initialized using RGBColor.Red, the value of g is initialized using Green, and the value of b is initialized using Blue(100).

enum RGBColor {
    | Red | Green | Blue(UInt8)
}

main() {
    let r = RGBColor.Red
    let g = Green
    let b = Blue(100)
}

When the type name is omitted, the names of enum constructors may conflict with type names, variable names, or function names. In this case, you must add the enum type name to specify the enum constructor. Otherwise, only type, variable, and function definitions with the same name are used.

In the following example, only the constructor Blue(UInt8) can be used without the type name. Red and Green(UInt8) cannot be directly used due to name conflict and must be prefixed with the type name RGBColor.

let Red = 1

func Green(g: UInt8) {
    return g
}

enum RGBColor {
    | Red | Green(UInt8) | Blue(UInt8)
}

let r1 = Red                 // Will choose 'let Red'
let r2 = RGBColor.Red        // Ok: constructed by enum type name

let g1 = Green(100)          // Will choose 'func Green'
let g2 = RGBColor.Green(100) // Ok: constructed by enum type name

let b = Blue(100)            // Ok: can be uniquely identified as an enum constructor

In the following example, only the constructor Blue cannot be directly used due to name conflicts and must be prefixed with the type name RGBColor.

class Blue {}

enum RGBColor {
    | Red | Green(UInt8) | Blue(UInt8)
}

let r = Red                 // Ok: constructed by enum type name

let g = Green(100)          // Ok: constructed by enum type name

let b = Blue(100)           // Will choose constructor of 'class Blue' and report an error