On the c-lambda page, you write:
"In a sense C is simplistic and in many ways a glorified
assembler but I actually like it."
I first learned C quite a while ago. And up until three months ago, I would have agreed with this. But then I started using assembler in a way that I'd never used it before: I had written a bunch of C functions to interface lightning and libffi with Moscow ML. And it was hard work. The CAML byte-code interpreter uses a similar sort of cell encoding scheme, folding longs and pointers together, and passing 'vectors' of pointers around, often several deep. It's really easy to make a mistake with the casts and end up de-referencing a pointer to a pointer to a string reference, say, too many or too few times.
Once I had the lightning primitives going, though, I found it got a lot easier. And to my genuine surprise I found the assembler easier to write and easier to understand than the C had been. And there was another benefit that wasn't so surprising, because I think it was one of the reasons why I was doing this: the assembler was much easier to generalise. It was almost trivial to compose assembler-generating ML functions together, and the result was that the fragments of assembler were automatically stacked up to implement, for me, quite complicated C function bindings for ML. But because the fragments of code were all being composed mechanically, they either worked first time (most of the time) or they failed every time. I have had very, very few of those bugs that show up once in a blue moon. One was, I think, a fairly predictable case of some SCM pointers that guile's GC wasn't protecting, because they were only referenced from CAML bytecode environments And another was, I think, a bug in my C code implementing off-heap malloc'ed buffers for ML functions. And there haven't been any others. But this is hundreds, if not thousands of 'lines' of assembler, because I use the same pieces of code over and over again, in different contexts.
You might be interested to try it out. I have made some examples of how to do the Guile bindings for lightning, and you won't have any problem at all generating the scheme code to implement the whole lot. There are about 350 functions to bind, so it needs to be done programmatically, but I have set up an ML list which will translate trivially to a scheme s-exp to implement the definitions. And there are some examples of lightning code to access and create SCMs here:
https://github.com/IanANGrant/red-october/blob/master/src/dynlibs/ffi/testguile.smlThe example of multiple-use starts around line 450.
If you are interested, then I would like to ask you about how or if it might be possible to use Prolog to solve some logical equations and do automatic register allocation once a whole lot of assembler fragments have been stacked up. If you try it, you will see that it is easy to assign registers as scheme parameters and fix them in the caller, but there are some compromises one has to make, which are less than 100"% efficient. And I think maybe a bit of AI would be able to make them dynamically and according to context, which is definitely not what we would want to do manually, because it would destroy the simplicity and reliability of function composition for 'stacking fragments.' The manually specified steps have to be very uniform and regular, because we want the results to be _very_ reliable.
Happy hacking.