Tables and integrated query

Given a record such as:

record Rec {
  public int id;
  public string name;
  public int age;
  public int score;
}

This record can be used with the table:

table<Rec> _records;

This table is a way of organizing information per given record type. In general, the table is a useful construct which enables many common operations found in data structures. The above record would create a table like:

idnameagescore
1Joe451012
2Bryan49423
3Jamie42892
4Jordan527231

Diving Into Details

A table in and of itself requires a toolkit to handle it, and we introduce a variant of SQL in the form a language integrated query (LINQ). It is a variant in many ways, and we will introduce the mechanics.

Adding rows/records to table via ingestion

The ingestion operator (<-) allows data to be inserted from a variety of sources. For instance, we can simply use ingestion to copy a message into a row.

record Rec {
  message int x;
  message int y;
}

message Msg {
  int x;
  int y;
}

table<Rec> tbl;

channel foo(Msg m) {
  tbl <- m;
}

#foo {
  tbl <- {x:42, y:13};
}

Notes about the special id field. If a record has the 'id' field, then it must be an integer. ingestion will generate an id, and we can get that via the 'as' keyword.

channel foo(Msg m) {
  tbl <- m as m_id;
}

#foo {
  tbl <- {x:42, y:13} as xy_id;
}

Reactive lists

Lists of records can be filtered, ordered, sequenced, and limited via language integrated query (LINQ).

iterate

First, the iterate keyword will convert the table<Rec> into a list<Rec>.

public formula all_records = iterate _records;

Now, by itself, it will list the records in their canonical ordering (by id). It is important to note that the list is lazily constructed up until the time that it is materialized by a consumer, and this enables some query optimizations to happen on the fly.

where

We can suffix a LINQ expression with where to filter items.

public formula young_records = iterate _records where age < 18;

indexing!

Yes, we can make things faster by indexing our tables. The index keyword within a record will indicate how tables should index the record.

public formula lucky_people = iterate _records where age == 42;

This will accelerate the performance of where expressions when expressions like age == 42 are detected via analysis.

shuffle

The canonical ordering by id is not great for card games, so we can randomize the order of the list. Now, this will materialize the list.

public formula random_people = iterate _records shuffle;
public formula random_young_people = iterate _records where age < 18 shuffle;

order

Since the canonical ordering by id is the insertion/creation ordering, order allows you to reorder any list.

public formula people_by_age = iterate _records order by age asc;

limit

public formula youngest_person = iterate _records order by age asc limit 1;

offset

With offset, you can skip the first entries with a query.

public formula next_youngest_person = iterate _records order by age asc offset 1 limit 1;

Bulk Assignments

A novel aspect of a reactive list is bulk field assignment, and this allows us to do some nice things. Take the following definition of a Card table representing a deck of cards:

record Card {
  public int id;
  public int value;
  public principal owner;
  public int ordering;
}

table<Card> deck;

We can shuffle the deck using shuffle and bulk assignment.

procedure shuffle() {
  int ordering = 0;
  (iterate deck shuffle).ordering = ordering++;
}

This assignment of ordering will memorize the results from shuffling. With a single statement, we can deal cards by assigning ownership.

procedure deal_cards(int count) {
  (iterate deck             // look at the deck
    where owner == @no_one  // for each card that isn't own
    order by ordering asc   // follow the memoized ordering
    limit count             // deal only $count cards
    ).owner = @who;          // for each card, assign an owner to the card
}

This ability makes it simple to update a single field, but it also applies to method invocation as well.

Bulk method execution

Bulk Deletes