fail "names must be non NULL and a list of strings";
// Now ensure that tpl is not NULL
tpl != NULL || fail "tpl must not be NULL";
let name = mod.names.0;
let fail_msg = "Attempt to descend into a non existent name @" % (name);
// Field selector function that throws a compile error if the field
// doesn't exist.
let select_field = func(name) => select (name in mod.tpl), fail fail_message {
true = mod.tpl.(name),
};
// calculate our next value.
// If there are no more names then something has gone wrong and
// we should hard fail.
let next = select l.len(names) > 0, mod.tpl, fail "Impossible" {
true = select_field(name),
};
// we need to get the tail of our list.
let tail = l.tail(names);
// if the tail is non empty then recurse down.
// If the tail is empty then return the current value.
let result = select l.len(tail) > 0, next, {
// recurse
true = self{tpl=result, names=tail},
};
};
```
Our `deep_index` module can now descend down into a tuple given a list of field
names. If any of the names does not exist it will be a compile failure.
## Final notes
One consquence of this method of doing recursion is that you can not reference
the module in the file it is defined in. Doing so would result in a compile
error as UCG detects an import loop. In practice this does not prove to be a
problem since nearly all usages of the module will be outside of the file it is
defined in.
<hr>
[^1]: Technnically functions can do recursion if you manually pass in the same function as an argument to itself, but this is unwieldy and error-prone.