I was asked in another thread to comment on OP_DEFINE / OP_INVOKE vs OP_EVAL so will respond here.
In general I think the evaluation feature is an immensely valuable addition to the bytecode language. It brings functions and recursion which are essential building blocks. Together with loops it completes the key features of a simple but expressive language. And these features are fully constrained by the VM sandbox and VM limits. After adding OP_EVAL to albaVm it became simple to implement functions such as exponentiation, merge sort, and basic elliptic curve multiplication.
I prefer OP_EVAL (possibly combined with OP_PUSH_EXECUTABLE) over OP_DEFINE / OP_INVOKE. There is a deeper level fundamental difference between these two solutions for function evaluation even though they look similar. Basically OP_EVAL fits into our current stack-based model of computation whereas OP_DEFINE / OP_INVOKE extends it by introducing global state. (Jason also brings up this topic in his CHIP). The global state is assign-once, but even so does change things around a bit. A couple of examples:
1)
With OP_DEFINE / OP_INVOKE, expressions involving function definitions are no longer self contained. You can paste the following expression involving the pow function (recursively implemented using OP_EVAL) anywhere into your own code where an integer is expected and it should work (e.g. in bitauth IDE 2026):
... (your code)
<2>
<16>
<0x3178009c635177777767785297009c63527952795296527976627695777777675279537953795194537976629577777768687662>
OP_EVAL
... (your code)
Had it been implemented using OP_DEFINE / OP_INVOKE, then the above evaluation would fail in case pow used a function slot that was already occupied. This makes interactive bitauth/REPL use more complicated and is as far as I can see a divergence from what we have today.
This also has implications for example when sharing libraries of compiled functions between tools. Now we need a linker to patch up function definitions from separately compiled modules so that their function slot usage does not overlap, instead of just bringing them in as is.
2)
OP_DEFINE / OP_INVOKE allows the function table to be used as a global assign-once array. A value can be assigned to a slot in the table by assembling a function that returns the value and OP_DEFINEing it to that slot. Two expressions at separate places in a program may have an agreement to pass data via function slot x. This way an expression is no longer only a function of its arguments on the stack, but also has access to global state calculated somewhere else in the program.
My sense is that we should continue to keep the bytecode language purely stack based and not also introduce global state. If we want to explicitly call out all “lambda creation sites” then I prefer the eval-bit suggestion by @bitcoincashautist/ @im_uname ( https://github.com/bitjson/bch-functions/issues/2). Although, currently my overall preference is to just have OP_EVAL on its own.