Records

A record is a composite data type representing an unordered collection of labelled values. Each pair of label and value is called a member.

If you are familiar with other programming languages, you may recognize records as "objects", "dataclasses" or "dictionaries".

Creating records

Records are created using record expressions, listing each member's label together with an expression denoting its value.

// Example: create a record with two members labelled 'name' and 'latinName'.

let animal = {
    name: "Koala",
    latinName: "Phascolarctos cinereus"
};

// We can now access the members using dot notation:
//    animal.name, or
//    animal.latinName
let animalName = animal.name;

Records may be arbitrarily nested:

// Example: create a nested record

let city = {
    name: "Kyoto",
    location: {
        latitude: "35.011665",
        longitude: "135.768326"
    }
};

// Now we can access the inner record using dot nation
let longitude = city.location.longitude;
Advanced: Punning

If you are assembling a record from existing variables, you can use a shorthand syntax to let the variables supply both the label and the value of the members. This is known as "punning".

// Example: create a record with implicit labels
let name = "Panda";
let latinName = "Ailuropoda melanoleuca";

// animal will be { name: "Panda", latinName: "Ailuropoda melanoleuca" }
let animal = { name, latinName };
Advanced: Creating a record from a named type
// Define a named type
type Point = {
    x: number,
    y: number,
    z: number = 0
};

// Create a record 'p' and initialize x and y.
// Because the Point type has a member z with default value 0,
// which is not initialized in the record expression, p.z == 0.
let p = new Point { x: 1, y: 2 };

Accessing members

Record members are accessed using dot notation. Nullable records can also be used with dot notation: the result of such an expression is also null.

let users = [
    {id: 0, name: "Aline"},
    {id: 1, name: "Natia"}
];

let nullUser = firstOrNull(users where id == 2); // note: there is no user with id 2

return nullUser.name; // this will return null.

Updating records

Records, like all other values in FlowScript, are immutable: you cannot modify an existing record but only make modified copies of it. This is accomplished using the with keyword followed by a record expression specifying the members to overwrite or add.

The record expression following a with keyword has all the record's members in its variable scope.

// Example: create a record and then update it.

let currency = {
    name: "EUR",
    exchangeRate: 1.08
};

let modifedCurrency = currency with {
    exchangeRate: exchangeRate + 0.01
};

// 'modifiedCurrency' now has members name ("EUR") and exchangeRate (1.09).
// 'currency' has not been changed and still has exchangeRate 1.08.
Advanced: JSON compatibility

The records values in FlowScript are designed to be a superset of the JSON format. For this reason, arbitrary text literals can be used in place of labels.

// Example: a record where the labels are text literals.
let mapLocation = {
    "city/town": "Gothenburg",
    "latitude and longitude": "57.708870, 11.974560"
};

// To access such a member, you use dot notation followed by a text literal.
let coordinates = mapLocation."latitude and longitude";

// When creating a modified copy of a record with literal labels,
// the scope of the with-expression contains 
let newLocation = mapLocation with {
    "city/town": upper(#"city/town") // Notice the hash sign
};

The types of records

The type of a record is an image of the record itself, but with each member's value replaced by its type. The type is inferred automatically from the value. This mechanism is known as structural typing.

// Example: create two records with type { name: text, population: number }

let country1 = {
    name: "Costa Rica",
    population: 5154000
}

let country2 = {
    name: "Australia",
    population: 25690000
}

In the example above, country1 and country2 have the same type and are therefore compatible: they can both be held by the same variable. For a more in-depth treatment on the subtleties of record type compatibility, refer to the Appendix on subtyping rules.

Last updated