September 11, 2007
On 9/11/07, Nathan Reed <nathaniel.reed@gmail.com> wrote:

> > Then just decree that it be illegal to take the address of a const class member variable. Problem solved.
>
> That places totally unnecessary restrictions on the programmer...there's absolutely no reason why you shouldn't be able to take the address of something const.

Remember, this is a special circumstance - /as a class member variable/

Consider:

class C
{
    const int n = 42;
    /* other stuff */
}

Suppose you new a thousand of those things. What's the point of making a thousand ints, all the same. Why not just do:

class C
{
    static const int n = 42;
    /* other stuff */
}

and only consume four bytes.

I argue that per-class-instance const variables is so silly, that you might just as well let the compiler "disappear" them.



> It's not /my/ macro idea. :)

My apologies. I lost track of to whom I was replying.

> Although, as other
> people have pointed out, this doesn't break type safety, just makes the
> type not explicitly stated in the code.

And this is different to #define how exactly?
September 11, 2007
On 9/11/07, Janice Caron <caron800@googlemail.com> wrote:
> I argue that per-class-instance const variables is so silly, that you might just as well let the compiler "disappear" them.

Otherwise I'll end up doing

class C
{
    enum : int { n = 42; }
}

because I want my type specified at the point of declaration.
September 11, 2007
Janice Caron wrote:
> class C
> {
>     const int n = 42;
>     /* other stuff */
> }
> 
> Suppose you new a thousand of those things. What's the point of making
> a thousand ints, all the same. Why not just do:
> 
> class C
> {
>     static const int n = 42;
>     /* other stuff */
> }
> 
> and only consume four bytes.
> 
> I argue that per-class-instance const variables is so silly, that you
> might just as well let the compiler "disappear" them.

Fine for things that are known at compile time.  But it's also possible to have const variables whose values are not known till runtime.  For instance

void foo (int bar)
{
    const int baz = bar * 47;
    ...
}

or class instance variables:

class Foo
{
    const int bar;
    this (int baz)
    {
        this.bar = baz;
    }
}

The variable needs to be initialized but is then not supposed to change over its lifetime.

>> Although, as other
>> people have pointed out, this doesn't break type safety, just makes the
>> type not explicitly stated in the code.
> 
> And this is different to #define how exactly?

#define does text substitution.  In C, you could write something like this:

#define THREE 3
float pi = THREE.14159;

With D macros that would not be allowed.

Thanks,
Nathan Reed
September 11, 2007
Janice Caron wrote:
>> So, if you'd do "macro x=5;" (or whatever the macro syntax will be),
>> and you try `char[] s="abc"~x;` you'll get a nice, clean type error.
> Looks the same as C to me.

Isn't!!!111!!

> #define x 5
> strcat(s,"abc");
> strcat(s,x); /* compile error */
> 
> That doesn't count as strong typing.

It is! You'll be able to do "typeof(x)" and probably get "int"... :-)

Regards, Frank
September 11, 2007
On 9/11/07, Nathan Reed <nathaniel.reed@gmail.com> wrote:

> void foo (int bar)
> {
>      const int baz = bar * 47;
>      ...
> }

That's not a class member variable. That's a local variable. It's a different animal.

In the case of local variables, the compiler can decide whether or not memory storage is required.


> class Foo
> {
>      const int bar;
>      this (int baz)
>      {
>          this.bar = baz;
>      }
> }
>
> The variable needs to be initialized but is then not supposed to change over its lifetime.

That can be done in different ways. Walter was originally going to allow final for that purpose, but final is now dropped. I think that the above example will not compile under Walter's new plans, because const is unmodifiable even in a constructor. Of course, I could be wrong. One way to rewrite that, though, would be

class Foo
{
     private int bar_;
     this (int baz)
     {
         bar_ = baz;
     }
     int bar() return bar_;
}


> #define does text substitution.  In C, you could write something like this:
>
> #define THREE 3
> float pi = THREE.14159;
>
> With D macros that would not be allowed.

True. But I want more than that. I want to be able to distinguish between

const int x = 42;
const uint x = 42;

etc.

And if I want type deduction, of course the following would work just fine:

const x = 42;
September 11, 2007
Janice Caron wrote:
> On 9/11/07, Nathan Reed <nathaniel.reed@gmail.com> wrote:
>> #define does text substitution.  In C, you could write something like this:
>>
>> #define THREE 3
>> float pi = THREE.14159;
>>
>> With D macros that would not be allowed.
> 
> True. But I want more than that. I want to be able to distinguish between
> 
> const int x = 42;
> const uint x = 42;

No problem:

macro x = 42;   //int
macro x = 42U;  //uint
macro x = 42L;  //long
macro x = 42UL; //ulong

In addition:

macro x = 2_147_483_648; //long

See:
"Decimal Literal"
http://www.digitalmars.com/d/lex.html


Regan
September 11, 2007
On 9/11/07, Regan Heath <regan@netmail.co.nz> wrote:
> No problem:
>
> macro x = 42;   //int
> macro x = 42U;  //uint
> macro x = 42L;  //long
> macro x = 42UL; //ulong

Maybe it's just a gut feeling thing.

const ulong x = 42; /* obvious to newbies what it does */ macro x = 42UL; /* looks weird */
September 11, 2007
Oskar Linde wrote:
> Daniel Keep wrote:
>>> Janice Caron 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
>>>>
>>
>> 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?!
> 
> They are. The meaning of words is very domain dependent is often based on convention rather than anything else.
> 
> I am sorry, but I can't pass this chance to present my side of the argument once more. :)
> 
> To an electrical engineer, "read-only" will surely give an association of memory that is "hard to change quickly or easily" (wikipedia), or maybe read-only pins, or read-only registers.
> 
> To a mathematician, "constant" is a fixed value, the opposite of a "variable".
> 
> In computer science, read-only could have a number of meanings. The top search results on citeseer for read-only yields the following terms:
> 
> * read-only transactions
> * read-only memory
> * read-only file system
> * read-only query
> * read-only fields
> * read-only methods
> * read-only replication
> * read-only access
> * read-only parameters
> * read-only actions
> * read-only state
> * read-only optimizations
> * read-only file
> 
> of which "read-only memor(y|ies)" corresponds to a mere 3.7 % of the papers, while "read-only transaction(|s)" corresponds to 29 %.
> 
> The prevailing meaning of read-only in CS seems to be that in regard to access. Using read-only in the meaning of access will also not conflict with existing uses (even for an electrical engineer). On the other hand, using "const"(ant) in the meaning of something that may change, or is only access protected does conflict with the mathematical definition.
> 
> It is therefore, in my view, obvious that substituting the words (readonly,const) for todays (const,invariant) would result in a much better match between the semantics of D and that of established and natural languages.
> 
> The crucial separation here is:
> * read-only -> access
> * constant -> data
> 
> Does the following conversation make sense?
> 
> A: "I have a function that requires a read-only reference to some data"
> B: "Fine, I can use it for both my variable and my constant data"
> 

I agree. (Damn, I always agree with you Oskar :P )

But I'll add: It's not just that the "read-only" meaning as in "read-only access/permissions" is the most popular one. It's also the meaning most congruent with the natural language term "read-only". That is, it's the most "meaningful".
ROM memory "is" read-only in the same sense that a dear is mammal. A deer is indeed a mammal, but it's more than a mammal, it's... a deer. :P
It's likely due to that that the alternative terminology wouldn't sound so strange for an electrical engineer, while the current one sounds strange for (most of) us CS programmers.

-- 
Bruno Medeiros - MSc in CS/E student
http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
September 11, 2007
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 have not really been following the discussions thus far, but would still like to offer my uninformed view on things. (More questions than opinions actually)


1. I think one reason it is not understandable is the poor choice of keywords. My latest reiteration on the keywords (elsewhere in this thread):

http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D&article_id=58135

(This may sound like a silly issue, but the advantages to using descriptive names should not be underestimated. Imagine using banana/apple/orange instead of int/float/void.)


2. There are two distinct problems. Access control vs constantness of data. The old proposal doesn't really take advantage of that distinction.


3. For write access control, I don't see transitivity making as much sense. There are cases where you want write access to a certain depth. Certain members of a class need not be part of that class, and therefore not bound by the same non-mutability requirements. Robert Fraser has posted a few use cases too.

What are the motivations for transitivity here? What problems does it solve? Are there alternative solutions to those problems?


4. Transitivity could still make sense for constant data (there are few cases were it makes sense to have for example a compile time rommable pointer pointing to mutable data).


An alternative suggestion would be to make a better separation between constness and access control by making const intransitive and only referring to access control, i.e const would prevent modification access to data in the following forms.

- writing though a pointer
- modifying class fields
- calling non-readonly methods on classes
- changing element of an array
- appending to an array

This means, more or less the D 2.000 const but without transitivity, coupled with the recently suggested invariant proposal.

Combine this with my wish to rename invariant->const, const->readonly:

* readonly handle to access control
* const refers to constness of the actual data
* readonly would be intransitive
* const would remain transitive

data -- const -- transitive
access -- readonly -- intransitive

simple, right? :)

The behavior of readonly would become (all of this should be deducible, so just skip this part):

A readonly type prevents modifying access through that type.

readonly int *  a; // no writing allowed through a
readonly int    b; // readonly doesn't apply to non-reference types
readonly Class  c; // c can be reassigned but is a readonly reference
readonly Struct d; // members are readonly, d isn't
readonly int[]  e; // e may change, e[] may not

Structs are value types, so readonly doesn't apply to them. It does however apply it its members.

struct S { int[] arr; String str; }

readonly S s;

Here, s.arr and s.str are readonly.

This means that as far as readonly is concerned, a struct containing a reference to data behaves the same as a free reference to data.

For arrays:

readonly Class[]           g; // readonly array of mutable classes
readonly(Class)[]          h; // mutable array of readonly classes
readonly readonly(Class)[] i; // readonly array of readonly classes

TransitiveReadonly!() is possible to implement.

references to constant data are automatically readonly.

const(int)[]  j; // j is readonly

Intransitivity allows all combinations of mutability [m] and immutability [i] for nested types:

[m][m][m] int[][][]
[i][m][m] readonly int[][][]
[m][i][m] readonly(int[][])[]
[m][m][i] readonly(int[])[][]
[i][i][m] readonly(readonly(int[][]))[]
[m][i][i] readonly(readonly(int[])[])[]
[i][m][i] readonly readonly(int[])[][]
[i][i][i] readonly(readonly(readonly(int[]))[])[]

As far as I can see this resolves the issues with head/tail const and the issues with class references in the latest proposal. But I have probably missed many issues.

-- 
Oskar
September 11, 2007
Derek Parnell a écrit :
> Thank you. I do remember now ... silly how I couldn't get that from the
> keywords.
> 
> 'const' is sort of a locally write protected.
> 'invariant' is globally write protected.

So am I, each time I see 'invariant' I try to think 'value', which is much more easy to associate with the correct properties:
values are immutable and you can't take the address of a value either, exactly like 'invariant'.

For const, I've not found an easy to memorize keyword, but as D's const are like C++ const (AFAIK), it's not an issue for me.


Of course with a Limbo syntax for variable declaration instead of the weird C-like, the parenthesis would be less(not?) useful..

renoX