ballerina-platform / ballerina-spec

Ballerina Language and Platform Specifications
Other
167 stars 53 forks source link

Clarification on representing mulitple-keys using table type's `key-constraint` #1310

Closed lochana-chathura closed 4 months ago

lochana-chathura commented 4 months ago

Description: Consider the below sample extracted from ballerina by example: https://ballerina.io/learn/by-example/multiple-key-fields

import ballerina/io;

type Employee record {
    readonly string firstName;
    readonly string lastName;
    int salary;
};

public function main() {
    // `employees` has a key sequence with the `firstName` and `lastName` fields.
    table<Employee> key<[string, string]> employees2 = table key(firstName, lastName) [
            {firstName: "John", lastName: "Smith", salary: 100},
            {firstName: "John", lastName: "Bloggs", salary: 200}
        ];

    Employee? e = employees2["John", "Smith"];
    io:println(e);
}

The above sample is valid in the current jBallerina implementation.

In this sample, we use [string, string] tuple to represent the multiple-keys in the LHS table type descriptor. (Each element in the tuple represents a key in multiple-keys) Is that correct? (I couldn't find such in the spec) If not, how do we represent multiple-keys using table key-constraint?

Also, note that in the current implementation, if one wants to have a single key which itself is a tuple, they have to use that tuple inside another tuple, as the first tuple represents the number of keys. e.g.

type Employee2 record {|
   readonly [string, string] name;
   int salary;
|};

public function case1() {
   table<Employee2> key<[string, string]> t2 = table key(name) [
       {name: ["John", "Smith"], salary: 100},
       {name: ["Jane", "Doe"], salary: 200}
   ]; // NOT ALLOWED. Because LHS is expecting a multiple-key table with key types string and string.

   table<Employee2> key<[[string, string]]> t3 = table key(name) [
       {name: ["John", "Smith"], salary: 100},
       {name: ["Jane", "Doe"], salary: 200}
   ]; // ALLOWED. Because LHS is expecting a single key with the key type [string, string]
}

Useful or not, member access for the multiple-key tables anyway has been modeled using the list-constructor-expr. Quoting from the spec.

A multi-key-expression is allowed only when the static type of a container-expression is a subtype of table. A multi-key-expression E1, E2,..., En is equivalent to a list-constructor-expr [E1, E2,..., En].

CC: @pcnfernando @gimantha @MaryamZi

jclark commented 4 months ago

The representation of multiple keys as tuples is defined by this sentence:

If the key sequence consists of multiple field names f1,f2,...,fn where n is ≥ 2, then the key value for a member r is a tuple with members v1,v2,...,vn where vi is the value of field fi of r.

jclark commented 4 months ago

Also, note that in the current implementation, if one wants to have a single key which itself is a tuple, they have to use that tuple inside another tuple, as the first tuple represents the number of keys.

I don't think that's right. The key constraint is constraining the key value of each table row. In the case, where the key constraint has a single field name, then the key value consists of that key. The spec says:

if the key sequence consists of a single field name f, then the key value of a member is that value of field f of that member

lochana-chathura commented 4 months ago

Consider the below sample,

type Employee record {|
   readonly [string, string] name;
   readonly string department;
   readonly string city;
   int salary;
|};

public function foo() {
   // case I
   table<Employee> key<[string, string]> t1 = table key(name) [ ];
   // case II
   table<Employee> key<[string, string]> t2 = table key(department, city) [ ];
}

According to the aforementioned spec rules, both single-key and multiple-key table constructor scenarios in this sample are assignable to the table<Employee> key<[string, string]> type right?

This means if we consider, table<C> key<K> where K = [T1, T2, ... , Tn],

i.e. For table<C> key<K> where K = [T1, T2, ... , Tn] and n>=2, we cannot guarantee the number of fields in the key. It could be both 1 and n.

jclark commented 4 months ago

Right on both points.

The way I think of it is that every row in the table has exactly one key which is constructed from one or more fields. When the type of the key is a tuple with length > 1, you cannot tell just from the type of the key whether it is constructed from one field (of tuple type) or multiple fields. All you care about is the type of the key.

lochana-chathura commented 4 months ago

Thanks, @jclark for the clarification.