Skip to content

Clone types

tim-hardcastle edited this page Oct 28, 2024 · 3 revisions

Besides the other user-defined types, it can be useful to have concrete types which resemble, but are distinct from, the built-in types. If, for example, we use an integer to represent a UID, then it is convenient to declare a UID type which can be distinguished from other integers.

newtype UID = clone int

apples = clone int
oranges = clone int

Fruit = abstract apples/oranges

Cloneable types are float, int, list, map, pair, set, and string. Every clone, together with its parent, belongs to an abstract type called <base type>like, e.g. intlike, maplike.

The elements of the type are constructed as you would expect: Uid 5; the int and float types are also supplied with suffix constructors so you can write e.g. 5 apples. To have it styled like that, simply overload the builtin string function:

string(x fruit) :
	string(x) + " " + string(type x)

It is acceptable to use lowercase for clone types used in this way as units.

We can convert back to the parent type by using its name as a conversion function: int(5 apples) will return 5.

Other converters should be added by hand as needed, e.g. apples "5" or apples(5 oranges) will fail.

The cast function will cast any clone to its parent, parent to a child, or clone to another clone with the same parent: cast 5 apples, int returns 5.

Such clones often need not have the full range of built-in functions and operators, and if they need not, they should not. You would never want to add two UIDs together, let alone multiply them. For this reason, by default the clone types are supplied without certain built-in operations, which can however be had by request:

newtype

apples = clone int using +, -
oranges = clone int using +, -

fruit = abstract apples/oranges

We can now add and subtract apples and apples; and oranges and oranges. Though not, of course, apples and oranges.

Some operations, such as len, don't need to be requested in this way; others, such as +, do. The underlying rule is that an operation must be requested if it would be expected to return a value in the clone type. Hence the operations that need requesting are +, -, *, /, %,with, without, the operators >> and ?> for lists, and slicing clones of strings and lists. (Request the slicing operation with the word slice, the other operations by their names and symbols.)

An operation which you don't request may of course still be overloaded. This allows you to do fancy math things like this:

newtype

Vec = clone list

def

(v Vec) + (w Vec) :
    Vec from z = [] for i::x = range v :
        z + [x + w[i]]

Mostly however we anticipate that this feature will be used simply to distinguish inert pieces of data on which we don't want to perform operations: a username, a SKU, a web address, each of which might be distinguished as a different clone of the string type.

Clone this wiki locally