namespace F
{
template <typename T1, ..., typename Tn>
struct Union
{
...
template <typename U> Union(const U &x);
template <typename U> operator const U&() const;
template <typename U> constexpr unsigned index();
};
template <typename T1, ..., typename Tn>
pure unsigned index(Union<T1, ..., Tn> u);
}
|
A discriminated union between n child types.
A Union object can be constructed from an object of any child type using an up-cast, e.g.:
Ti x = ...;
auto u = (Union<T1, ..., Tn>)x;
This will create a copy of x and assumes Ti is
copy constructible.
The child object can be retrieved via a downcast, e.g.:
auto &y = (Ti &)u;
auto y = (Ti)u; // Alternative
Note that it is undefined (nullptr dereference) if u
was not constructed from a Ti object.
The type of a Union object can be retrieved at runtime via the index function, e.g.:
switch(index(u)) { ... }
The index function returns 0 if u is the
first child type, 1 for the second child type, etc.
The index of a child type can be determined via the static function:
Union<T1, ..., Tn>::index<Ti>() = i-1
A Union object is compact, i.e.
sizeof(Union<T1, ..., Tn>) = sizeof(void *)
This means that Union objects can be passed-by-copy without
performance loss.
A Union object is implemented internally as tagged pointers,
and can support up to 16 child types.
See the documentation on value functions for more information.
Example 1We can use a Union to define a simple Maybe type as follows:
struct Nothing { };
template <typename T>
using Maybe = F::Union<Nothing, T>;
#define NOTHING Maybe<void>::index<Nothing>()
#define JUST Maybe<void>::index<void>()
This is analogous to the Maybe type from Haskell.
We can define Maybe comparison as follows:
template <typename T>
int compare(Maybe<T> x, Maybe<T> y)
{
switch (index(x))
{
case NOTHING:
switch (index(y))
{
case NOTHING:
return 0;
case JUST:
return 1;
}
case JUST:
switch (index(y))
{
case NOTHING:
return -1;
case JUST:
return compare((T)x, (T)y);
}
}
}
Example 2
We can define a List type as follows:
template <typename T> struct ListNode;
struct ListEmpty;
template <typename T>
using List = F::Union<ListNode<T>, ListEmpty>;
template <typename T>
struct ListNode
{
T elem;
List<T> next;
};
#define LIST_EMPTY List<void>::index<ListEmpty>()
#define LIST_NODE List<void>::index<ListNode<void>>()
We can calculate the length of a list as follows:
template <typename T>
int length(List<T> xs)
{
int len = 0;
while (true)
{
switch (index(xs))
{
case LIST_EMPTY:
return len;
case LIST_NODE:
len++;
auto &node = (ListNode &)xs;
xs = node.next;
}
}
}
namespace F
{
template <typename T1, ..., typename Tn>
struct Result
{
...
};
}
|
A specialized tuple of objects. Unlike the related Tuple type, a Result can be used to return multiple values from a function without relying on heap allocated memory. Furthermore, Result can be matched using C++17's structure binding feature:
Result<float, float, float> get_point_3D(...);
auto [x, y, z] = get_point_3D(...);
This provides a convenient way to extract multiple return values from a
function call.
An object of type Result is not necessarily compact.
namespace F
{
template <typename T1, ..., typename Tn>
struct Tuple
{
...
};
}
|
A tuple of objects. Unlike the related Result type, a a Tuple is a compact object and relies on heap allocation.
See the documentation on tuple functions for more information.
namespace F
{
template <typename T>
struct Optional
{
...
};
}
|
An optional object that may or may not hold the underlying value. Unlike the related Maybe type, an Optional is not a compact object.
See the documentation on value functions for more information.
namespace F
{
struct Nothing
{
// Empty
};
template <typename T>
using Maybe = Union<Nothing, T>;
enum
{
NOTHING = Maybe<void>::index<Nothing>(),
JUST = Maybe<void>::index<void>()
};
}
|
A maybe
type that may or may not hold a value.
Unlike the related Optional type, a
Maybe is a compact object and is more likely to rely on
heap allocation.
See the documentation on maybe functions for more information.
namespace F
{
struct Nil
{
// Empty
};
template <typename T>
struct Node;
template <typename T>
using List = Union<Nil, Node<T>>;
template <typename T>
struct Node
{
Value<T> elem;
List<T> next;
};
enum
{
NIL = List<void>::index<Nil>(),
NODE = List<void>::index<Node<void>>()
};
}
|
A (singularly-linked) list type. See the documentation on list functions for more information.
namespace F
{
template <typename K, typename V>
struct Map
{
...
};
}
|
A key-value map type. See the documentation on map functions for more information.
namespace F
{
template <typename T>
struct Set
{
...
};
}
|
A set type. See the documentation on set functions for more information.
namespace F
{
template <typename T>
struct Vector
{
...
};
}
|
A vector type. See the documentation on vector functions for more information.
namespace F
{
struct String
{
...
};
}
|
A unicode string type. See the documentation on string functions for more information.