Friday 8 July 2011

Implementation Details

Haven't posted in a while. I've got a lot to say this time around though - I've been working more on N-pass. I've been coming a cropper against a few implementation details.

Firstly, we come to referential transparency. In C++ then it's impossible to make a non-referentially-transparent template, but in "DeadMG++" it would be trivial to do so- especially since the source of the type may not even be present. Thus, how could you implement type equality? So far, I've decided to have a hash mechanism, where I will hash the arguments and use a thread-safe cache to ensure memoization of "pure" functions. This would effectively mean that for all the calls to std::containers::vector()(int), there is only ever actually one call. Non-pure functions will always return different types. The trouble with this is firstly, "pure" functions not only guarantee referential transparency, but must also take only hashable arguments, a limitation I'm not a particular fan of. Whilst I can see that most relatively trivial arguments will be hashable, like types, strings, integers/floats, etc, then it would be perfectly reasonable to have arguments which are not hashable, in which case I would be unable to guarantee type equality. I especially don't like it because it would bind the library (hashing) to the language. Secondly, finding a concurrent cache structure is not a trivial undertaking.

And then I have another issue. Consider C++ code such as

template<typename T> void f(std::vector<T> t);



In "DeadMG++" then this would roughly correspond to

void f(type T)(std::containers::vector()(T) t);


However, this would mean that given a type, I would have to find it's source function call, basically. In the general case, I'm guessing this would be akin to solving the Halting Problem- especially since the source code need not exist. I'm considering just not supporting it, I think it's hardly the generic code I want to write, but I'm not sure that I want to have easily demonstratable code that C++ supports that I don't.

Thirdly, I'm also combating the problem of mutable types. Mutable types are important, but they also offer possibilities for the invariants to break. Right now, I'm dealing with reference types that look more like .NET. For example,

type vector()(type t) {
    t = struct {};
    if (t.is_pod()) {
        t.add_member_function("is_pod", [&]()(t& ref) {
            return true;
        });
    }
    return t;
}

type fail() {
    type t = struct {};
    type vectort = vector(t);
    t.add_member_variable("hai", std::string);
    // OOPS no longer POD.

}

Now, const would be the usual solution to this, and as such, you can bet that immutability is the cure. I've been thinking of adding an immutable_type. This would, when constructed, copy it's contents from a type, and then be immutable from then on, meaning that types would effectively have a construction phase and a use phase. Then, the above vector function would take and return an immutable type.

So I've got one major implementation problem left- referential transparency.

No comments:

Post a Comment