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:
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.