THIS POST IS ARCHIVED

# Value Types in Rel

Differentiating between types of values is an essential aspect of application development, as it enables clarity of modeling and leads to fewer errors.

Consider a database that stores distances and travel times between different cities. For example:

``````def distance = ("Tampa", "Miami", 279.9)
def distance = ("Miami", "Atlanta",  662.6)

def travel_time = ("Tampa", "Miami", 5)
def travel_time = ("Miami", "Atlanta", 11)``````

Although this data describes the scenario reasonably well, it may lead to issues when querying the data. Specifically, distances and time intervals are represented here as plain numbers even though they represent quantities denominated in different units, so it's possible to confuse the two when querying.

For example, the following query produces results even though they are not meaningful:

``````def length[x, y] = distance[x, y] + travel_time[x, y]
def output = length``````
``````def distance = ("Tampa", "Miami", 279.9)
def distance = ("Miami", "Atlanta",  662.6)

def travel_time = ("Tampa", "Miami", 5)
def travel_time = ("Miami", "Atlanta", 11)
def length[x, y] = distance[x, y] + travel_time[x, y]
def output = length``````

Output:

In this case distance (in miles) was accidentally added to travel time (in hours), thus producing unhelpful output. Ideally, users should be protected from these kinds of mistakes.

We are excited to announce the support of value types in Rel, which allow users to distinguish elements that represent different things when writing applications in Rel.

You can define a value type as follows:

``````value type Distance = Float
value type Duration = Int``````

These declarations define two types, one for travel distance and one for travel duration. A value type has its own name and is distinct from every other type, even though it may be parameterized by common underlying types.

Value types are useful as they allow users to make their models clearer, more readable, and more maintainable. They also help avoid mistakes similar to the one presented above.

Value types allow users to distinguish between different kinds of values, regardless of the potentially identical underlying representation. Additionally, value types can be used to define other value types under certain conditions.

## Defining Value Types

Using value types, the previous example can now be rewritten:

``````value type Distance = Float
value type Duration = Int

def distance = ("Tampa", "Miami", ^Distance[279.9])
def distance = ("Miami", "Atlanta", ^Distance[662.6])

def travel_time = ("Tampa", "Miami", ^Duration[5])
def travel_time = ("Miami", "Atlanta", ^Duration[11])``````

In this example, the partial application `^Distance[279.9]` evaluates to the value of type `Distance` that corresponds to the number `279.9`. Given the use of value types, the relation `distance` is now represented as follows:

``def output = distance``
``````value type Distance = Float
value type Duration = Int

def distance = ("Tampa", "Miami", ^Distance[279.9])
def distance = ("Miami", "Atlanta", ^Distance[662.6])

def travel_time = ("Tampa", "Miami", ^Duration[5])
def travel_time = ("Miami", "Atlanta", ^Duration[11])
def output = distance``````

Output:

The third column now contains values of type `Distance`.

Value types need not be parameterized by individual values; they can be parameterized by tuples. For example, here is a value type `Point3d` that represents a point in a three dimensional space:

``value type Point3d = Float, Float, Float``

Value types also support expressions in their declaration. For example, here is a value type `Age`, which is meant to represent a person's age:

``````value type Age(x in Int) { 0 <= x and x <= 120 }

def output = ^Age[15] ; ^Age[150]``````

Output:

Value types don't necessarily represent infinite relations as we saw with the `Distance` relation above.

As an example, consider the following two value type definitions. The first specifies the days of the week, while the second defines a boolean value type which is parameterized by an 8-bit integer constrained to be either 0 or 1:

``````value type Weekday = 1; 2; 3; 4; 5; 6; 7

value type Bool(x) = Int[8](x) and (x = 0 or x = 1)``````

Additionally, a value type does not necessarily need a representation or an underlying type. For example, the following declaration specifies a value type called `None` that has the empty constructor relation `^None`.

``````value type None

def none = ^None[]

def output:tuple = 1, none, "str"
def output:mul = none * 10``````

Output:

Value types can also be parameterized by other value types. Here is an example that first defines the value type `DistanceUnit` with distance units and then defines another value type `Distance` that uses `DistanceUnit`.

``````value type DistanceUnit = :Meters; :Miles
value type Distance = DistanceUnit, Int

def output = ^Distance[^DistanceUnit[:Meters], 10]``````

Output:

Defining value types that build upon other value types allows for greater expressivity and clarity in the applications being created.

## Performing Operations Using Value Types

The definitions presented so far show how to describe different types and construct data that adhere to those value types.

In order to use and perform operations on the data of a given value type, the associated data need to be extracted to perform the operation accordingly.

Consider again one of the first examples using the `Distance` value type:

``````value type Distance = Float
value type Duration = Int

def distance = ("Tampa", "Miami", ^Distance[279.9])
def distance = ("Miami", "Atlanta", ^Distance[662.6])``````

In order to find the distance from Tampa to Atlanta through Miami, the two distances need to be added together. This operation can be performed by extracting the two floating point numbers from `^Distance`:

``````def float_from_distance(d in Distance, x) = ^Distance(x, d)

def output {
float_from_distance[distance["Tampa", "Miami"]]
+ float_from_distance[distance["Miami", "Atlanta"]]
}``````
``````value type Distance = Float
value type Duration = Int

def distance = ("Tampa", "Miami", ^Distance[279.9])
def distance = ("Miami", "Atlanta", ^Distance[662.6])
def float_from_distance(d in Distance, x) = ^Distance(x, d)

def output {
float_from_distance[distance["Tampa", "Miami"]]
+ float_from_distance[distance["Miami", "Atlanta"]]
}``````

Output:

We are also excited to announce support for operators over value types. Specifically, the same addition operation used over the `Distance` value type can also be defined as an operator.

Operators can be applied over data of specific value types to perform computations depending on need. Here is the same example where an addition `+` operator is defined:

``````def (+)[x in Distance, y in Distance] =
^Distance[float_from_distance[x] + float_from_distance[y]]

def output = distance["Tampa", "Miami"] + distance["Miami", "Atlanta"]``````
``````value type Distance = Float
value type Duration = Int

def distance = ("Tampa", "Miami", ^Distance[279.9])
def distance = ("Miami", "Atlanta", ^Distance[662.6])
def float_from_distance(d in Distance, x) = ^Distance(x, d)
def (+)[x in Distance, y in Distance] =
^Distance[float_from_distance[x] + float_from_distance[y]]

def output = distance["Tampa", "Miami"] + distance["Miami", "Atlanta"]``````

Output:

In summary, value types help distinguish between different kinds of values, even though the underlying data may be identical. Value types can be used to define other value types.

For more details please see the Value Types Concept Guide.