One of the biggest problems with memory is that it's inherently unsafe. We know the solution to that is immutability. Immutable memory is inherently safe. The problem is that Immutable memory is useless because it cannot be changed. To make it useful and safe we have to cheat by creating mutable things then turning them in to immutable things, which is safe, but generally not the other way around, which is unsafe.

So, Immutable memory sort of as a direction, that if we follow it, we are safe, and when we go in the opposite direction, we are unsafe.

Now, to real world stuff. I was building some container classes using mixin templates like:

// An Immutable Array Template
template _ImmutableArray(T, A)
{
// Has functionality to create itself from a indexable object and supply accessor methods for indexing, etc. The typical array stuff.

}

template _MutableArray(T,A)
{
    mixin _ImmutableArray!(T,A);

// Adds stuff so we can mutate the array(change it's length, remove elements, etc...
}

struct ImmutableArray(T)
{
    mixin _ImmutableArray!(T,A);
}


struct MutableArray(T)
{
    mixin _MutableArray!(T,A)
}


With such a pattern we can build up a hierarchy of templates that have corresponding run-time types. We can use introspection by having things like IsImmutable, etc. Very oop like, but with static constructs. On top of these we can build stacks(just a MutableArray), a queue(a MutableArray with Enqueue and Dequeue), a circular queue(a fixed array(length can't change)), and their immutable variants.

Now the cool thing about this, I think, is that we can do stuff like:

global ImmutableArray!int Data;


MutableArray!int DataCopy = Data.Copy; // Creates a mutable copy of Data.
... Do work with DataCopy ...
Data.Replace(DataCopy); // Makes a copy of DataCopy.


Note that Data is immutable and doesn't even have functionality that mutate the Data. So we know that we never mutate Data(this is checked at compile time). Immutable data always has it's own copy. The difference is that this is a weaker form of standard immutability. It allows us to logically separate code that mutates something with the code that doesn't. We can reduce overhead of allocating relatively simply:

void foo()
{
    MutableArray!int Data;
    scope(Exit) Data.Reuse();
}

Reuse can simply mark the memory reusable rather then freeing it. This memory can then be reused the next time foo is called(or possibly use the stack for memory). Utilities could find optimal uses for typical program behavior. e.g., foo uses on average 500 bytes for Data. Hence allocate the capacity for Data to 500 bytes.

While compiler support would make this shine, I think the main benefit is that one doesn't keeps mutable functionality with mutable data and immutable functionality with immutable data. The more a program works with immutable the safer it gets. It's leaving the little dropping of mutable data around that create the hard to find bugs. It makes it easier to reason about code(compiler or tool support can make it even better). Basically all the benefits of immutability as that is all it is but with a bit more type logic than the keyword.

e.g., If you use intelligent and mark something immutable, You'll still have "access" visually to all the mutable functionality. If you don't use intelligent, you'll get a compile time error higher up the stream that is more meaningful(basically trying to access a member that doesn't exist in the type).






Reply via email to