Skip to content

The Constness Problem

July 11, 2014

These days, I spend most of my programming time working on the Higgs JavaScript JIT compiler for my thesis, and most of the code I write is in D. I don’t always take the time to write the best, most carefully engineered code, especially if I know I might rework a given piece of code later. Still, I try to pay some attention to generally-accepted-best-software-design-practices. One thing I try to pay special attention to is which data should be immutable and which shouldn’t. I write my code in an imperative language, but I too have come to accept that immutable data and immutable data structures can make a program safer and easier to debug.

The main thing I’m working on right now, for Higgs, is a system of shapes (aka hidden classes) to describe object layouts in memory. In the Higgs object system, JavaScript objects are essentially a flat list of slots which can contain values. Shapes are essentially a tree data structure, where each node describes a property in an object, and most importantly, where that property will be stored in a flat object layout (the slot index associated with that property). Each object has an associated pointer to a node in the shape tree. Through this shape node pointer, you can find the properties stored in that object, their names, and which slot you can find them at. The important thing is that the shape tree nodes are essentially immutable. Once such a node is created, the name of the property it describes, the associated slot index, and all other attributes of the node should never change.

As someone who wants to write safe, descriptive code, I wanted to explicitly express those immutability constraints in my code. I wanted to use const(ObjShape) pointers when manipulating shapes. I also wanted to make the fields of the ObjShape class themselves const. Unfortunately, this plan didn’t work out. The DMD compiler wasn’t having it. I quickly found out that the D programming language (which is quite like C++ in this regard) just didn’t offer a way to express the semantics I wanted. Introducing const or immutable keywords in the code introduced strange and unpredictable compiler errors. Struct objects that could be copied by value before couldn’t anymore. Some of the errors reported were entirely nonsensical problems I never saw coming.

Consider the following snippet, and the compiler error it produces

class A { const(A) parent; }

void main()
    auto a = new A();
    auto p = a.parent;
    p = new A(); // test.d(7): Error: cannot modify const expression p

This is complete utter nonsense, and it deeply offends me! I specified that the parent field of class A should be constant. In D, you get “constness all the way down”, which means that I can’t mutate the parent field, and I also can’t mutate the object that the parent field points to. But, where in this program did I specify that the variable p was immutable and could not be redefined? Nowhere! This is problematic, because as far as I know, there is pretty much no way to write the type of p in such a way that p points to a const(A) object, but p remains mutable, not without ugly, unsafe type casting tricks. There’s no way to have a mutable variable that points to an immutable object? That’s insane!

In my opinion, C++ and D both lack expressivity when it comes to constness. D’s const and immutable keywords are is simply overpowering. It’s an issue in the other direction, too, because “constness all the way down” means that I can’t have a class field that’s immutable without also being forced to assume that the object pointed to by the field is entirely immutable. That’s not necessarily what I want. In the end, I’ve been forced to give up and remove const keywords on many occasions, because I end up fighting with the type system too much, it’s getting in my way.

It’s good that modern imperative languages have some tools to express purity and immutability. However, I think that there’s an issue of granularity. There are ways in which it makes sense to talk about constness, and ways in which it doesn’t. I’d almost say that I prefer JavaScript’s notion of constness, because it’s much simpler. In JavaScript, you can make an object property constant, but what that field points to does not have to be immutable. That is all! This might seem primitive, since you’re only able to painstakingly mark constness on individual object properties one at a time. It might feel like the only tool you have to deal with immutability in JavaScript is a tiny toothpick. This, however, is forgetting that JS has powerful introspection capabilities which allow to write code that makes an entire data structure immutable with a single function call. The language offers you the tools to express constness any way you want to.

  1. namelessfaceless permalink

    There is Rebindable to have mutable refs to const objects. It’s not pretty though.

    • Mixing const and structs gets very messy also. I had some code with const methods on a struct that worked just fine. It worked fine until I added pointers inside the struct definition. This broke the code in many places. Const structs with data that can be aliased inside just don’t work the same as other structs. I ended up having to refactor the code, and cast away the constness in a few places.

  2. Luke permalink

    You probably already know that in C++ you can have a mutable variable that points to a const object:

    const Foo* foo;

    and a const variable that points to a mutable object:

    Foo* const foo;

    And of course the mouthful

    const Foo* const foo;

  3. “But, where in this program did I specify that the variable p was immutable and could not be redefined? Nowhere! This is problematic, because as far as I know, there is pretty much no way to write the type of p in such a way that p points to a const(A) object, but p remains mutable, not without ugly, unsafe type casting tricks.”

    This is a problem with all type attributes for class references in D, as there’s no way to specify that the attribute refers only to the referenced data. Say you want to have a local reference to a shared object. How is that variable declared? Personally, I think this is enough of an obstacle that I’m disinclined to use these attributes with classes at all.

    • I agree. With const I ended up having to cast away constness in several places. Semantically, I feel that if I have a working program in which I’m not mutating some variable x anywhere, then making x constant by adding an attribute should not break my program (I’m adding a new invariant that I wasn’t violating).

      Unfortunately, adding attributes does break code. Making some variables/fields const/immutable/etc forces you to change your code in many places, breaks it in annoying and unpredictable ways. In the end, you want to just go “screw that, it worked before”. Who wants to fight against the compiler on odd semantics?

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: