## digitalmars.D - Oskar's const proposal

• Bill Baxter (42/88) Jan 04 2008 (Referring to http://www.csc.kth.se/~ol/const.pdf in comments below that...
• Oskar Linde (47/141) Jan 05 2008 I can't say it fixes all tricky issues, and since I have no full grasp
Bill Baxter <dnewsgroup billbaxter.com> writes:
```Oskar Linde wrote:
Walter Bright wrote:
Janice Caron wrote:
On 12/28/07, Walter Bright <newshound1 digitalmars.com> wrote:
Janice Caron wrote:
y is a /copy/ of x, and clearly it should be possible to make a copy
of a const thing and have the copy be mutable.

*BEEP* Confusion warning: you mean *constant*, not "const". Huge
difference. :)

That doesn't work for structs or classes.

It doesn't? For structs

struct S {}
const S x;
auto y = x;

Imagine you have:
struct S { int* p; }
Because const is transitive, const(S) implies that now p points to
const. But if you strip off the const in the assignment, you've lost
the const-ness of p, and now you have a gaping hole in the
const-correctness.

The problem with the current const in D is the lack of orthogonality. As
far as I can see, those problems would be solved by my orthogonal const
proposal posted earlier. Constant values (such as structs) are by their
nature always implicitly convertible to mutable values (worst case: just
make a copy).

The problem with the current const iteration is that there is no way to
separate the orthogonal concepts constness (as in constants) from

Also, as far as I can see (not very) there would also not be any need
for a third type of constant (the manifest one).

const int x = 5;

could behave like a template and only be instantiated when the address
of x is taken.

The result would be a const design, equally powerful but without
spoiling the beauty and simplicity of the D1.0 const. In D1.0, when
something is *constant*, you simply declare it as const:

struct T {
const int a = 5;
int b;
}

static assert(T.sizeof == int.sizeof);

No need to stop and think. In D2.0 it seems like you have to go through:
"Hmm, this value will never change. I will mark it as const.... no wait
invariant... or maybe manifest constant using the enumeration hack?"

(Referring to http://www.csc.kth.se/~ol/const.pdf in comments below that

I think everyone has trouble seeing how your const proposal fixes the
tricky issues.  You give some syntaxes but don't show how they solve the
problems that have caused all the consternation (!) over the past year
that arise when you start pulling on the string.  Also your comparison
table doesn't include structs, which seems a big omission since structs
often end up causing the trouble with const proposals.  Also it would be
helpful if your table gave an brief description of the behavior of each
row: like "in int*: the pointer can be modified but the int cannot via
this pointer"

But anyway, if I understand from the brief description, the idea is
basically to use "const" to apply to values only and to mean the value
will not change, period, no way, never.  And you use 'in' exclusively
for things that can refer to other memory to say "you can't change what
it refers to through this reference".

Ok, that sounds pretty neat.

So first question is: what happens when you use "in" on a plain int?  It
isn't a reference of any kind so is "in int" an error or a no-op?

Does it have to be "const const(int)*"?  Seems like that should be the
same as "const(int*)".  It's unclear in your proposal how const and in
are scoped.

I see some potential messiness given a pointer variable t of type 'T'
determining whether I can modify what it points to or not.  As far as I
can tell, T can come in 3 non-modifiable flavors:
* "in S*" (readonly ref to mutable S),
* "in const(int)*" (readonly ref to immutable S),
* and "const(int)*" (mutable ref to immutable S)
So i'd need to check if either "typeof(*t) is const" or "typeof(t) is in".

It also seems syntactically rather complex.  Every reference type have
both "in"-ness and "const"-ness, right?  I take it both things are
transitive?, so I can't have a const pointer to mutable pointer to const
value.  But even then If I have int****** how do I say it's 'in' from
two levels down and 'const' from level 4 on down?  in(const(int**)**)**.

I guess I don't really understand why you'd ever have "const" appear
twice in a declaration as you do for a couple of cases in your table.
Both 'const' and 'in' I can see, but not const twice.  Or are you trying
to have tail const?

Hope my questions are clear enough to you and give you some insight into
what parts of your proposal people find difficult to understand.

--bb
```
Jan 04 2008
Oskar Linde <oskar.lindeREM OVEgmail.com> writes:
```Bill Baxter wrote:
Oskar Linde wrote:
> Walter Bright wrote:
>> Janice Caron wrote:
>>> On 12/28/07, Walter Bright <newshound1 digitalmars.com> wrote:
>>>> Janice Caron wrote:
>>>>> y is a /copy/ of x, and clearly it should be possible to make a copy
>>>>> of a const thing and have the copy be mutable.
>
> *BEEP* Confusion warning: you mean *constant*, not "const". Huge
> difference. :)
>
>>>> That doesn't work for structs or classes.
>>>
>>> It doesn't? For structs
>>>
>>>     struct S {}
>>>     const S x;
>>>     auto y = x;
>>
>> Imagine you have:
>>     struct S { int* p; }
>> Because const is transitive, const(S) implies that now p points to
>> const. But if you strip off the const in the assignment, you've lost
>> the const-ness of p, and now you have a gaping hole in the
>> const-correctness.
>
> The problem with the current const in D is the lack of orthogonality. As
> far as I can see, those problems would be solved by my orthogonal const
> proposal posted earlier. Constant values (such as structs) are by their
> nature always implicitly convertible to mutable values (worst case: just
> make a copy).
>
> The problem with the current const iteration is that there is no way to
> separate the orthogonal concepts constness (as in constants) from
>
> Also, as far as I can see (not very) there would also not be any need
> for a third type of constant (the manifest one).
>
> const int x = 5;
>
> could behave like a template and only be instantiated when the address
> of x is taken.
>
> The result would be a const design, equally powerful but without
> spoiling the beauty and simplicity of the D1.0 const. In D1.0, when
> something is *constant*, you simply declare it as const:
>
> struct T {
>     const int a = 5;
>     int b;
> }
>
> static assert(T.sizeof == int.sizeof);
>
> No need to stop and think. In D2.0 it seems like you have to go through:
> "Hmm, this value will never change. I will mark it as const.... no wait
> invariant... or maybe manifest constant using the enumeration hack?"
>

(Referring to http://www.csc.kth.se/~ol/const.pdf in comments below that

I think everyone has trouble seeing how your const proposal fixes the
tricky issues.  You give some syntaxes but don't show how they solve the
problems that have caused all the consternation (!) over the past year
that arise when you start pulling on the string.

I can't say it fixes all tricky issues, and since I have no full grasp
of what all the tricky issues are I regret calling it a proposal. Let's
just consider it a thought experiment.

table doesn't include structs, which seems a big omission since structs
often end up causing the trouble with const proposals.  Also it would be
helpful if your table gave an brief description of the behavior of each
row: like "in int*: the pointer can be modified but the int cannot via
this pointer"

More on that below.

But anyway, if I understand from the brief description, the idea is
basically to use "const" to apply to values only and to mean the value
will not change, period, no way, never.  And you use 'in' exclusively
for things that can refer to other memory to say "you can't change what
it refers to through this reference".

Ok, that sounds pretty neat.

It does, doesn't it? My main gripe with the design of the current const
is that the meanings of invariant and const are too overlapping
(non-orthogonal if you wish). A plain int can have three different types:

int
const int
invariant int

Where the latter two are almost (but not fully) identical. There are
other ways to resolve this, but for now I will focus on orthogonalizing
the concepts.

Plain old data would only come in two flavors: variable and constant.
The "plain" data types are:
* all primitive non-reference types (int,float,char,...)
* structs and unions containing only plain data types.

So first question is: what happens when you use "in" on a plain int?  It
isn't a reference of any kind so is "in int" an error or a no-op?

Both behaviors are possible, but I think making it a no-op is the most
helpful one. The result in either case is that nothing could ever have
the type "in int".

Does it have to be "const const(int)*"?  Seems like that should be the
same as "const(int*)".  It's unclear in your proposal how const and in
are scoped.

If const only applies to the actual data without imposing any
contractual limitations on what may be done with eg. a copy of the data
we gain the neat effect of always having const T implicitly convertible
to T. From this also follows that const (as opposed to in) has to be
intransitive which is why I wrote "const const(int)*".

We could also consider a design where constness imposes contractual
limitations on the data, eg. making const fully transitive, but we would
lose the implicit const T => T conversion. This is outside the scope of
this thought experiment, but nevertheless leads down some interesting
paths. (*)

I see some potential messiness given a pointer variable t of type 'T'
determining whether I can modify what it points to or not.  As far as I
can tell, T can come in 3 non-modifiable flavors:
* "in S*" (readonly ref to mutable S),
* "in const(int)*" (readonly ref to immutable S),
* and "const(int)*" (mutable ref to immutable S)
So i'd need to check if either "typeof(*t) is const" or "typeof(t) is in".

The "in" attribute of a reference could be implicit from having a
reference to constant data. So

typeof(const(T)*) == in const(T)*

and you'd only need to check the in attribute of the type to know if you
can modify what it refers to.

Regarding in and structs, I think it is advantageous if

T[] and
struct Slice(T) { T *ptr; size_t length; }

behaved identically with respect to a const T and an in type attribute
which would have certain implications.

Hope my questions are clear enough to you and give you some insight into
what parts of your proposal people find difficult to understand.

Thank you for the questions. They were good, although not as tricky as I
feared (there exists tricky ones). :) I hope I managed to answer them
and bring some clarity on the topic. At least, I hope this may
possibilities forward for the current debacle.

--
Oskar
```
Jan 05 2008