Showing posts with label Silly buggers. Show all posts
Showing posts with label Silly buggers. Show all posts

Wednesday, 29 August 2007

Fakin' closures

Closures are awesome. There are so many algorithms that can be implemented in a very straightforward manner once you have the ability to make little customised functions and pass them around.

For example, it makes functional programming in D a snap. Here's a simple example of what I mean:

string format_list(real[] parts, string sep)
{
    string reduce_closure(string left, string right)
    {
        return left ~ sep ~ right;
    }

    string map_closure(real n)
    {
        return toString(n);
    }

    return reduce(&reduce_closure, map(&map_closure, parts));
}

The one failing of D's closures are that... they aren't really closures. See, D's closures are made up of a function pointer, and a pointer to the enclosing function's stack pointer. The stack pointer is what allows you to actually create closures, but it's also why you must not use them outside the enclosing scope's lifetime. As soon as your closure goes out of scope, it's bye-bye stack frame, and hello Mr. Segmentation Fault if you should try to call it. Oh dear.

In practice, this can be worked around by putting your closure inside a class and heap allocating it, but that's annoying. Wouldn't it be nice to be able to just return the damn things out of your functions, or store them somewhere for later use?

If you're easily put off my horrible, hackish code: you'd best leave now. It's going to get nasty.

Remember how I said that closures are made up of a pointer to some code and the enclosing function's stack pointer? Well, on x86 the stack grows downwards, and a function's arguments are stored just above the stack pointer with its local variables below the stack pointer.

Say we have a function foo, with a closure bar, and another function baz.

If we call baz from foo, passing our closure bar, then baz would know two important things:

  1. It would have an upper-bound on foo's local variables by virtue of knowing its stack pointer (which is contained in the delegate to bar) and

  2. it would have a lower-bound based on the address of its own first argument on the stack.

So if we have both a lower and upper bound, we can create a heap copy of foo's local variables, and store that in our delegate instead of the pointer to the stack.

Just to prove the concept, here's a working implementation:

module closures;

import std.stdio;

/**
 * Takes a closure defined in the caller, copies the local variables onto the
 * heap, and returns a modified delegate.
 */
dgT closure(dgT)(dgT dg)
{
    void* end_ptr = dg.ptr;
    void* start_ptr = &dg;

    auto context = new ubyte[end_ptr-start_ptr+1];
    context[] = cast(ubyte[]) start_ptr[0..context.length];

    dg.ptr = &context[$-1];
    return dg;
}

/**
 * Creates a closure that sets *ptr to value when called.
 */
void delegate() make_dg(uint* ptr_, uint value_)
{
    auto ptr = ptr_;
    auto value = value_;

    void dg()
    {
        *ptr = value;
    }

    return closure(&dg);
}

void main()
{
    uint f;

    auto fn1 = make_dg(&f, 42);
    auto fn2 = make_dg(&f, 1701);

    f = 15;
    writefln("f: %s", f);
    fn1();
    writefln("f: %s", f);
    fn2();
    writefln("f: %s", f);
}

When run, it prints:

f: 15
f: 42
f: 1701

Just like one would expect. As I mentioned before, this only works if your closure doesn't access the enclosing function's arguments. But apart from that, it's a pretty neat trick, don'tcha think?

Well, hey, let's just make everything into a closure, and then we'll have our general garbage collector, installed by 'use less memory'. —Larry Wall

Tuesday, 19 June 2007

Const Wars

A long time ago, in an IRC channel far, far away...

Episode 5

A NEW CONST

It is a period of civil war. Rebel posters, striking from irc and the newsgroups, strive to win their final victory against the evil Constant Empire.

During this battle, Rebel agents were horrified to discover that the Empire's ultimate weapon, the D 2.0 Release, a new compiler with three kinds of const, had been released.

Pursued by the Empire's sinister agents, Princess Mutable races against the clock to find a way to destroy the terrible compiler to save her people and restore mutability to the galaxy...

Episode 5

A NEW CONST

It is a period of civil war. Rebel posters, striking from irc and the newsgroups, strive to win their final victory against the evil Mutable Empire.

During this battle, Rebel agents were horrified to discover that the Rebel's ultimate weapon, the D 2.0 Release, a new compiler with three kinds of const, was feared and hated.

Pursued by the Empire's sinister agents, Princess Invariant races against the clock to find a way to justify the badly needed compiler to save her people and restore type safety to the galaxy...

That's the wonderful thing about religion; you can use it to justify anything, with no requirement for facts or logic!

Thursday, 7 June 2007

Incidentally...

... while(nan) doesn't compile since nan isn't defined. Technically, it should be while(real.nan), but that was a bit unwieldy.

As for what it does, it turns out that despite nan comparing false to everything including itself, it's effectively the same as while(true). Go figure.