Prexonite Script

a .NET hosted scripting language with a focus on meta-programming and embedded DSLs

View the Project on GitHub SealedSun/prx

A functional touch

Posted on 2008-01-02

Over the last few days, I've been working on two things: the reorganization of built-in commands and the improvement of the "Functional Experience". Why do commands need reordering? Because it gets difficult to find the right file among over 40 commands. Why the sudden increase in numbers? I added proxies for System.Math methods for both easy and fast access to mathematical functions such as Sqrt and Sin, but also Pi.Additionally, the most important coroutines from the Prexonite Standard Repository for list processing have been implemented in managed code, again for performance reasons.

Map, Where, Limit, Skip and friends now inject managed coroutines into the stack. The commands are now organized in the namespaces Core, List, Math and Text. The latter currently contains the fixed layout functions SetCenter, SetLeft and SetRight, which fill a given string with some character sequence until it has a certain length and is aligned correctly.

Now what the hell do you mean by "Functional Experience"? I haven't told anyone but the Prexonite VM is absolutely terrible when it comes to recursion. Unfortunately, recursion happens to be one of the key elements in functional programming and, as you might have noticed, Prexonite Script comes with a lot of syntactic sugar that makes it look like a functional programming language. Ok, lambda expressions and closures are "true" functional features but the lack of a sophisticated type system makes it almost impossible to reason about a program in the way functional compilers do.

Nonetheless, I added two features with the last commit, that make PXS a tiny little bit more functional.

First of all: Tail Call Optimization

Yes, the thing that helps with recursion.

function fac(n,r) =
if(n == 1)
  r
else
  fac(n-1,n*r);

I benchmarked this function three times, with different tail call optimization strategies. The difference is huge. See for yourself (10'000 computations of 16!):

Comparison of different tail call optimization strategies.

Two strategies are employed: An implementation of tail call optimization for directly recursive functions inside the compiler, that turns recursive calls into direct iterations (jumps to the beginning of the function with different arguments).

What I call "virtual machine optimization" is a special tail call instruction that removes the current stack frame after having called the function or closure. Now apparently the virtual machine "optimization" is not particularly fast but uses far less memory than the normal invocation. Prexonite will never be able to recognize indirect recursion due to the lack of control flow analysis. This, however, does not mean that return statements inside conditions or calls in tail position are not recognized.

I'm not sure if Prexonite will ever handle simple recursive return expressions like the normal definition of the factorial:

function fac n =
if (n==1)
  1
else
  n*fac(n-1);