Egel has an atypical semantics, one of the most glaring differences with other languages is that combinators are not restricted to one arity. Evaluating an Egel script, therefore, in anthropomorphic terms, boils down to repeatedly asking any combinator: "Do you feel like rewriting yet?"
So far, the Egel interpreter employed a wasteful braindead scheme where every term node was treated like a potential redex, and a thunk was set up for that node. That works well since you cannot reduce a redex twice, but -of course- explodes an Egel script's bytecode and running time.
But it was a robust scheme, so I left it for a long while since there were more pressing issues.
Let's speed stuff up! For that, the aim is to only build thunks for potential redexes. It will be faster but at the same time, due to a lack of metainformation, there's no way around asking combinators whether they will reduce more often than is usual in other languages. Egel will remain sluggish in that regard.
A few rules: A combinator encodes a pattern-matching abstraction that will rewrite a graph to another graph where the latter is built from a combination of variable, constant, and combinator applications. It is useful to see any application as a potential redex, and we call a term in a head-redex position when it's the first member of an application tuple.
- The to-be-reduced combinator, on entry, is responsible for first determining the contents of a thunk.
- Every root node must be treated specially since it may inherit spurious arguments of that thunk.
- Pattern matching will break down a part of the thunk and bind variables.
- A constant will never rewrite, so should never build a thunk, whether it's in a head-redex position or not.
- A variable binds to a reduced term, since Egel has eager semantics, and should build a thunk only when it's in a head-redex position.
- A combinator should always build a thunk since it may always be rewritten.