# Value Types

This guide describes a feature of Rel that allows you to define your own types.

## Introduction

There are two kinds of types that you can define yourself: entity types and value types. A value type has its own name and is distinct from every other type, even though its values are represented by tuples of primitive types. This allows you to make your model more clear and readable. It also helps you to avoid some common mistakes.

## A Simple Example

You have built the first small fragment of a toy railway, and you intend to expand it significantly. You decide to build a computer model of the rail network, so that you can answer questions about transit times, distances, and the like. This will come in handy when the network grows.

There are three stations: “A”, “B”, and “C”. You know the distances between them, and how much time it takes to travel from one to the other:

```
// install
def distance = (:A, :B, 100.0)
def distance = (:B, :C, 50.0)
def travel_time = (:A, :B, 30)
def travel_time = (:B, :C, 20)
```

This captures your data, but the model has a problem. Since the distances and time intervals are represented by numbers, it is all too easy to get confused and perform operations that should not be allowed. For instance, you can add a value that represents a distance to a value that represents a time interval:

```
// query
def output[x, y] = distance[x, y] + travel_time[x, y]
```

This doesn’t make much sense, and you would like to be protected from such mistakes.
In other words, you would like a number that represents a distance to be clearly distinguishable from a number that represents a time interval.
Rel allows you to do that by declaring *value types*.

### Declaring Simple Value Types

The declaration

`value type Distance = Float`

introduces a new type, `Distance`

, whose values are represented by floating-point numbers but are distinct from values of all other types.

The declaration also introduces an infinite binary relation, `^Distance`

, which is often referred to as *the constructor* of `Distance`

.
The relation `^Distance`

is a one-to-one map between floating-point numbers and the corresponding values of type `Distance`

.
It can be used both to construct value types (`^Distance[100]`

) and to retrieve the actual value(s) from it (`x: ^Distance(x, ^Distance[100])`

).

Note that there is nothing special about identifiers that begin with the character `^`

, but in the interest of readability it is best to avoid using them for other purposes.

Starting from scratch, you rewrite the model:

```
// install
value type Distance = Float
value type Duration = Int
def distance = (:A, :B, ^Distance[100.0])
def distance = (:B, :C, ^Distance[50.0])
def travel_time = (:A, :B, ^Duration[30])
def travel_time = (:B, :C, ^Duration[20])
```

The square brackets are important! The partial application `^Distance[100.0]`

evaluates to the value of type `Distance`

that corresponds to the number `100.0`

. The first definition of `distance`

above could also have been written as `def distance = (:A, :B, {d : ^Distance(100.0, d)})`

.

It may be instructive to display the relation `distance`

:

```
// query
def output = distance
```

The heading of the third column indicates that the column shows floating-point representations of the type `Distance`

, which is shown as the symbol `:Distance`

.

For good measure, here is the relevant part of the implicit relation `^Distance`

.

```
// query
def output(x, y) = ^Distance(x, y) and distance(_, _, y)
```

The problematic addition of distances to time intervals no longer works.
If you try it, the output will be empty, just as it would be for `def output = 5 + "five"`

:

```
// query
def output[x, y] = distance[x, y] + travel_time[x, y]
```

However, not all is well yet.
It would be nice to add a distance to another distance, but addition is not defined for values of type `Distance`

:

```
// query
def output = distance[:A, :B] + distance[:B, :C]
```

Currently such operations on value types must be defined by the user, as shown in the following subsection. Automatic support will be provided in the future.

### Extracting the Representation of a Value (Simple Case)

To define addition for two values of type `Distance`

, you must extract the associated floating-point numbers from `^Distance`

.
This can be done as follows:

```
// query
def (+)[x in Distance, y in Distance] =
xf + yf from xf, yf where ^Distance(xf, x) and ^Distance(yf, y)
def output = distance[:A, :B] + distance[:B, :C]
```

The above is a little clunky, because it uses the variables `xf`

and `yf`

rather than a partial application of the relation `^Distance`

.
The latter could not be used, because the desired numeric value is the first element of each tuple, not the second one.
This may be remedied by using the library function `transpose`

, which reverses the order of elements in a binary relation:

```
// install
def (+)[x in Distance, y in Distance] =
transpose[^Distance][x] + transpose[^Distance][y]
```

```
// query
def output = distance[:A, :B] + distance[:B, :C]
```

Everything is now in place to compute the average speed of the train between neighboring stations:

```
// install
def speed[a, b] = transpose[^Distance][dv] / transpose[^Duration][tv]
from dv in distance[a, b], tv in travel_time[a, b]
```

```
// query
def output = speed
```

## Full Generality

In general, a value type can be represented by a tuple. For example, you might want to associate a point in three-dimensional space with its three Cartesian coordinates. This can be done as follows:

```
// install
value type Point = Float, Float, Float
def point = (:P1, ^Point[0.0, 1.0, 2.0])
def point = (:P2, ^Point[-3.0, -2.0, -1.0])
```

The relation `^Point`

is not binary:

```
// query
def output(x, y, z, v) = ^Point(x, y, z, v) and point(_, v)
```

### Extracting the Representation of a Value (General Case)

Since `^Point`

is not binary, you cannot use `transpose`

to make it easy to get the three floating-point numbers that represent a value of type `Point`

.
However, you can define a relation that moves the last element of each tuple to the first position:

```
// install
@inline
def rotate_right[R](x, y...) = R(y..., x)
```

You might even want to use a convenient wrapper: functions that convert a value of type `Point`

to its coordinates.
The code looks like this:

```
// install
@inline
def coordinates[p in Point] = rotate_right[^Point][p]
@inline
def coord1[p in Point] = first[coordinates[p]]
@inline
def coord2[p in Point] = second[coordinates[p]]
@inline
def coord3[p in Point] = last[coordinates[p]]
```

It is now easy to get the coordinates of points or to compute the distance between two points:

```
// query
def output = label, coordinates[p] from label, p where point(label, p)
```

```
// install
def distance[x in Point, y in Point] =
sqrt[squared[coord1[x] - coord2[x]] +
squared[coord2[x] - coord2[y]] +
squared[coord3[x] - coord3[y]]]
```

```
// query
def output = distance[point[:P1], point[:P2]]
```

### Units of Measurement

The general form of value types allows you to associate quantities with units of measurement. For example, in the little application for toy trains it was implicitly assumed that distances were expressed in terms of some unit of distance, but that unit was not explicitly specified. The unit can become a part of the value type.

You can try it out without overwriting the previous definitions by using different names:

```
// install
def UnitOfDistance = :in ; :cm ; :km ; :mile
value type UDistance = UnitOfDistance, Float
def dist = (:A, :B, ^UDistance[:cm, 100.0])
def dist = (:B, :C, ^UDistance[:in, 50.0])
```

The results of displaying `dist`

may seem surprising at first:

```
// query
def output = dist
```

Why is the type “Mixed”? Things become more clear after displaying each of the distances separately:

```
// query
def output = dist[:A, :B]
```

```
// query
def output = dist[:B, :C]
```

The unit is a part of the type. It is worth knowing that the information about the unit is a compile-time artifact and is not stored in the database. The extra precision comes for free!

Distances in different units are not directly comparable:

```
// query
def output = equal(^UDistance[:cm, 100.0], ^UDistance[:cm, 100.0])
```

```
// query
def output = equal(^UDistance[:cm, 100.0], ^UDistance[:in, 100.0])
```

Although the unit field in a value of type `UDistance`

is not stored explicitly, `^UDistance`

is a ternary relation.
So the following works:

```
// install
def unit[d in UDistance] = first[rotate_right[^UDistance][d]]
def quantity[d in UDistance] = second[rotate_right[^UDistance][d]]
```

```
// query
def x = ^UDistance[:in, 50.0]
def output = unit[x]
```

```
// query
def x = ^UDistance[:in, 50.0]
def output = quantity[x]
```

### Declaring Value Types in Terms of Value Types

Value types are fully fledged types. In particular, value types can be used to define other value types.

This is made clear by the following example:

```
// query
value type A = Int
value type B = String
value type C = A, B
def output = ^C[^A[1], ^B["bb"]]
```

It’s helpful to compare the above with the following:

```
// query
value type C = Int, String
def output = ^C[1, "bb"]
```

When you look at the output, you will see that the representation of tuples is identical in both examples. In other words, you can use value types without incurring any additional runtime costs.

### General Expressions in Declarations of Value Types

The general form of a value type declaration is quite similar to the definition of a relation. The body of the declaration can include expressions that constrain the kind of values that can be constructed.

For example, you can declare a value type `Small`

that is represented by a pair consisting of a small integer and a short string.
An attempt to construct a value of this type with a larger integer or a longer string will be unsuccessful.
Here is a way to write this:

```
// query
value type Small(x in Int, y in String) { abs[x] < 4 and string_length[y] < 4 }
def output = ^Small[0, "abc"]; ^Small[5, "a"]; ^Small[3, "long"]; ^Small[3, "b"]
```

### Empty Value Types

A value type need not have any representation.
The declaration `value type None`

is correct; the corresponding constructor relation, `^None`

, is empty.

The effect is similar to that of type `Missing`

in Julia.
The non-existing value can be used as an element in a tuple, but yields no result when applied in arithmetic operations:

```
// query
value type None
def none = ^None[]
def output:tuple = 1, none, "three"
def output:mult = none * 1
```

In fact, Rel already has a built-in type that is somewhat similar.
As in Julia, it’s called `Missing`

, and the non-existent value has the name `missing`

.

```
// query
def output:tuple = 1, missing, "three"
def output:mult = missing * 1
```

The type `Missing`

is currently not a value type and the relation `^Missing`

does not exist.