Actually there's a much simpler metastasis function, perhaps the most evil function of all, namely, the result of applying the Y combinator to the Y combinator itself: Y Y
If you expand this function two steps, you'll notice that it equals: Y Y (Y (Y Y))
And so you end up with an infinitely left-recursive thing from hell.Fortunately my implementation of Fexl does not use built-in recursion, which would quickly throw up a segmentation fault due to stack overflow. Fexl does its own stack with a chain of nodes. Also, Fexl runs inside a completely bounded "arena", which is a fixed-size array of machine integers. All node "pointers" are actually just integer offsets into this arena. This makes it possible to increase the size of the arena by allocating a brand new one and doing a mass copy from the old to the new. But I have not bothered with this yet, because I figure that deciding on your upper bound on memory once up front is just fine for now. Note also that my approach allows for nested arenas, so for example you could have a Fexl function which allocates an arena and runs a Fexl interpreter inside there. The whole thing is completely reflective in the most profound way you could imagine, so the sky's the limit. The use of pure functions enables abstractions which are not leaky. This leads me to my main point. I've decided to stick with the C implementation because it gives me the complete control which I need. When I want to call Fexl from Perl, I will simply fork a separate Fexl process and have Perl pump a Fexl function into its standard input. That initial function can then read any remaining input. You can do any sort of bootstrapping this way, for example the initial function may expect another Fexl function at the head of the remaining input. Any file names on the Fexl command line would simply be treated as if their contents had appeared on standard input first. The Perl code then reads the standard output of the Fexl process, and that's its answer. On the web, it could connect the output directly to the client socket and be done with it. One nice thing about this approach is that it's utterly safe. Your code could read the evil (Y Y) function straight off a socket from a known black hat and evaluate it with confidence, knowing that it would simply reach your upper bound on memory -- or cycles, whichever comes first -- and halt in the most ordinary way. Another nice thing is that I don't have support an endless list of implementations in other languages, or even bindings for other languages. There is one authoritative piece of code written in C and that's that. If you feel that this approach is too slow, then you can simply move more of your logic from Perl into the Fexl program itself. If you take this principle to its extreme, you would end up with a Perl process which does nothing except feed a program into Fexl -- at which point you would drop Perl altogether. Note that Fexl programs can be extremely compact, and you could even read auxiliary functions from files on demand. Ultimately the entire API at https://loom.cc will be based on Fexl. Instead of doing a bunch of grungy back-and-forth API calls and doing all your branching and looping on the client side, you can just ship an entire Fexl program to the Loom server and have it run there, delivering precisely the answer you want in the format you want. You could even send back a single number such as "42" if that's all you need. To avoid shipping the same program to Loom repeatedly, you could store the program in an Archive slot once. Then whenever you want to run that program, you only have to send the archive ID. |
I think a C implementation would be better anyway. C is the common denominator between almost every language. Have you already done the foreign function interface to C?
When do you think we might see either source code or a live web demo to play with?
For a web demo, couldn't you just add a quick CGI interface to your C program?