Pattern Matching

The when keyword replaces the transitional switch statement found in many other languages. It provides a more powerful and expressive way to handle conditional logic and pattern matching in Conjure.

Basic Usage

When using when as a statement, it executes different blocks of code based on the value of an expression. Each case is defined using the case keyword followed by the value to match and a colon.

enum Color { Red, Green, Blue }

var myColor = Color.Red

when myColor {
	case .Red: io.println("myColor is red")
	case .Green: io.println("myColor is green")
	case .Blue: io.println("myColor is blue")
}

When matching enum values, you can use the shorthand .Value syntax since the type is already known from the expression being matched.

Else Case

Use else to handle any values not explicitly matched. For non-exhaustive types (like integers or strings), an else case is required.

when dayOfWeek {
	case 0: io.println("Sunday")
	case 6: io.println("Saturday")
	else: io.println("Weekday")
}

Multi-Statement Cases

Cases can contain multiple statements. Use a block or place statements on subsequent lines.

when color {
	case .Red:
		io.println("Color is red")
		io.println("This is a warm color")
	case .Blue:
		io.println("Color is blue")
		io.println("This is a cool color")
	else: io.println("Some other color")
}

Fallthrough

By default, each case in a when statement is independent. After executing a case’s body, control transfers out of the when block. To explicitly continue execution into the next case, use the fallthrough keyword.

enum Priority { Low, Medium, High, Critical }

func handlePriority(p Priority) {
	when p {
		case .Critical:
			io.println("ALERT: Critical priority!")
			fallthrough
		case .High:
			io.println("Notifying on-call team")
			fallthrough
		case .Medium:
			io.println("Logging to monitoring system")
		case .Low:
			io.println("Recorded in debug log")
	}
}

In this example, .Critical will execute all three messages, .High will execute two, and .Medium or .Low will execute only their own message.

Note: fallthrough can only be used in when statements that match on enum types. It cannot be used in boolean-style when statements (those without a tag expression).

Exhaustiveness Checking

When working with enum types (including data enums), when statements must be exhaustively checked; meaning all possible variants must be handled. This provides compile-time safety.

enum Direction { North, South, East, West }

var dir = Direction.North

// This will compile - all cases handled
when dir {
	case .North: io.println("Going north")
	case .South: io.println("Going south")
	case .East: io.println("Going east")
	case .West: io.println("Going west")
}

// This would also compile - else covers remaining cases
when dir {
	case .North: io.println("Going north")
	else: io.println("Going some other direction")
}

Pattern Matching on Data Enums

Data enums (tagged unions) can be matched to determine which variant is currently held. The pattern syntax .Variant(binding) binds the associated data to a variable you can use in the case body.

enum IntOrStr {
	Int i32
	Str string
}

var value = IntOrStr.Int(42)

when value {
	case .Int(n):
		io.println("Value is an integer: #{n}")
	case .Str(s):
		io.println("Value is a string with length #{s.len}")
}

When Expressions Proposed

when can also be used as an expression to produce values based on conditions.

var colorName = when color {
	case .Red: "Red"
	case .Green: "Green"
	case .Blue: "Blue"
}