Writing const-correct code in D

Don Clugston dac at nospam.com.au
Wed Mar 8 06:57:22 PST 2006


Johan Granberg wrote:
> xs0 wrote:
>> 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
> I would like it this is how I have been using const in c++

I think having seperate overloads for const and non-const parameters is 
a design mistake in C++. I see C++ 'const' as a compile-time contract, 
not a type. IMHO, overloading const vs non-const is like overloading 
functions based on their contracts.

I suspect that 'const' has exaggerated importance to those of us from a 
C++ background, because it's almost the only contract that C++ provides.
Maybe the solution to 'const' is a more general compile-time contract 
mechanism.



More information about the Digitalmars-d mailing list