How does the field access behave after cloning a constant map in Ballerina

58 views Asked by At

Consider the following code:

import ballerina/io;

type User record {|
    string name;
    string email;
|};

const USER = {
    name: "John Doe",
    email: "[email protected]"
};

public function main() {
    User user = USER.clone();
    user.name = "Jane Doe";
    io:println("User name: " + user.name);
}

Is this code correct? I have a constant map and clone it as user and updated one field. Since it is a clone with a constant map, are the fields cloned as readonly? Then the clone() should return a compiler error, since in the target type, the field is not explicitly readonly right?

Currently this is failing with this runtime error:

error: {ballerina/lang.map}InherentTypeViolation {"message":"cannot update 'readonly' field 'name' in record of type '(service:record {| "John Doe" name; "[email protected]" email; |} & readonly)'"}

But if I use cloneWithType() it is working fine:

public function main() returns error? {
    User user = check USER.cloneWithType();
    user.name = "Jane Doe";
    io:println("User name: " + user.name);
}

Can someone explain the behaviour here?

1

There are 1 answers

0
Sasindu Dilshara On

In here the type of the USER is inferred as

{
    name: "John Doe",
    email: "[email protected]"
} & readonly

by the Ballerina compiler.

The USER.clone() will return the USER record itself since it is immutable.

So, the type of USER is a subtype of User record. So Compiler allows to assign that into the user variable. But the original type of the USER record is readonly. So we cannot update the fields of the record(We can't update fields in readonly maps). That is the meaning of the error message.

Example:- Assume there is a type called A.

A & readonly a = value

A b = a

In here the type of the b is A & readonly So we cannot update the fields in value b

But if you use CloneWithType function, It will clone the value and convert it into the expected type.

So in here the type of the user is User.

User user = check USER.cloneWithType();

So we can update the fields in here.

By the way important to know that when you use clone() (or cloneReadOnly()) with an immutable value a copy will not be created. The value itself will be the result.

User user = USER.clone();
io:println(user == USER); // true

User & readonly userImmutable = USER.cloneReadOnly();
io:println(userImmutable == USER); // true