There’s a discussion of head-const, i.e. const
, but only the first layer of indirection, conversely to D’s transitive const
, here and there. I never understood the real deal because head-const doesn’t really bring much to the table in terms of guarantees.
As of lately, I had to code some Java, which I hadn’t done in many years. Java lacks many things, but it has IMO two nice things going for it: It’s type system, which I’m not really discussing here, and how closures work.
In Java, to capture a variable in a closure, it must be final
, i.e. head-const.
It’s simple, but genius. Java’s closures capture by value. That would be really bad if the programmer were to mutate a captured value and expect the effect to be visible to the context, but it isn’t. D, like many languages, has the capture-in-a-loop problem. C++’s solution is to let the user decide about how to capture; if something doesn’t work as intended, it’s on you. C# makes a decision for you that you might not agree with or surprise you. Java likewise makes a decision for you, but it won’t surprise you.
For capturing values, head-const really shines. If D required const
, it would ask for too much. In Java, final
is essentially what a storage class is in D, a property of a variable next to its type, not part of its type. For head-const in D, it would have to be a type constructor because the pointer to a head-const value must retain that information. While one could go all-in and make it const
, that would overly restrict the type.
I’m not proposing to add head-const to D. I just wanted to share a little thing I learned recently.