Structures
Users can define custom types in Quirl, so-called structures. A structure is basically a named tuple with constraints on its elements. Possible constraints are the same as for function parameters: you can restrict a structure's elements by their number, by their type, to specific values or by arbitrary conditions.
This page assumes that you are already familiar with the basics of Quirl as outlined in the tutorial.
Defining a Structure
The definition of a structure is similiar to a function definition. The equals sign serves as definition operator. The left side starts with the name of the structure, directly followed by a paramter list. A structure's parameter list is wrapped in square brackets. Here is an example:
Pair[? ?] = ;
The statement defines a structure called Pair that can hold two arbitrary items. Note that the right side of the definition can be empty. Unlike a function, a structure doesn't do computations on its own. It just accepts and holds elements. Some valid values of this structure would be these:
Pair[7, -3],
Pair[1/2t "half turn"],
Pair[Pair["Tom", "Jerry"] Pair["Hopps", "Wilde"]]
Accessing Elements
Values of a structure are automatically converted to a regular tuple when necessary. Hence you can pass structures to any function that expects a tuple. In particular, you can peel a structure to extract its elements. Other tuple-related functions like peek can be applied, too. Here are two examples:
peel(Pair[1/2t "half turn"]); # 1/2t "half turn"
peek(Pair[Pair["Tom", "Jerry"] Pair["Hopps", "Wilde"]], 1, 2); # "Jerry"
Type Parameters
The Pair structure above accepts arbitrary elements. Let us restrict the values of a structure further now. The following statement defines a Point in the plane as a pair of rational coordinates.
Point[Rat Rat] = ;
After this definition, Quirl understands points like these:
Point[0, 0]
Point[7, -3]
Point[3.6, 1/2] # same as Point[18/5, 1/2]
But it would not accept a text like "Hopps" or "Wilde" as coordinate of a point.
Functions on Structures
Having custom data types is okay, but we also want to be able to do something with them, don't we? So let us define functions on structures! centroid, for example, computes the midpoint between two points – or a middle point between any positive number of points actually:
centroid(Point ...Point) = Point[zip(avg $a $b)];
This uses the higher-order function zip with the avg function to compute the average first coordinates and the average second coordinates of the given points and creates a new point from these. Here are example calculations:
centroid(Point[-1, 0] Point[3, 3]); # Point[1, 3/2]
centroid(Point[3.6, 1/2] Point[0, 0]); # Point[9/5, 1/4]
centroid(Point[9 1] Point[5 0] Point[2 6]); # Point[16/3, 7/3]
Let's also compute the distance covered on a travel from point to point. We define the function distance in two pieces. The first mapping is for the distance between two points. The second mapping extends the definition to three or more points by summing all straight line segments between consecutive points.
distance(Point Point) = sqrt@sum@zip(x^2@sub $a $b);
distance(Point Point Point ...Point) = sum@link(distance $a $b $c $d);
Here are some example calculations:
distance(Point[-1, 0] Point[3, 3]); # 5
distance(Point[3.6, 1/2] Point[0, 0]); # 1/10\1321
distance(Point[9 1] Point[5 0] Point[2 6]); # 3\5+\17
Conditions
Typed parameters are not always specific enough. Imagine we want to store colors as 8-bit red, green and blue channels. 8 bits can hold an integer from 0 to 255. We can define a structure and restrict its parameters to that range with conditions:
RGB[Int Int Int]: inq(0 $a 255): inq(0 $b 255): inq(0 $c 255) = ;
A condition always starts with a colon : and must evaluate to a boolean value, T for true or F for false. The inq function fulfills that requirement.
Branding
Some mathematical objects share many properties so that classifying them as values of a common structure makes sense – despite only some of them being compatible with each other. Consider vectors for example. Vectors have a length and direction; they can be scaled, added and subtracted.
However, operations that involve two vectors like addition and subtraction require both vectors to have equally many coordinates. We can't simply add a two-dimensional to a three-dimensional vector. This is modelled in Quirl by branding. Quirl only recognizes values of the same structure as compatible, if their brands are compatible.
The brand, or a recipe for a brand, is placed at the right side of the definition operator = when a structure is created. The following statement defines complex vectors with their coordinate count as brand:
Vector[...Complex] = count($a);
Let's also define addition of vectors:
add(Vector Vector) = Vector[zip(add $a $b)];
If you try the following two calls afterwards, you'll see that the first one works. The second one causes an exception though, because addition got only defined for vectors of the same brand.
add(Vector[0.5 i] Vector[1 2]); # works
add(Vector[0.5 i] Vector[1 2 3]); # not defined due to incompatible brands!
Multiple Definitions and Recursion
A data structure that saves arbitrary items ? in a binary tree could be defined as follows:
Tree[? Tree Tree] = ;
Tree[] = ;
As you can see, structures can have multiple definitions that complement each other. The first statement in the example defines a tree as a strucuture that contains two sub-trees. That is self-referential, recursive, and allows trees to branch out into more and more sub-trees.
The second definition defines an empty tree – a stump, if you will. It is needed to cut the recursive branching process off somewhere. Otherwise the tree would have to be infinitely big. Here is an example that encodes a pedigree chart in the Tree structure:
Tree[
"Wotan"
Tree[
"Odin"
Tree["Sabre" Tree[] Tree[]],
Tree["Romance" Tree[] Tree[]]
], Tree[
"Fina"
Tree["Benno" Tree[] Tree[]],
Tree["Elvira" Tree[] Tree[]]
]
];
Note that Quirl only supports direct recursion in the definition of structures. Indirect recursion where two structures require each other as parameter types is not yet supported.