Type narrowing doesn't work when a record defined with rest field

41 views Asked by At
import ballerina/io;

type Vehicle record {|
    string mode;
    string color;
    string make;
    string ...; // rest field
|};

type AeroPlance record {|
    *Vehicle;
    string noOfEngines;
|};

type Helicopter record {|
    *Vehicle;
    string noOfRotors;
|};

public function main(string[] args) {
    
    AeroPlance|Helicopter flight = getFlight();
    if (flight is AeroPlance) {
        printAeroPlance(flight);
    } else {
        printHelicopter(flight);
    }
}

function getFlight() returns AeroPlance|Helicopter {
    return {mode:"Air", color:"Red", "make":  "Boeing", noOfEngines:"2"};
}

function printAeroPlance(AeroPlance aeroPlance) {
    io:println("AeroPlance:", aeroPlance);
}

function printHelicopter(Helicopter helicopter) {
    io:println("Helicopter: ", helicopter);
}

In the above code, type narrowing doesn't work as expected when the Vehicle record is defined with a rest field. I'm getting the following compilation error.

ERROR [b.bal:(26:25,26:31)] incompatible types: expected 'Helicopter', found '(AeroPlance|Helicopter)'

However, this works when the rest field is removed from the Vehicle record. What is the reason for this behavior?

1

There are 1 answers

0
MOHAMED SABTHAR On BEST ANSWER

This is because of how typing and type narrowing is defined at the moment with open records (Vehicle is an open record, and therefore the AeroPlane and Helicopter records are also open). Although the jBallerina implementation doesn't allow it, since the records are open and mutable. the specification says a value of a type like

type AeroPlaneOrHelicopter record {|
    *Vehicle;
    string noOfRotors?;
    string noOfEngines?;
|};

is valid for AeroPlane|Helicopter.

Therefore, the type cannot be narrowed to Helicopter in the else block. If the values were closed or immutable, narrowing would work as expected in the sample.

(AeroPlane|Helicopter) & readonly flight = getFlight().cloneReadOnly();
if flight is AeroPlane {
    printAeroPlane(flight);
} else {
    printHelicopter(flight);
}