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.latinNamelet animalName =animal.name;
Records may be arbitrarily nested:
// Example: create a nested recordlet city = { name:"Kyoto", location: { latitude:"35.011665", longitude:"135.768326" }};// Now we can access the inner record using dot nationlet 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 labelslet 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 typetypePoint= { 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 2returnnullUser.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.