Skip to content

Type definitions

Stian Håklev edited this page Aug 4, 2017 · 3 revisions

To ensure that only graphs with valid data flow patterns can be instantiated by the engine, activityTypes have to declare the data that they accept as input, and the data they produce as output (the reactive data structure).

We can verify that a source type definition is compatible with a target type definition, using check(source, target). The order of the arguments is not arbitrary - if x includes the key age, and y does not include the key age, then check(x, y) will succeed - all the keys required by y are provided by x, however check(y, x) will fail, since x requires age, which is not provided by y.

Basic structure

A type definition looks like the structure of the data itself, with some caveats. Here is an example of two type definitions, one for an object keyed by ID, and another for an array of objects.

const typedef_obj = { Id: { id: 'Id', title: 'string', content: 'string', url: 'URL' };
const typedef_ary = [{ id: 'Id', title: 'string', content: 'string', url: 'URL' }];

If input data is optional, we can use the special notation {_: typedef} to specify that we do not require any data, but if any data is sent, it must adhere to the type specification. So given the example:

const a = { _: [{ title: 'string', content: 'string' }] };
const b = undefined
const c = { Id: { title: 'string', content: 'string' } }
const d = [{ title: 'string', content: 'string' }]
  • check(b, a) would be valid, because it's optional to supply any data at all
  • check(a, b) would be valid, since the target does not require any data at all
  • check(c, a) would not be valid, since if there is source data, the type definitions have to match
  • check(d, a) would be valid, since the type definitions match
  • check(a, d) would not be valid, since optional data in source type definitions is discarded

Leaf types

Leaf types are the primary types string and number, as well as any number of opaque (nominal) types, indicated with a capital first letter, like Id or URL.

An activity package should define exactly what kind of input data it requires to run, and be specific about input, and conservative about output. Although a URL is a string, if a video player would malfunction when returning a string that was not the URL of a video, it should specify that it requires VideoURL, and not string. However, if an activity asks students to enter the URL of a video in a text input field, it should specify that it is generating string, unless it actually verifies that these are guaranteed to be VideoURLs.

Arrays

Arrays should have exactly one item, which is the type definition of array items. An actual array could have 0, 1 or many items, but they should all correspond to the same type definition. For example, given the following definition:

const type = [{ age: 'number', name: 'string' }];

all three examples below are valid:

const ex1 = [{ age: 21, name: 'Stian' }];
const ex2 = [];
const ex3 = [{ age: 21, name: 'Stian' }, { age: 33, name: 'Peter' }];

Objects

A source type definition must have all obligatory keys of the target type definition, and the types of the keys must match. A target definition can define optional fields, denoted through a leading _ in the name. In this case, source definitions do not have to supply this key, but if they supply it, the type must match the target definition. Optional fields in source definitions are not taken into account.

For example:

const a = { age: 'number', _description: 'string' }
const b = { age: 'number', year: 'number' }
const c = { age: 'number', description: { text: 'string' } };
const d = { age: 'number', description: 'string' }
  • check(a, b) will not be valid, because b requires year, which is not provided by a
  • check(b, a) will be valid, because description is optional, and it's not a problem to provide extra keys
  • check(a, d) will not be valid, because optional strings in a source type definition is discarded
  • check(c, a) will not be valid, because when the description is provided, its type must match

Any

The special variable any will match any incoming data. It can only be used from a target type definition, and it is expected to only be used by operators which pass some data through, rather than for activity types which will require specific data types to be able to display them. For example:

const a = ['any']
const b = [{ age: 'number' }]
const c = { age: 'number' }
  • check(b, a) is valid, because a expects an array with any value
  • check(a, b) is invalid, because any is disregarded from source, and thus b cannot find the object it's looking for
  • check(c, a) is invalid, because a requires an array, not an object

Any can also be used to specify a structural transformation. Anytime the chain checker encounters a type called any or matching any-*, it will store a reference to what the any type matches in the input type, and substitute this in the output type. As an example:

const types = {
  incoming: { Id: 'any-1' },
  outgoing: ['any-1']
};

This type definition specifies an operator which transforms an id-mapped object, to an array.