switch statement

The switch statement executes code conditionally according to an enumerated list of disjoint cases.

Usage

The general form of the switch statement is

switch ( /* switch expression */ )
case ( /* case */) {
    /* case block */
}
case ( /* case */) {
    /* case block */
}
else case ( /* case */) {
    /* case block */
}
else {
    /* else block */
}
/* code after switch statement */

There must be one or more disjoint case clauses. There may optionally be one or more non-disjoint else case clauses. There may be at most one else clause.

The else clause is required if (and only if) the cases are not exhaustive.

Description

Unlike most other programming languages, a switch statement in Ceylon must be exhaustive, covering all possible values of the switch expression, and its cases must be disjoint, having no value in common.

Execution

The switch expression is evaluated and then each of the cases is considered. The matching case has its block executed, and then execution continues with the code after the switch statement. If none of the given cases match and an else clause is given, then the else block is executed, and then execution continues with the code after the switch statement.

Note: The compiler need not evaluate the cases in the order they are written.

Exhaustivity and else

If the cases cover every possible case of the switch expression then the switch is said to be exhaustive, and the else clause is prohibited. Otherwise the else clause is required.

case with an enumerated type (value reference)

If the switch expression is of an enumerated type U then a case may be of the form case (x) where x is one of the cases of U. A list of cases, case(x | y | z), is also permitted.

Since Boolean and Null are both enumerated types, we can use their enumerated values in a switch:

void switchOnEnumValues(Boolean? b) {
    switch(b)
    case (true) {
        print("yes");
    }
    case (false) {
        print("no");
    }
    case (null) {
        print("Who cares");
    }
}

case(is...) (assignability condition)

If the switch expression type U is a union of disjoint types, or an enumerated type, then a case may be of the form case (is V) where V is a case of the type U.

void switchOnEnumTypes(Foo|Bar|Baz var) {
    switch(var)
    case (is Foo) {
        print("FOO");
    }
    case (is Bar) {
        print("BAR");
    }
    case (is Baz) {
        print("BAZ");
    }
}

case(...) with literals

If the switch expression is of type Integer, Character, or String then the cases may be literal values.

void switchOnLiteralValues(Integer i) {
    switch (i)
    case (0) {
        print("zero"); 
    }
    case (1) {
        print("one");
    }
    case (2) {
        print("two");
    }
    else { 
        print("lots"); 
    }
}

Since it's impossible to enumerate every value of any of these types, the else clause is required.

case(...) with tuples

Since Ceylon 1.3.1 it is possible to switch over a Tuple (and Empty) expression using tuples:

Float[2]|Float[3] coord = ... ;
switch (coord)
case ([Float x, Float y]) { 
    print((x^2+y^2)^0.5);
}
case ([Float x, Float y, Float z]) {
    print((x^2+y^2+z^2)^0.5);
}

It is also possible to match tuples with literal elements

String formatComplex([Integer, Integer] complex) {
    switch(complex)
    case ([0, 0]) {
        return "origin";
    }
    case ([1, 0]) {
        return "1";
    }
    case ([0, 1]) {
        return "i";
    } 
    else {
        return "``complex[0]``+``complex[1]``i";
    }
}

Flow typing

Note that case (is ...) narrows the type of the switch value within the scope of the case. Furthermore this flow typing can also affect the else clause of the control structure:

void go(Car|Bicycle|Motobike vehicle) {
    switch(vehicle)
    case (is Car) {
        // vehicle has type Car
        vehicle.drive();
    } 
    else {
        // vehicle has type Bicycle|Motobike
        vehicle.ride();
    }
}

Variable declaration

As an alternative to the switch expression, an inline variable declaration (containing an initializing expression) is allowed. This declares a new variable which is then usable inside the case and else blocks (with the correct type in each of them).

switch(line = process.readLine())
// line has type String?
case(is Null) {
    // line has type Null
    print("End of file!");
}
else {
    // line has type String
    print(line);
}

See also