Wednesday 20 July 2011

The value of mutable types

So, previously, I've been discussing some of the problems that come up with mutable types. Recently, I've been revisiting some of the advantages, and they may cause me to re-think my approach to mutability of types. For example, take this simple, easy dynamic_cast implementation.

type t = struct {};
t.public_functions.insert(
    name := "dynamic_cast",
    function := [&](type other_t)(this) {
        if (decltype(this).inherits_from_or_is(other_t))
            return forward(this);
        else
            throw std::bad_cast();
    },
    virtual := true
);


The logic is pretty simple. For each dynamic_cast attempted, insert a new function (the instantiation) into the vtable and then call it, and we return this or throw appropriately. This requires two things: first, unified compilation, which I will go into in another post, and secondly, that the type t is still mutable, and that all it's derived classes are still mutable. This means that, in my previously proposed solution, this can't work for any type that you may wish to place in a place where non-mutability is required or any type where any of it's derived types are placed somewhere where non-mutability is required.

The upside is that one, assuming that indexing into a giant vtable isn't a problem, this is one hell of a faster implementation than the linked-list scanning seen in C++ dynamic_cast implementations, and two, you can dynamic_cast types that don't currently have virtual functions, and indeed, if it's never inherited then we can issue appropriate optimizations/warnings/etc at compile-time, and three, dynamic_cast would no longer have to be part of the language, which is a massive win. Of course, this technique would be generalizable to any virtual function which has compile-time arguments, and that could be a big win. I don't think even managed languages can do that.

However, an interesting thing hit me- mutability of types could solve itself. So I've decided to ditch the immutable_type thing I had going on before and I'm thinking about to something more fine-grained. I will have an event-based model that can fire functions when types are modified. This way, we can even choose what to do in reaction. If you want an immutable type, you can simply set the event to "any" and throw an error in any condition.

type vector(type t, bool throw_on_change = false) {
    if (t.is_pod()) {
        type retval = ...;
        // generate an optimized std::vector
        t.on_change(
             
             event := is_not_pod,
             action := [&]() {  
                 if (throw_on_change)
                     throw build_error("Attempted to change type to non-POD.");
                 else
                     // change retval to be non-optimzed now           
             }
        );
        return retval; 
    }
    // Do non-optimized version

}

This is more effort on the part of type creators, but vastly more fine-grained and controllable, and it saves everyone from having to use immutable_type.

No comments:

Post a Comment