View mode: basic / threaded / horizontal-split · Log in · Help
September 11, 2007
Re: Const sucks
Janice Caron wrote:
> On 9/11/07, *Janice Caron* <caron800@googlemail.com 
> <mailto:caron800@googlemail.com>> wrote:
> 
>     It's simple, but - I would argue - the wrong choice. A better choice
>     (in my humble opinion) would be
> 
>     const = constant
>     readonly = read-only
> 
>     If it's not too late to change the keywords, that, to me, would be
>     the way to go.
> 
> 
> 
> By which I mean, if it's at all feasable to consider ditching the 
> keyword "invariant" altogether, and introducing a new keyword 
> "readonly", then let's do it. "readonly" should indicate a read-only 
> view of data which someone else might modify, and "const" should imply 
> that no-one can modify it ever, no way, nohow.
> 
> I'm not quite sure why no one before now has suggested using "readonly" 
> to mean read-only and "const" to mean constant, but seems kind of a 
> no-brainer to me. You know - calling a thing what it is, instead of 
> something it's not. I know I'd be dead confused if int meant float, for 
> example.

It has been suggested before several times.  The problem is there's 
disagreement over what it should mean.  To some it is obvious that 
"readonly" should mean permanently unwriteable, just like "read only 
memory" is unwriteable.  To others it is equally obvious that it should 
mean a read-only view of data that is writeable through some other means.

--bb
September 11, 2007
Re: Const sucks
On Tue, 11 Sep 2007 04:50:32 +0400, Miles <_______@_______.____> wrote:

> Walter Bright wrote:
>> Const, final, invariant, head const, tail const, it's grown into a
>> monster. It tries to cover all the bases, but in doing so is simply not
>> understandable.
>
> I was going to say that, that D's const sucks, many months ago, but
> didn't because I was afraid being regarded as a troll.
>
> Its not that I like const-less languages like Java (in fact, I hate
> them), but I think that C++'s const, with all its problems, was the
> right answer (for that time). It fixes the problem without putting much
> burden onto the programmer.
>
> D's const puts way too much burden onto the programmer, and gives very
> little back. Too many different combinations of const, final, invariant,
> pointers and references (and the fact that in D you don't immediately
> know if a type "T" is a reference of a value, specially while writing
> templates, adds to this confusion) with confusing semantics when
> combined (with a few special cases and lack of orthogonality).
>
> When the new const was being designed, I told in this list: "Don't try
> to fix what ain't broken". What I meant was: Don't try to do a new const
> just because you want a better const than C++s. Many people dislike C++s
> const, but are unable to point exactly why it is wrong. Most people
> doesn't even understand C++'s const, and try to get more from it than it
> was actually intended to provide (that is the whole point behind the new
> D's invariant).
>
> Sometimes people give use-cases to exemplify (STL's need for a
> difference between iterator<> and const_iterator<> is the first thing
> that comes to my mind), but then you ask what a language could provide
> to fix this, they don't know. This is where you should aim first you  
> laser.
>
> When I first saw D, I was amazed with the possibilities. Now I'm
> somewhat scared. Perhaps one of the things that are scaring me is just
> like you said: there is a (at least one) monster in the language.

I'm totaly agree with all you have said.

-- 
Regards,
Yauheni Akhotnikau
September 11, 2007
Re: Const sucks
Walter Bright wrote:
> Const, final, invariant, head const, tail const, it's grown into a
> monster. It tries to cover all the bases, but in doing so is simply not
> understandable.

Does that mean I'm a genius, or insane? :P

> [snip]
> 
> o  no more final

Not 100% sure what you mean by "no more".  But then, "final" was always
kind of like the appendix of the new const system; I always viewed it as
something of a side issue since it didn't affect the type of declarations.

> o  const and invariant now mean "fully const" and "fully invariant",
> where fully means "head and tail combined":
> 
> const int x = 0;   // x is constant
> const int* p = &x;  // neither p nor *p can be changed
> const(int*) p = &x;  // neither p nor *p can be changed
> const(int)* p = &x;  // p can change, *p cannot be changed

Ok.

> o  const and invariant are transitive. There is no way to specify a
> (pointer to)(const pointer to)(mutable int).
> 
> o  tail invariant for an array or pointer type can be done using the
> existing syntax:
> 
>  invariant(char)[] s;   // string, i.e. an array of invariant chars
>  const(T)* p;  // pointer to tail const T

This makes me happy, because although I understood the what, I never
could grasp the why of "invariant(char)[]" meaning that you couldn't
change the elements of the array.

> o  tail const of a struct would have to be done by making the struct a
> template:
> 
>   struct S(T) { T member; }
>   S!(int)   // tail mutable
>   S!(const(int)) // tail const

This is... not so good.  I'll come back to this...

> o  one can construct a template to generically produce tail const or
> tail invariant versions of a type.

This makes me rather nervous; it seems like a bad thing that we can no
longer do tail-const, and have to resort to wrapping templates to do it.

I mean, in the end, you can make a const version of any type by
replacing all fields with read-only properties; but then you can't
cleanly cast between them.

> o  it will be illegal to attempt to change the key value of a foreach
> loop, but the compiler will not be able to diagnose all attempts to do so

I assume this means that for built-in types, instead of being

 ( ref key, ref value ; aggregate )

We'll have

 ( const ref key, ref value ; aggregate )

If so; I'll be happy about that.  So long as I can do evil cr*p like:

 ( ref idx, ref chr ; string.foo ) ++idx;

Why, you ask?  Er, you don't wanna know... :P

> o  static const/invariant means the initializer is evaluated at compile
> time. non-static const/invariant means it is evaluated at run time.

Ok.

> o  no initializer means it is assigned in the corresponding constructor.

Sounds good.

> o  const/invariant declarations will always allocate memory (so their
> addresses can be taken)

I guess this is good from a consistency standpoint.

> o  So, we still need a method to declare a constant that will not
> consume memory. We'll co-opt the future macro syntax for that:
> 
>     macro x = 3;
>     macro s = "hello";

The syntax doesn't *really* speak to me; honestly, I was expecting you
to use "alias" before I scrolled down and saw "macro".  Thing is, if
this works for arbitrary expressions, does that mean this will be possible:

   macro string = char[];

My main problem with this is the loss of tail const on structs/classes.
Others have already listed several useful things that you won't be able
to do because of this, so I won't bother with that.

This problem seems to be unique to structs and classes.  This is because
every other reference type is written in such a way that you can "split"
the type in such a way as to get tail-const semantics.  For example:
const(char[]) versus const(char)[].  This can't be done with structs or
classes since their types are single identifiers.

So what we need is some way to write that split.  Really, what we're
saying is "I don't want *this* to be const, but I do want what it
references to be const".

Back when I was writing my article on the upcoming const changes, I gave
each kind of const a different, longer name to help differentiate them;
the names I gave const and invariant were "reference const" and
"reference immutable".

So, stealing a little bit of C++ syntax, what about "const& T" being a
tail-const version of some struct or class T?  Since this could
potentially introduce synonyms in other places, I would also propose the
following:

 const& int a; // Error: cannot use reference const on an atomic type
 const& int* b; // Error: equivalent to const(int)*
 struct V { int a; }
 const& V c; // Warning: V does not have any references
 struct R { int* a; }
 const& R d; // OK
 class C {}
 const& C e; // OK

Yes, this means there is a distinction between structs, classes and
"everything else".  But honestly, I don't think we can have tail const
without this distinction.  Maybe disallowing const& on arrays and
pointers isn't a good idea; but I do think we need some kind of
tail-const for structs and classes.

Thoughts?

	-- Daniel
September 11, 2007
Re: Const sucks
Bill Baxter wrote:
> Janice Caron wrote:
>> On 9/11/07, *Janice Caron* <caron800@googlemail.com
>> <mailto:caron800@googlemail.com>> wrote:
>>
>>     It's simple, but - I would argue - the wrong choice. A better choice
>>     (in my humble opinion) would be
>>
>>     const = constant
>>     readonly = read-only
>>
>>     If it's not too late to change the keywords, that, to me, would be
>>     the way to go.
>>
>>
>>
>> By which I mean, if it's at all feasable to consider ditching the
>> keyword "invariant" altogether, and introducing a new keyword
>> "readonly", then let's do it. "readonly" should indicate a read-only
>> view of data which someone else might modify, and "const" should imply
>> that no-one can modify it ever, no way, nohow.
>>
>> I'm not quite sure why no one before now has suggested using
>> "readonly" to mean read-only and "const" to mean constant, but seems
>> kind of a no-brainer to me. You know - calling a thing what it is,
>> instead of something it's not. I know I'd be dead confused if int
>> meant float, for example.
> 
> It has been suggested before several times.  The problem is there's
> disagreement over what it should mean.  To some it is obvious that
> "readonly" should mean permanently unwriteable, just like "read only
> memory" is unwriteable.  To others it is equally obvious that it should
> mean a read-only view of data that is writeable through some other means.
> 
> --bb

The problem is that, broadly speaking, all these words mean the same
thing.  Once you get down to splitting hairs over exactly how
constant/invariant/immutable/readonly/final something is, you're always
going to find some (possibly obscure) argument against the way you want
it to work.

Aren't natural languages fun?!

:P

	-- Daniel
September 11, 2007
Re: Const sucks
On 9/11/07, Derek Parnell <derek@nomail.afraid.org> wrote:
>
> In other words, if I have a struct with three members, each of a different
> type, I need to code ...
>
> struct S3(T, U, V)
> {
>    T member1;
>    U member2;
>    V member3;
> }
>
> S3!(const(int), const(float), const(bool));
>
> and so on for 4, 5, 6, .... 23 member structs.
>
> I'm sure I'm misunderstanding you, because this is really silly.
>

I don't think you're misunderstanding. I think that's what Walter is saying.

But here's another idea. If it were allowable that
(1) an alias template parameter could accept a type constructor, and
(2) "auto" were accepted as a do-nothing type constructor
then you would be able to do this:

struct S(alias X)
{
    X(int)* pi;
    X(float)* pf;
    X(double)* pd;
};

S(const) k; // k's members are tail-const
S(auto) m; // m's members are mutable
September 11, 2007
Re: Const sucks
Daniel Keep wrote:
 > This problem seems to be unique to structs and classes.  This is because
> every other reference type is written in such a way that you can "split"
> the type in such a way as to get tail-const semantics.  For example:
> const(char[]) versus const(char)[].  This can't be done with structs or
> classes since their types are single identifiers.

I came to the same conclusion, the basic problem is the inability to put 
a * or [] outside the () of const.

> So what we need is some way to write that split.  Really, what we're
> saying is "I don't want *this* to be const, but I do want what it
> references to be const".

Exactly.

> So, stealing a little bit of C++ syntax, what about "const& T" being a
> tail-const version of some struct or class T?  Since this could
> potentially introduce synonyms in other places, I would also propose the
> following:
> 
>   const& int a; // Error: cannot use reference const on an atomic type
>   const& int* b; // Error: equivalent to const(int)*
>   struct V { int a; }
>   const& V c; // Warning: V does not have any references
>   struct R { int* a; }
>   const& R d; // OK
>   class C {}
>   const& C e; // OK
> 
> Yes, this means there is a distinction between structs, classes and
> "everything else".  But honestly, I don't think we can have tail const
> without this distinction.  Maybe disallowing const& on arrays and
> pointers isn't a good idea; but I do think we need some kind of
> tail-const for structs and classes.
> 
> Thoughts?

In D when we write:

Foo f;

we are declaring a reference to a Foo.  The problem, as you mentioned is 
the inability to seperate the reference from the Foo for purposes of 
declaring const, so...

I was initially thinking that for classes we might write:

class Foo { int a; }
const(Foo)& pFoo;  //pFoo can change, pFoo.a cannot

Where the & doesn't introduce another reference or level of indirection, 
in fact it does _nothing_ at all except allowing us to place it outside 
the () of const.

Meaning, that these two declarations would in fact be identical:

Foo& pFoo;
Foo  pFoo;

which will no doubt bother some people, perhaps a lot of people?

The first form could be made illegal, after all it's pointless(right?) 
to take a reference to a reference.

Alternately, and I think I might prefer this solution, perhaps _adding_ 
something to the const() is the solution. eg.

class Foo { int a; }
const(*Foo) pFoo;  //pFoo can change, pFoo.a cannot

As in, were saying "the value of Foo is const", therefore implying the 
reference is not.


As for structs, as someone else has mentioned there isn't really any 
difference between making a struct variable const and making all members 
of that struct const, without a pointer or reference they are the same 
thing.  So, saying:

struct Bar { int a; Foo pFoo; }
const(Bar) bar;

Is perfectly sufficient in that case.

The difference occurs when you want to make _some_ of the members consts 
and not the others, typically you want to make references/pointers 
const, or tail const.

The syntax:

struct Bar { int a; Foo pFoo; }
const(Bar)& bar;

doesn't make the same sense for structs because there is no reference 
involved (as many programmers would expect upon reading that).

Neither does the * syntax I proposed above:

struct Bar { int a; Foo pFoo; }
const(*Bar) bar;

because that would logically make all members of the struct const, and 
we get that already.

Assuming there is even a requirement to make select members of a struct 
(initially declared without const) const (if there isn't we have no 
problem) then using a template seems the most sensible solution.  It 
would also allow you to apply const or tail const where appropriate. eg.

From this initial struct:

struct Bar {
  int a;     //we want this to be const
  Foo pFoo;  //we want this to be tail const
}

Our template generates this:

struct BarConst
{
  const int a;
  const(*Foo) pFoo;
}

Regan
September 11, 2007
Re: Const sucks
Bruno Medeiros wrote:
> Derek Parnell wrote:
>>  
>>> o  const and invariant now mean "fully const" and "fully invariant", 
>>> where fully means "head and tail combined":
>>
>> Remind me again what the difference between 'const' and 'invariant' is.
>>
> 
> 'invariant' = constant
> 'const' = read-only
> 
> It's simple I think.
> 

I must withdraw somewhat my comment that "it's simple", since 
"read-only" is indeed a somewhat subjective term. If you think read-only 
as in permissions, you get the intended meaning. But if you think 
read-only as in memory (ROM), as Bill mentioned, you get the same 
meaning as constant. Still, I think the "read-only as in permissions" 
meaning for "read-only" is becoming more prevalent as it is more... 
meaningful.

-- 
Bruno Medeiros - MSc in CS/E student
http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
September 11, 2007
Re: Const sucks
Regan Heath wrote:
> Assuming there is even a requirement to make select members of a struct 
> (initially declared without const) const (if there isn't we have no 
> problem) then using a template seems the most sensible solution.  It 
> would also allow you to apply const or tail const where appropriate. eg.

Talking to myself, a sure sign of mental instability ;)

The same applies to making select members of a class const.  I think 
it's important to remember that we're talking about _modifying_ an 
existing class/struct definition _adding_ const where it was not initially.

This would only be required in cases where:
1. you need different const rules for instances of the same class/struct 
in the same version of the code.
2. you need to const correct a 3rd party API.

In any other case you can simply apply const to the class/struct 
definition directly, no need for a template to do it. eg.

struct Something
{
  const(*Foo) pFoo;
  const int a;
  int b;
}

There will be no need to _modify_ this with a template because it will 
always need to have a mutable reference to a const Foo and that 
requirement won't be different for some instances and not others.  It 
may change in future versions of the code, but in that case a simple 
change to the struct definition is all you need, not a template.

I think the ability to tail const a class reference with something like:

class Foo { int a; }
const(*Foo) pFoo;

Handles 99% of the cases which we should realistically be worried about.

Regan
September 11, 2007
Re: Const sucks
Walter Bright wrote:
> Bruno Medeiros wrote:
>> Walter Bright wrote:
>>> What, exactly, is the use case that needs a solution?
>>
>> Here's another use case, which is significantly different than the 
>> other (there should be lots more to be found). Say you have a Person 
>> class, which has a name, and for whatever reasons you want the name be 
>> a String class instead of const(char)[]. Then you have:
>>
>>   class Person {
>>     const(String) name;
>>     ...
>>
>> but now how do you change the name of the Person?...
> 
> I'd redesign String so it holds invariant data, then just have:
> 
> class Person {
>     String name;
>     ...
> 

Err... you do realize what this implies? This example holds not just for 
String but for the general case where one would want to use aggregation 
of immutable objects. In all those cases you'd have to redesign the 
aggregated class to be immutable, with all the annoyances that has. That 
basicly amounts to not be able to use const/invariant for classes, and 
then in that regard D would revert to the same status quo as Java and 
other languages.

(Another alternative is to use an actual pointer to a class reference, 
but that's sure to bring another slew of inconveniences)

-- 
Bruno Medeiros - MSc in CS/E student
http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
September 11, 2007
Re: Const sucks
Janice Caron wrote:
> 
> I'm not quite sure why no one before now has suggested using "readonly" 
> to mean read-only and "const" to mean constant, but seems kind of a 
> no-brainer to me. You know - calling a thing what it is, instead of 
> something it's not. I know I'd be dead confused if int meant float, for 
> example.
> 

It was quite naive to think that no one had suggested keyword changes 
for this before. :P

-- 
Bruno Medeiros - MSc in CS/E student
http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
4 5 6 7 8 9 10 11 12
Top | Discussion index | About this forum | D home