Prexonite Script

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

View the Project on GitHub SealedSun/prx

A bit of curry?

Posted on 2007-01-31

I'm talking about Currying, "the technique of transforming a function that takes multiple arguments into a function that takes a single argument…"

With two recent additions to Prexonite (Script), it has become very easy to implement higher-order functions, including "curry" and "uncurry". With "additions" I'm referring to Closures as a virtual machine feature and Lambda Expressions as a Prexonite Script expansion.

Something of the form (a1, a2, a3, ... , an) => expr is an expression that is compiled into a separate function. Instead of a value, such a construct returns a closure, an enhanced reference to that anonymous function, that acts like a normal function reference.

It get's even better: As this so called lambda expression is defined inside another function, you can use local variables as if they were part of the expression. That's also the reason why this closure-thingy is returned instead of a normal function reference: It carries information about additional local variables available to the lambda expression.

But now, let's get back to

Currying

unction curry(ref f) = a => b => f(a,b);

function uncurry(ref f) = (a, b) =>
{
    ref bc = f(a);
    return bc(b);
};

function map(ref f, lst)
{
    var nlst = new List;
    foreach(var x in lst)
        nlst.Add( f(x) );
    return nlst;
}

I admit that these functions are limited to a total number of two arguments. It's more a proof-of-concept, as it's very rare that you actually need something like this in an imperative language like Prexonite Script.

One of my unit tests uses currying to partially apply an addition to a list of integers

map( curry( (x,y) => x + y ) , ~List.Create( 2, 3, 5 ))

In this case, curry returns a function that takes the one argument (x) and itself returns a function that takes the other argument (y) and returns x + y. So the result of this expression can be written as:

var additions = ~List.Create( y => 2+y, y => 3+y, y => 5+y );

To finally get a list of integers again, you'll have to supply the missing y, either using a for (each) loop or by treating the list itself as a reference and passing one argument:

var results = additions(5);

The result is again a list. And no, this is not a case study. It's reality. (I'm sorry, I'm just a bit like "Closures! In my own programming language! Working!")