Tenet language
If you’ve dug up some old scripts you wrote years ago, you know that feeling of being an archaeologist carefully trying to piece together what this strange person was writing in an alien language.
Tenet is a high-level language that tries to address that problem, while still being an approachable scripting language that lets you get stuff done.
Clarity in types and values
Tenet bakes clarity deep into the language. Types are homoiconic, meaning they look like values, and named arguments are standard.
// We could include this inline, but we'll create
// a type name for convenience.
type Triple = (a: Int, b: Int, c: Int);
// Find a Pythagorean triple using the classic Euclidean method.
fun pythagorean-triple(m: Int, n: Int) : ok~Triple | #bad-args {
// Ensure n > 0 and m > n
if n le 0 or m le n { return #bad-args; }
// Construct our return value; this constructor looks
// like the type.
return ok~(a: m * m - n * n, b: 2 * m * n, c: m * m + n * n);
}
do {
// A `when` statement is like a switch.
// We accept the result, and handle the possibilities.
when pythagorean-triple(m: 4, n: 3) {
// This captures elements of the result in names,
// and, again, this pattern looks like the type.
ok~(a: let a, b: let b, c: let c) ->
print-line("(" ++ int-to-str(a) ++ ", " ++
int-to-str(b) ++ ", " ++ int-to-str(c) ++ ")");
#bad-args -> print-line("Invalid argument");
}
}
Flexible return types
In this example, we have a function to classify a word as a palindrome or an anadrome. It returns a union indicating a palindrome or anadrome. Our top-level code is interested in compiling all the anadromes.
This illustrates a common situation facing function authors: it’s unclear which result is erroneous or exceptional and which is correct or expected. This is especially true when one person is writing a library that another person wants to use. Tenet simplifies this communication by not having hierarchies of errors or exceptions that results must be shoehorned into, rather, it asks the function author to state what it is and lets the function caller decide what to do with it.
fun palindrome(txt: Str): #palindrome | anadrome ~ Str {
let mut reversed = "";
for char in txt {
reversed = char ++ reversed;
}
return reversed == txt ? {
true -> #palindrome;
false -> anadrome ~ reversed;
};
}
do {
let mut anadromes = \[];
for word in \["101", "diaper", "rotor", "knit", "9271", "aibohphobia", "desserts"] {
when palindrome(word) {
anadrome ~ let a -> anadromes ++= \[word, a];
#palindrome -> print-line("Discard palindrome: " ++ word);
}
}
show("Anadromes: ", anadromes);
}
See our guide where we’ll write a short game to give you a better sense of Tenet as a language.