Writing const-correct code in D

xs0 xs0 at xs0.com
Wed Mar 8 04:05:45 PST 2006


Kevin Bealer wrote:
> Also, this is not full "C++ const", only parameter passing and const
> methods, which seems to be the most popular parts of the const idea.
> It seems like it should require more syntax that C++, but it only
> takes a small amount.
> 
> 
> When working with types like "int", use "in" - const is not too much
> of an issue here.
> 
> The same is true for struct, it gets copied in, which is fine for
> small structs.  For larger structs, you might want to pass by "in *",
> i.e. use "in Foo *".  You can modify this technique to use struct, for
> that see the last item in the numbered list at the end.
> 
> 
> For classes, the issue is that the pointer will not be modified with
> the "in" convention, but the values in the class may be.
> 
>  // "Problem" code

OK, this is like the 5000th post I've read regarding const correctness 
and related issues in D. Can we really not come to some kind of an 
agreement on what would be best? I'm sure if there's a consensus about a 
solution, Walter will eventually implement it.

I've read the paper Andrew posted a link to in the last const thread, 
and I really like that system:
http://pag.csail.mit.edu/pubs/ref-immutability-oopsla2005-abstract.html

Walter, have you also read it? What do you think?

Here's a bad summary, if you don't feel like reading 20 pages :)

References/pointers have two properties, assignability and mutability. 
Assignability is already handled in D by declaring something const, 
which prevents reassignment, but there is no notion of immutability.

Javari introduces a readonly keyword, which applies to a reference. When 
a reference/pointer is readonly, it means that the data it points and 
also all transitively reachable data cannot be changed.

Note that there is no implication that the data will not change through 
other references.

And, obviously, you can't assign a readonly reference into a mutable 
var, and you can do the opposite.

Well, that's the gist of it, other features in random order include:

- overloading based on mutability of this:

class StringWithDate {
    Date getDate() { return m_date; }
    //   returns a mutable Date, can be called
    //   only through a mutable reference

    readonly Date getDate() readonly { return m_date; }
    // - the second readonly applies to this
    // - can be called only through a readonly ref
    // - the Date returned could still be mutable
    //   but probably the implementation would dup it first
}


- romaybe keyword for simple cases like above:

romaybe Date getDate() romaybe { return m_date; }
// is exactly the same as the two funcs above; romaybe
// basically expands into two methods, one replaces
// all romaybes with readonly, the other with mutable


- readonly classes:

readonly class ConstString { ... }
// will make all references to ConstString immutable
// (much like how auto classes work)


- conceptually, each class definition (say Foo : Bar) produces two new 
types, "readonly Foo : readonly Bar" and "Foo : readonly Foo, Bar", the 
first of which only contains readonly methods, while the second contains 
all others. That makes it trivial to do verification and overloading, 
and has a nice feature that there is no need to actually compile the 
readonly version, as all verification is done statically, so there is no 
increase in code size etc.


- one can still explicitly allow changing an object's fields even 
through readonly references, which is useful for things like caching 
hashcodes, logging, etc., which do not change an object's "abstract 
state" but do still have side effects


The problem of having to write two versions of functions, depending on 
mutability, is somewhat helped by "romaybe", and could be further eased 
if the compiler did some simple inference on its own:

- class fields are "important" by default and "ignorable" if they are 
explicitly declared "mutable" or "readonly" (readonly is ignorable, 
because it cannot be changed in the first place; mutable actually 
declares the field to be ignorable)

- any method that could write to important fields or could call 
non-readonly method on them is inferred to be mutable, unless specified 
otherwise

- other methods are considered readonly, unless specified otherwise

- "in" parameters are resolved analogous to above, "inout" and "out" 
default to mutable

- return values default to mutable


So, would a system like this be acceptable?


xs0



More information about the Digitalmars-d mailing list