unpaintable (the solution to logical const)

Janice Caron caron800 at googlemail.com
Fri Apr 4 05:11:53 PDT 2008


I was wrong. Stephen was right. (This is great, actually. I love being
wrong because every time I'm wrong, I learn something new. Plus, I
love the debate). Still, I must bow my head to the superior argument.

I'm pretty good at explaining stuff, once I understand it, so I'm
going to try to do that, right now.

The thing is, I didn't actually truly understand why I (and Walter)
was wrong until I made that post about "transitive" being the wrong
word, and "recursive" being the right word, to describe what the
const() operation does. I guess it's all about how you think about
stuff. Words are so important.

So let's reinvent "logical const" from scratch, in a way that will
work for D, if it were to be implemented. For the record, this is
Stephen's idea, not mine - I'm just rewording it - although I do make
/one/ new observation.

What we do is this: we classify every field of a class/struct/union as
being either "paintable" or "unpaintable". (Fields which would be
called "mutable" in C++ are called "unpaintable" here).

When the type-constructor const(...) is applied to a type, all
paintable fields are recursively painted const. (Unpaintable fields
are left alone). That's it!

Similarly, when invariant(...) is applied to a type, all paintable
fields are recursively painted invariant. (And again, unpaintable
fields are left alone).

For functional programming to work, you need one additional
restriction: pure functions are only allowed to access fields which
are either paintable or invariant. (In the current regime, all fields
are paintable).

Because you still have recursion, this buys you enough guarantees to
make D's const system work, to allow functional programming, etc..

One thing Steven /didn't/ think of (or at least, didn't tell us about)
is that it is perfectly possible for a field to be simultaneously both
paintable and invariant. (That's one reason why "mutable" is really
not a good word for "unpaintable"). For example:

    class C
    {
        T a;
        unpaintable T b;
        invariant(T) c;
        unpaintable invariant(T) d;
    }

If we paint this class const, with the const(...) type constructor, we
get a const(C) - the fields of which will look like this:

    // pseudocode
    class const(C)
    {
        const(T) a;
        unpaintable T b;
        const(T) c;
        unpaintable invariant(T) d;
    }

As you can see, the members a and c have been painted const, but the
members b and d have been left alone. They are untouched. They are
/unpainted/. That's what unpaintable means.

Now let's see what a pure function might be allowed to do in such a class:

    class C
    {
        T a;
        unpaintable T b;
        invariant(T) c;
        unpaintable invariant(T) d;

        pure void f() invariant
        {
            auto w = a; /* OK: "this" is invariant */
            auto x = b; /* ERROR: b is unpaintable and not invariant */
            auto y = c; /* OK: "this" is invariant */
            auto z = d; /* OK: d is invariant */
        }
    }

Words matter. What we call things matters. When we use the word
"mutable", it  becomes counterintuitive that a field could be both
mutable and invariant at the same time. But when we use the word
"unpaintable", the mental dichotomy disappears: of /course/ you can
have an invariant field cannot be painted const.

Walter said that having mutable fields breaks the whole const system.
He may have been right - but having /unpaintable/ fields is just fine
and dandy. Everything works. const(...) and invariant(...) will paint
only the paintable fields with constancy, but those fields the paint
recursively.

It just works.



More information about the Digitalmars-d mailing list