Enum and Match

Enums (short for enumerations) and match expressions are powerful features in AIScript that work together to provide type-safe pattern matching. These features are inspired by Rust's enum and match system, offering an elegant way to handle variants and control flow.

Enums

An enum is a type that can hold one of several possible variants, each with its own associated data. Enums are declared using the enum keyword:

enum Color {
    Red,
    Green,
    Blue,
}

In this example, Color can be one of three variants: Red, Green, Blue.

WARNING

AIScript does not support algebra data types like Rust, and has no plan to support it. So following enum type is not supported.

enum Color {
    Red,
    Green,
    Blue,
    RGB(int, int, int),
    Hex(str),
}

Create Enum Variant

You can create enum variant using the double colon (::) syntax:

let red = Color::Red;

Enum with Values

Enum variants can have associated values:

enum Color {
    Red = "#FF0000",
    Green = "#00FF00",
    Blue = "#0000FF",
}

let red = Color::Red;
print(red); // Color::Red(#FF0000)

Enum's number value will be auto increment if you don't specify the value.

enum Status {
    Draft = 1,
    Pending,
    Active = 10, // you can change the value of enum variant
    Finished,
}

print([Status::Draft]); // 1
print([Status::Pending]); // 2
print([Status::Active]); // 10
print([Status::Finished]); // 11

However, some caveats should be noted:

  • you cannot mix different type value
  • for string value, no default value will be set, you must specify all values for all variants
enum Status {
    Draft = "draft",
    Pending, // [line 3] Error at 'Pending': Must specify value for non-integer enum variants
    Active = 10,
    Finished,
}

Evaluate Enum Values

You can use [] to evaluate the value of enum variant.

let value = [Color::Red];
print(value); // #FF0000

let color = Color::Blue;
print([color]); // #0000FF
TIP

The [Enum::Variant] syntax is similar to Python Enum's .value.

from enum import Enum

class Color(Enum):
    RED = "#FF0000"
    Green = "#00FF00",
    Blue = "#0000FF",

print(Color.RED.value) # "#FF0000"

Enum Methods

enum supports methods like class.

enum Color {
    Red = "#FF0000",
    Green = "#00FF00",
    Blue = "#0000FF",

    fn name(self) -> str {
        match self {
            Color::Red => "red",
            Color::Green => "green",
            Color::Blue => "blue",
        }
    }

    fn static_method() {
        print("static method of Color enum");
    }
}

print(Color::Blue.name()); // blue
Color.static_method(); // static method of Color enum

Match Expression

The match expression allows you to compare a value against a series of patterns and execute code based on which pattern matches:

fn match_string(s: str) {
    return match s {
        "hello" | "hi" => "greeting",
        "bye" => "farewell",
        s if s.starts_with("#") => "sharp",
        x if x.starts_with("@") => "at",
        x if x in ["luck", "magic"] => "lucky",
        s if s == "ai" => "ai",
        _ => "unknown",
    };
}

match_string("hello"); // greeting
match_string("bye"); // farewell
match_string("#id"); // sharp
match_string("@aiscript"); // at
match_string("luck"); // lucky
match_string("ai"); // ai
match_string("unknown"); // unknown
  • Use | to match multiple values
  • Match support if guards

Pattern Matching on Number Types

Match works on other data types too, not just enums:

let value = 42;

match value {
    0 => print("Zero"),
    1 | 2 | 3 => print("Small number"),
    n if n < 10 => print("Single digit:", n),
    n if n >= 10 and n < 100 => print("Double digits:", n),
    _ => print("Large number"),
};
// Double digits: 42

Match with Variable Binding Not supported yet

You can bind values to variables in match patterns:

let point = [10, 20];

match point {
    [0, 0] => print("At origin"),
    [0, y] => print(f"On y-axis at y={y}"),
    [x, 0] => print(f"On x-axis at x={x}"),
    [x, y] => print(f"At position ({x}, {y})"),
};

Match with Guards

Add conditional guards to match arms for more specific matching:

let num = 15;

match num {
    n if n % 15 == 0 => print("FizzBuzz"),
    n if n % 3 == 0 => print("Fizz"),
    n if n % 5 == 0 => print("Buzz"),
    n => print(f"{n}"),
};

Advanced Pattern Matching

Range Patterns

Match values within ranges:

let grade = 85;

let letter = match grade {
    n if n >= 90 and n <= 100 => "A",
    n if n >= 80 and n < 90 => "B",
    n if n >= 70 and n < 80 => "C",
    n if n >= 60 and n < 70 => "D",
    n if n >= 0 and n < 60 => "F",
    _ => "Invalid grade",
};

print("Grade: ", letter); // Grade: B

fn match_range(n) {
    return match n {
        0..60 => "F",
        60..70 => "D",
        70..80 => "C",
        80..90 => "B",
        90..=100 => "A",
        _ => "Invalid grade",
    };
}

print(match_range(92)); // A
print(match_range(101)); // Invalid grade

Or Patterns

Use the pipe (|) symbol to match multiple patterns in a single arm:

let day = "Saturday";

let type = match day {
    "Monday" | "Tuesday" | "Wednesday" | "Thursday" | "Friday" => "Weekday",
    "Saturday" | "Sunday" => "Weekend",
    _ => "Invalid day",
};

print(day, " is a ", type); // Saturday is a Weekend

Match with Error Handling

Use match with error enums for elegant error handling:

let result = fetch_data("example.com") |err| {
    match err {
        NetworkError!::ConnectionFailed => "Failed to connect",
        NetworkError!::Timeout => "Connection timed out",
        NetworkError!::ServerError => "Server error",
    }
};

print(result);

Practical Examples

State Machine

enum TrafficLight {
    Red,
    Yellow,
    Green,
}

fn next_state(current: TrafficLight) -> TrafficLight {
    match current {
        TrafficLight::Red => TrafficLight::Green,
        TrafficLight::Yellow => TrafficLight::Red,
        TrafficLight::Green => TrafficLight::Yellow,
    }
}

let light = TrafficLight::Red;

let next_light = match next_state(light) {
    TrafficLight::Red => "Stop",
    TrafficLight::Yellow => "Caution",
    TrafficLight::Green => "Go",
};
print(next_light); // "Go"

Enums and match expressions in AIScript provide a powerful way to model domain concepts, handle errors, and create clean, maintainable code. By leveraging pattern matching, you can write code that is both safer and more expressive than traditional if-else chains.