Over the past couple of weeks, I’ve ironed out most of the design for both the Tenerezza core language and a convenient Lisp-like macro layer.
Tenerezza is distinguished from mainstream languages in a few ways:
Tenerezza is an untyped language where all values are distinguished by sealer/unsealer tags. Even function closures are unencapsulated to someone who has the right unsealing permission.
Tenerezza’s first-class values are always sets. In fact, you can only unseal an element at the time you loop over its container because you can’t manipulate a single element directly. This builds on some existing work around doing computation on sets.
Tenerezza supports general recursion, but only by way of a computation monad (namely, returning a stack of functions to call). Otherwise if a Tenerezza computation’s set inputs are a constant size, it takes constant time. This is meant to make it easy to visualize, step through, patch, and persist the dynamic shapes of a Tenerezza computation.
Actually, Tenerezza’s first-class values aren’t merely sets. Every first-class value in Tenerezza carries an input set, but it also receives an output set. Overall, it represents a communication channel.
This actually means Tenerezza programs may have causality paradoxes as inputs loop around to become outputs and back, but these infinite loops should be about as easy to debug as the infinite loops today’s programmers are used to.
The macro layer
Although the Tenerezza language does some desugaring so programs can be written in a convenient format, the Tenerezza language is going to be pretty verbose to use directly. Possibly the biggest issue is that a Tenerezza program needs to have names denoting every one of its cheap steps, and these special-purpose, implementation-dependent names clutter the code.
I’ve designed a macro layer. Macros can locally convert custom surface syntaxes into the standard Tenerezza sugar, and Tenerezza can then do nonlocal desugaring transformations to decompose the program into cheap steps.
I’ve put together the macro layer in an extensible way, using some late binding techniques I learned making Penknife. Unlike in Penknife, this time I’m not reliant on global method tables; I’m now using an idiom where the method is a data structure, and an object is a function that takes a method as an argument. This is a pretty simple idiom, and I might revise it in various quirky OO ways to support things like
super and meta-object protocols, but I’m happy that these quirks will not pollute the global definition semantics.
Taking it from here
This is shaping up to be a small design that covers most of the bases of what I’d want for general-purpose programming. Once some further details of the design are fleshed out, such as macro hygiene and code signing, I may start implementing this and using it.
It’ll probably be slow at first since the implementation will be passing around sets, but thanks to Tenerezza programs’ decomposition into cheap steps, it should be pretty easy to profile a program and figure out which specific subprograms could benefit from optimization.