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