🖊️
Metalang99
  • Introduction
  • Syntax and semantics
  • Partial application and function composition
  • Data model
  • Testing, debugging, and error reporting
  • Final words
Powered by GitBook
On this page
  • Variadics
  • Tuple
  • Choice
  • Cons-list
  • Sequence

Was this helpful?

Data model

PreviousPartial application and function compositionNextTesting, debugging, and error reporting

Last updated 5 months ago

Was this helpful?

Metalang99 provides four different types of collections, as well as a means for defining and dealing with compound data types. (Type checking is not performed due to performance reasons.)

Variadics

Representation: x, y, z, ....

Documentation: .

Variadics are a comma-separated list of preprocessor tokens. Most of the time, a user will provide variadic arguments to your macro so that you could manipulate them as you want:

[]

#include <metalang99.h>

#include <assert.h>

#define ASSERT_FOR_EACH(...)                                                                       \
    do {                                                                                           \
        ML99_EVAL(ML99_variadicsForEach(                                                           \
            ML99_compose(v(ML99_semicoloned), ML99_reify(v(assert))),                              \
            v(__VA_ARGS__)))                                                                       \
    } while (0)

int main(void) {
    ASSERT_FOR_EACH(123 == 123, 2 + 2 == 4, "foo"[1] == 'o');
}

Tuple

Representation: (x, y, z, ...).

A tuple is formed by putting variadic arguments into parentheses. Besides a collection-like usage, tuples can simulate C structures; the common pattern is to define a tuple constructor (rect below) and accessors of the corresponding fields (rectWidth and rectHeight):

// Computes the area of a rectangle.

#include <metalang99.h>

#define rect(width, height) ML99_tuple(width, height)
#define rectWidth           ML99_tupleGet(0)
#define rectHeight          ML99_tupleGet(1)

#define rectArea(rect) ML99_mul(rectWidth(rect), rectHeight(rect))

/*
 *                15
 * +------------------------------+
 * |                              |
 * |                              |
 * |                              | 7
 * |                              |
 * |                              |
 * +------------------------------+
 */
#define RECTANGLE rect(v(15), v(7))

ML99_ASSERT_EQ(rectArea(RECTANGLE), v(15 * 7));

int main(void) {}

Choice

Representation: (tag, ...).

A choice type encodes a set of alternatives; each alternative can be constructed by a corresponding value constructor and be inspected by the means of pattern matching:

// Sums all nodes of a binary tree, recursively.

#include <metalang99.h>

#define leaf(x)              ML99_choice(v(leaf), x)
#define node(lhs, data, rhs) ML99_choice(v(node), lhs, data, rhs)

#define sumTree(tree)                     ML99_match(tree, v(sumTree_))
#define sumTree_leaf_IMPL(x)              v(x)
#define sumTree_node_IMPL(lhs, data, rhs) ML99_add3(sumTree(v(lhs)), v(data), sumTree(v(rhs)))

/*
 *         4
 *        / \
 *       /   \
 *      /     \
 *     2       6
 *    / \     / \
 *   1   3   5   7
 */
#define TREE node(node(leaf(v(1)), v(2), leaf(v(3))), v(4), node(leaf(v(5)), v(6), leaf(v(7))))

ML99_ASSERT_EQ(sumTree(TREE), v(28));

int main(void) {}

Cons-list

Representation: choice type.

#include <metalang99.h>

// 3, 3, 3, 3, 3
static int five_threes[] = {
    ML99_LIST_EVAL_COMMA_SEP(ML99_listReplicate(v(5), v(3))),
};

// 5, 4, 3, 2, 1
static int from_5_to_1[] = {
    ML99_LIST_EVAL_COMMA_SEP(ML99_listReverse(ML99_list(v(1, 2, 3, 4, 5)))),
};

// 9, 2, 5
static int lesser_than_10[] = {
    ML99_LIST_EVAL_COMMA_SEP(
        ML99_listFilter(ML99_appl(v(ML99_greater), v(10)), ML99_list(v(9, 2, 11, 13, 5)))),
};

Sequence

Representation: (x)(y)(z) ....

#define Shape_IFACE                      \
    vfunc( int, perim, const VSelf)      \
    vfunc(void, scale, VSelf, int factor)

interface(Shape);

With vfunc being defined as follows (simplified):

#define vfunc(ret_ty, name, ...) (ret_ty, name, __VA_ARGS__)

Here, ASSERT_FOR_EACH accepts variadic arguments, which is communicated by its signature. Internally, it calls to iterate through each argument, resulting in a number of assert(...); statements instead of one assert(a && b && ...); (which makes debugging easier because failed assertions will not be collapsed with each other).

Documentation: .

[]

In type theory, tuples are known as .

Documentation: .

[]

In type theory, choice types are usually referred to as .

Documentation: .

A is represented as a choice type with two alternatives: an empty list ML99_nil() and a list constructor ML99_cons(x, xs). Cons-list is the most powerful collection:

Although cons-lists provide many more functions than any other collection, they are also more time and space-consuming. If you can deal with a native representation directly (such as variadics, tuples, or sequences), you should do so. Most of the time, a user will provide variadic arguments to your macro, and the only appropriate operation will be for-each loop (such as ).

However, if you still miss a certain function for native collections, you can always convert your representation to a cons-list via , , etc.

Documentation: .

A sequence is yet another fast native collection. A perfect example of using sequences is , which allows us to define a software interface like this:

Thus, a number of vfunc invocations forms a complete sequence. Later, Interface99 works with this sequence via and a few other helpful macros.

variadics.h
examples/assert_for_each.c
ML99_variadicsForEach
tuple.h
examples/rectangle.c
product types
choice.h
examples/binary_tree.c
sum types
list.h
cons-list
ML99_variadicsForEach
ML99_list
ML99_listFromSeq
seq.h
Interface99
ML99_seqForEach