import ballerina/io;
type Student record {
    string name;
    int age;
    Grades grades;
};
type Address record {|
    string city;
    string country;
|};
type Grades record {|
    int maths;
    int physics;
    int chemistry;
    int...;
|};public function main() {    int age = 17;
    Student john = {
        name: "John Doe",
        age,
        grades: {
            maths: 80,
            physics: 75,
            chemistry: 65
        }
    };
    io:println(john);
    io:println(john.name);
    io:println(john["name"]);
    io:println(john.grades.maths);    Student peter = {
        name: "Peter",
        age: 19,
        grades: {
            maths: 40,
            physics: 35,
            chemistry: 35
        }
    };
    peter.age = 16;    io:println(peter);
    io:println(john);
    Address address = {city: "Colombo", country: "Sri Lanka"};
    peter["address"] = address;
    io:println(peter);
    Grades grades = {maths: 80, physics: 75, chemistry: 65, "english": 90};
    io:println(grades);
    int? english = grades["english"];
    io:println(english);
    Student anne = {
        name: "Anne",
        age: 18,
        grades: {
            maths: 70,
            physics: 80,
            chemistry: 55
        },
        ...address
    };
    io:println(anne);
    var rec = {name: "Amy", age: 18, ...address};
    io:println(rec);
    io:println(rec.name);
}# To run this sample, navigate to the directory that contains the
# `.bal` file, and execute the `ballerina run` command below.
ballerina run records.bal
name=John Doe age=17 grades=maths=80 physics=75 chemistry=65
John Doe
John Doe
80
name=Peter age=16 grades=maths=40 physics=35 chemistry=35
name=John Doe age=17 grades=maths=80 physics=75 chemistry=65
name=Peter age=16 grades=maths=40 physics=35 chemistry=35 address=city=Colombo country=Sri Lanka
maths=80 physics=75 chemistry=65 english=90
90
name=Anne age=18 grades=maths=70 physics=80 chemistry=55 city=Colombo country=Sri Lanka
name=Amy age=18 city=Colombo country=Sri Lanka
Amy

Record

In Ballerina, records are a mapping type. However, the keys (fields) are named and their types define the types of values that are allowed for the fields. If the set of fields is fixed, the record is called a “closed record”. If the set of fields is not fixed, the record is called an “open record”.

import ballerina/io;
type Student record {
    string name;
    int age;
    Grades grades;
};

Define an open record type named Student. The { and } delimiters indicate that in addition to the defined fields, this record type allows additional fields with anydata values. The descriptor record { } is equivalent to record {| anydata...; |}.

type Address record {|
    string city;
    string country;
|};

Define a closed record type named Address. The {| and |} delimiters indicate that this record type allows mapping values, which contain only the described fields.

type Grades record {|
    int maths;
    int physics;
    int chemistry;

Define an open record type named Grades. Although it is defined using the {| and |} delimiters, it has an int rest field as well. Therefore, this is an open record type.

    int...;
|};

This is a rest field of the typeint. All additional fields should be of the type or a subtype of the rest field.

public function main() {
    int age = 17;
    Student john = {

This creates a Student record. Since all the fields are required and none of the fields have explicit default values assigned to them, values must be specified for all the fields when creating the record.

        name: "John Doe",

A field can be specified as a key-value pair.

        age,
        grades: {
            maths: 80,
            physics: 75,
            chemistry: 65
        }
    };
    io:println(john);

A variable reference can also be used to define a field. The name of the variable will be used as the key while the variable reference itself will be used as the value expression. This is equivalent to age: age.

    io:println(john.name);

This is an example of field-based access of record fields. The return type of this expression is the type of the field. Field access is only allowed for required fields in a record.

    io:println(john["name"]);

This is an example of member access of record fields. Where the type of the field is T, the type of this expression is T if the field is a required field or has a default value. If the field is an optional field or a rest field, the type of this expression is T?. If it is a closed record, accessing an undefined key will result in a compilation error.

    io:println(john.grades.maths);

This fetches a field of a nested record.

    Student peter = {
        name: "Peter",
        age: 19,
        grades: {
            maths: 40,
            physics: 35,
            chemistry: 35
        }
    };
    peter.age = 16;

This modifies the value of the age field. Field access is allowed with assignment only for fields defined in the record type descriptor.

    io:println(peter);
    io:println(john);
    Address address = {city: "Colombo", country: "Sri Lanka"};
    peter["address"] = address;
    io:println(peter);

Member access can be used to assign to fields that are not defined in the record type descriptor. An attempt to add additional fields to a closed record results in compile errors.

    Grades grades = {maths: 80, physics: 75, chemistry: 65, "english": 90};
    io:println(grades);

Create a Grades record adding additional fields for the int-typed rest field. The english field is not specified in the record, but is allowed since Grades is an open record with an int-typed rest field. Keys for such field should either be string literals or expressions (i.e., they cannot be identifiers).

    int? english = grades["english"];
    io:println(english);

Similarly, only member access can be used to access the fields that are possibly added for the rest field. An int value is returned if the field is present in the record, else () is returned.

    Student anne = {
        name: "Anne",
        age: 18,
        grades: {
            maths: 70,
            physics: 80,
            chemistry: 55
        },
        ...address
    };
    io:println(anne);

A mapping constructor expression used when creating a record value can also include a spread field referring to another mapping value. When a spread field is specified, all the fields of the relevant mapping value are added to the new record value being created. A spread field is used with the address to include the individual address entries in address when creating anne.

    var rec = {name: "Amy", age: 18, ...address};
    io:println(rec);

Using a mapping constructor expression with var (i.e., no contextually-expected type) results in a mapping value where the inferred type is a record type based on the fields specified in the mapping constructor expression.

    io:println(rec.name);
}

The record type inferred for rec is record {| string name; int age; string city; string country; |}. Thus, field access can be used to access the fields.

# To run this sample, navigate to the directory that contains the
# `.bal` file, and execute the `ballerina run` command below.
ballerina run records.bal
name=John Doe age=17 grades=maths=80 physics=75 chemistry=65
John Doe
John Doe
80
name=Peter age=16 grades=maths=40 physics=35 chemistry=35
name=John Doe age=17 grades=maths=80 physics=75 chemistry=65
name=Peter age=16 grades=maths=40 physics=35 chemistry=35 address=city=Colombo country=Sri Lanka
maths=80 physics=75 chemistry=65 english=90
90
name=Anne age=18 grades=maths=70 physics=80 chemistry=55 city=Colombo country=Sri Lanka
name=Amy age=18 city=Colombo country=Sri Lanka
Amy