March 19, 2007
Andrei Alexandrescu (See Website For Email) wrote:
> Sean Kelly wrote:
> 
>> Andrei Alexandrescu (See Website For Email) wrote:
>>
>>> Don Clugston wrote:
>>>
>>>> Assuming that 'invariant' = really constant, 'const' = C++ pseudo-const...
>>>
>>>
>>> Yah.
>>>
>>>> It's better than super const. However:
>>>> (1) I would want to go through all my existing D code and change 100% of my usages of 'const' to 'invariant'.
>>>
>>>
>>> Great to hear that!
>>
>>
>> Great perhaps, but it does suggest that 'const' should perhaps be the signifier for what 'invariant' has been proposed for (the literal meaning of each term notwithstanding)?
> 
> 
> No. There will be far more uses for const than for invariant. You can take my word for that.
> 
>>>> (2) although (1) could be avoided with the rule that 'const' on a declaration implicitly means 'invariant', this would then mean that to match a 'const' value, you use 'invariant'; but to match a non-const value, you use 'const'. That's horribly confusing.
>>>
>>>
>>> This I don't understand. Could you give an example?
>>
>>
>> invariant int* x;
>> int* y;
>>
>> void fn( const int* z ) {}
>>
>> fn( x );
>> fn( y );
>>
>> Assuming the above is legal, 'const' is the qualifier which binds to all reference types, ie. "to match a non-const value, you use 'const'."
> 
> 
> That is correct.


How about some further example? Given these sigs:

struct Foo
{
   int a;
}

class Bar
{
   char[] b;
}

void fooish (inout Foo foo) {}

char[] barish (Bar bar) {return bar.b;}


1) how do I modify the function decls to have the compiler *enforce* readonly upon the referenced content? e.g. Foo.a and Bar.b cannot be mutated within the function body?

2) how does the fooish() decl change to have the compiler *enforce* a readonly-view of the returned bar.b?

3) where fooish() has a Foo* rather than an inout Foo, is there any change to the modified decl vis-a-vis #1?

- Kris
March 19, 2007
(Reposted after cleanup :o))

kris wrote:
> How about some further example? Given these sigs:
> 
> struct Foo
> {
>    int a;
> }
> 
> class Bar
> {
>    char[] b;
> }
> 
> void fooish (inout Foo foo) {}
> 
> char[] barish (Bar bar) {return bar.b;}
> 
> 
> 1) how do I modify the function decls to have the compiler *enforce* readonly upon the referenced content? e.g. Foo.a and Bar.b cannot be mutated within the function body?

void fooish (const ref Foo foo) {}

char[] barish (const Bar bar) {return bar.b;}

> 2) how does the fooish() decl change to have the compiler *enforce* a readonly-view of the returned bar.b?

const char[] barish (const Bar bar) {return bar.b;}

> 3) where fooish() has a Foo* rather than an inout Foo, is there any change to the modified decl vis-a-vis #1?

void fooish (const Foo* foo) {}

By the way, a couple of days ago I've had an idea. Many people here ask
for a "string" alias because char[] is a tad hard to type and uneasy on
the eyes. Probably a good candidate would be:

alias invariant char[] string;

The resulting type is pretty damn slick:

1. You can alias it and slice it til you get blue in the face - no
confusion ever, because nobody can overwrite the contents underneath
your alias.

2. The EnsureCString(ref string) function will reallocate and copy the
string at most once and only for a slice, never for the "original"
string which was zero-terminated during construction.

3. The ~= operator works (somewhat surprisingly).

4. It's very, very rare that you want to modify some random character in
a string, and when you do, use a char[] and then copy it back into a
string, or rebuild the string from slices!

5. The Java experience (where String is immutable and StringBuffer is
the mutable, build-me-piecemeal object) turned out to be largely
successful, modulo syntax details that are not so elegant.

6. It meshes great with literals, which are exactly invariant arrays of
characters:

string hi = "Hello!"; // no dup needed! And it's 100% safe!

So it looks like we have solved the string issue in an unexpectedly
elegant way - by simply combining two features: invariant and array.


Andrei

March 19, 2007
Andrei Alexandrescu (See Website For Email) Wrote:
> 
> Yes. D's const is transitive.
> 

So:
finall = stage0_nontransitive_const?
const = stage1_transitive_const?

May be beter to separete concepts?

Do we need stage1_const?
Do we need nontransitive_const?

jovo
March 19, 2007
Andrei Alexandrescu (See Website For Email) wrote:
> 
> By the way, a couple of days ago I've had an idea. Many people here ask
> for a "string" alias because char[] is a tad hard to type and uneasy on
> the eyes. Probably a good candidate would be:
> 
> alias invariant char[] string;
> 
> The resulting type is pretty damn slick:
...
> 3. The ~= operator works (somewhat surprisingly).

I assume this is because a char[] is actually a struct containing a pointer, so 'invariant' allows that pointer to be reassigned--it simply protects the referenced string?  This is the sticking point for me. Assume we have this:

    struct Foo
    {
        int  x;
        int* y;
    }

    const Foo foo; // is this legal?
    foo.x  = 1;    // this is allowed
    *foo.y = 2;    // this is not allowed

Is that correct, or do 'const' and 'invariant' truly only apply to pure reference types?

> 4. It's very, very rare that you want to modify some random character in
> a string, and when you do, use a char[] and then copy it back into a
> string, or rebuild the string from slices!

This may be true for a string, but for arrays in general I think this is fairly common.

> 5. The Java experience (where String is immutable and StringBuffer is
> the mutable, build-me-piecemeal object) turned out to be largely
> successful, modulo syntax details that are not so elegant.
> 
> 6. It meshes great with literals, which are exactly invariant arrays of
> characters:
> 
> string hi = "Hello!"; // no dup needed! And it's 100% safe!
> 
> So it looks like we have solved the string issue in an unexpectedly
> elegant way - by simply combining two features: invariant and array.

Agreed.  This is pretty nice :-)


Sean
March 19, 2007

Andrei Alexandrescu (See Website For Email) wrote:
> (Reposted after cleanup :o))
> 
> kris wrote:
>> How about some further example? Given these sigs:
>>
>> struct Foo
>> {
>>    int a;
>> }
>>
>> class Bar
>> {
>>    char[] b;
>> }
>>
>> void fooish (inout Foo foo) {}
>>
>> char[] barish (Bar bar) {return bar.b;}
>>
>>
>> 1) how do I modify the function decls to have the compiler *enforce* readonly upon the referenced content? e.g. Foo.a and Bar.b cannot be mutated within the function body?
> 
> void fooish (const ref Foo foo) {}
> 
> char[] barish (const Bar bar) {return bar.b;}
> 
>> 2) how does the fooish() decl change to have the compiler *enforce* a
>> readonly-view of the returned bar.b?
> 
> const char[] barish (const Bar bar) {return bar.b;}

Hang on... wouldn't that need to be the signature of barish in the first place?  If const is transitive, bar is declared as a const Bar and you're returning bar.b, isn't bar.b const as well?

>> 3) where fooish() has a Foo* rather than an inout Foo, is there any
>> change to the modified decl vis-a-vis #1?
> 
> void fooish (const Foo* foo) {}
> 
> By the way, a couple of days ago I've had an idea. Many people here ask for a "string" alias because char[] is a tad hard to type and uneasy on the eyes. Probably a good candidate would be:
> 
> alias invariant char[] string;
> 
> The resulting type is pretty damn slick:
> 
> 1. You can alias it and slice it til you get blue in the face - no confusion ever, because nobody can overwrite the contents underneath your alias.
> 
> 2. The EnsureCString(ref string) function will reallocate and copy the string at most once and only for a slice, never for the "original" string which was zero-terminated during construction.
> 
> 3. The ~= operator works (somewhat surprisingly).
> 
> 4. It's very, very rare that you want to modify some random character in a string, and when you do, use a char[] and then copy it back into a string, or rebuild the string from slices!
> 
> 5. The Java experience (where String is immutable and StringBuffer is the mutable, build-me-piecemeal object) turned out to be largely successful, modulo syntax details that are not so elegant.
> 
> 6. It meshes great with literals, which are exactly invariant arrays of characters:
> 
> string hi = "Hello!"; // no dup needed! And it's 100% safe!
> 
> So it looks like we have solved the string issue in an unexpectedly elegant way - by simply combining two features: invariant and array.
> 
> 
> Andrei

You're a cruel, cruel man, Andrei.  Now we're going to have to wait patiently for this... :'(

Seriously, though, this is cool stuff.

	-- Daniel

-- 
int getRandomNumber()
{
    return 4; // chosen by fair dice roll.
              // guaranteed to be random.
}

v2sw5+8Yhw5ln4+5pr6OFPma8u6+7Lw4Tm6+7l6+7D i28a2Xs3MSr2e4/6+7t4TNSMb6HTOp5en5g6RAHCP  http://hackerkey.com/
March 19, 2007
Andrei Alexandrescu (See Website For Email) wrote:
> (Reposted after cleanup :o))
> 
> kris wrote:
> 
>> How about some further example? Given these sigs:
>>
>> struct Foo
>> {
>>    int a;
>> }
>>
>> class Bar
>> {
>>    char[] b;
>> }
>>
>> void fooish (inout Foo foo) {}
>>
>> char[] barish (Bar bar) {return bar.b;}
>>
>>
>> 1) how do I modify the function decls to have the compiler *enforce* readonly upon the referenced content? e.g. Foo.a and Bar.b cannot be mutated within the function body?
> 
> 
> void fooish (const ref Foo foo) {}
> 
> char[] barish (const Bar bar) {return bar.b;}
> 
>> 2) how does the fooish() decl change to have the compiler *enforce* a readonly-view of the returned bar.b?
> 
> 
> const char[] barish (const Bar bar) {return bar.b;}
> 
>> 3) where fooish() has a Foo* rather than an inout Foo, is there any change to the modified decl vis-a-vis #1?
> 
> 
> void fooish (const Foo* foo) {}
> 

Good; thanks. BTW: #1 was a tricky question, as Daniel spotted, since it would seem that the above answer permits an escaping mutable ref. I imagine that's not covered in the *cough* scope of the const domain since it can be sidestepped easily?


> By the way, a couple of days ago I've had an idea. Many people here ask
> for a "string" alias because char[] is a tad hard to type and uneasy on
> the eyes. Probably a good candidate would be:
> 
> alias invariant char[] string;
> 
> The resulting type is pretty damn slick:
> 
> 1. You can alias it and slice it til you get blue in the face - no
> confusion ever, because nobody can overwrite the contents underneath
> your alias.
> 
> 2. The EnsureCString(ref string) function will reallocate and copy the
> string at most once and only for a slice, never for the "original"
> string which was zero-terminated during construction.
> 
> 3. The ~= operator works (somewhat surprisingly).
> 
> 4. It's very, very rare that you want to modify some random character in
> a string, and when you do, use a char[] and then copy it back into a
> string, or rebuild the string from slices!
> 
> 5. The Java experience (where String is immutable and StringBuffer is
> the mutable, build-me-piecemeal object) turned out to be largely
> successful, modulo syntax details that are not so elegant.
> 
> 6. It meshes great with literals, which are exactly invariant arrays of
> characters:
> 
> string hi = "Hello!"; // no dup needed! And it's 100% safe!
> 
> So it looks like we have solved the string issue in an unexpectedly
> elegant way - by simply combining two features: invariant and array.


These are some of the reasons why a handful of us have been bitchin' about immutable views for so long.

Regarding the alias, you'd likely have to consider w/dchar variations also ... it becomes a bit messy as things progress, which is one of the reasons we never had a string alias in the first place -- at least now there's more weight to the argument for an alias
March 19, 2007
On Mon, 19 Mar 2007 15:05:29 -0700, Andrei Alexandrescu (See Website For
Email) wrote:


I wish it wasn't so hard to get a simple straight answer from the experts.

So far I think /The W&A Show/ is saying that 'const' and 'invariant' only apply to reference data types (objects, variable-length arrays, and pointers) and structs. They do not apply *in any way, shape or form* to any other data type.

  const char[] s;    // ok
  const char   c;    // meaningless, the 'const' is ignored.
  const classFoo f;  // okay
  const double* b;   // okay;
  const double  d;   // meaningless, the 'const' is ignored.
  const structBar q; // okay

'const' means you cannot change the stuff the reference is referencing or the members of the struct, *but* for reference types you can change the reference value.

  s[0] = 'a'; // fails
  s    = "new data"; //ok
  s.length = 2; // ok

  c    = 'a'; // ok

  f.bar = 'a'; // fails
  f = new classFoo(); // okay

  *b    = 1.0; // fails
  b     = &someDouble; // ok

  d     = 1.0; // okay

  q.foo = 'a'; // fails

'invariant' means you cannot change the stuff the reference is referencing or the members of the struct, *and* for reference types you cannot change the reference value.

 (If you one used 'invariant' rather than 'const' in above declarations...)
  s[0] = 'a'; // fails
  s    = "new data"; // fails
  s.length = 2; // fails

  c    = 'a'; // ok

  f.bar = 'a'; // fails
  f = new classFoo(); // fails

  *b    = 1.0; // fails
  b     = &someDouble; // fails

  d     = 1.0; // okay

  q.foo = 'a'; // fails


However, if this is the interpretation of 'invariant', how does one ever get to set the 'invariant' thing's value?

   invariant byte[] vCodes;
   . . .
   vCodes = LoadCodesFromFile();  // fails, no???


> By the way, a couple of days ago I've had an idea. Many people here ask for a "string" alias because char[] is a tad hard to type and uneasy on the eyes. Probably a good candidate would be:
> 
> alias invariant char[] string;
>
> The resulting type is pretty damn slick:
> 
> 1. You can alias it and slice it til you get blue in the face - no confusion ever, because nobody can overwrite the contents underneath your alias.

 void func( string s )
 {

     string t;
     char[] u;
     u = s;
     u[0] = 'a'; // fails.
     u = s[1..4];
     u[0] = 'a'; // fails
     u = s.dup;
     u[0] = 'a' // ok

     t = s; // fails???

 }


> 3. The ~= operator works (somewhat surprisingly).
 void func( string s )
 {
     string t;
     char[] u;

     u ~= s; // ok
     s ~= 'a'; // fails ???

     t ~= s; // fails???
 }


> 4. It's very, very rare that you want to modify some random character in a string, and when you do, use a char[] and then copy it back into a string, or rebuild the string from slices!

It is not so rare in the type of applications that I do.

Are you saying that I can do this...?

 string func( string s )
 {
     char[] u;
     u = s.dup;
     u[somerandomspot] = 'a';
     return u;
 }

or this ... ?

 void func( string ref s )
 {
     char[] u;
     u = s.dup;
     u[somerandomspot] = 'a';
     s = u;
 }

or this ... ?

 string func( string ref s )
 {
     char[] u;
     string t;
     u = s.dup;
     u[somerandomspot] = 'a';
     t = u;
 }

What I don't get here is the phrase "copy it back into a string". This sounds ambiguous to me. It could mean, "construct a new string containing the updated data", or "replace the original string reference with a reference to the updated data". But I was starting to think that 'invariant' meant that you couldn't change the reference value at all, so I don't see how one could change an 'invariant' parameter or construct an 'invariant' local /variable/.


> So it looks like we have solved the string issue in an unexpectedly elegant way - by simply combining two features: invariant and array.

I hope so.

-- 
Derek
(skype: derek.j.parnell)
Melbourne, Australia
"Justice for David Hicks!"
20/03/2007 9:28:14 AM
March 19, 2007
Derek Parnell wrote:
> I wish it wasn't so hard to get a simple straight answer from the experts.

It's not like anybody tries to obfuscate their writing.

> So far I think /The W&A Show/ is saying that 'const' and 'invariant' only
> apply to reference data types (objects, variable-length arrays, and
> pointers) and structs. They do not apply *in any way, shape or form* to any
> other data type.
> 
>   const char[] s;    // ok
>   const char   c;    // meaningless, the 'const' is ignored.
>   const classFoo f;  // okay
>   const double* b;   // okay;
>   const double  d;   // meaningless, the 'const' is ignored.
>   const structBar q; // okay
> 
> 'const' means you cannot change the stuff the reference is referencing or
> the members of the struct, *but* for reference types you can change the
> reference value.
> 
>   s[0] = 'a'; // fails
>   s    = "new data"; //ok
>   s.length = 2; // ok
> 
>   c    = 'a'; // ok
> 
>   f.bar = 'a'; // fails
>   f = new classFoo(); // okay
> 
>   *b    = 1.0; // fails
>   b     = &someDouble; // ok
> 
>   d     = 1.0; // okay
> 
>   q.foo = 'a'; // fails

Correct.

> 'invariant' means you cannot change the stuff the reference is referencing
> or the members of the struct, *and* for reference types you cannot change
> the reference value.

No. Invariant means that the data referenced indirectly is never modifiable by any part of the program.

What you called 'invariant' above is 'final const'.

[snip]
> However, if this is the interpretation of 'invariant', how does one ever
> get to set the 'invariant' thing's value?
> 
>    invariant byte[] vCodes;
>    . . .    vCodes = LoadCodesFromFile();  // fails, no???

Right now you can only initialize invariant with literals. This aspect of the language is still under development. (Things like the result of some_string.dup also come to mind.)

>  void func( string s )
>  {
> 
>      string t;
>      char[] u;
>      u = s;
>      u[0] = 'a'; // fails.
>      u = s[1..4];
>      u[0] = 'a'; // fails
>      u = s.dup;
>      u[0] = 'a' // ok
> 
>      t = s; // fails???
> 
>  }

You can't make the assignment u = s.

>> 3. The ~= operator works (somewhat surprisingly).
>  void func( string s )
>  {
>      string t;
>      char[] u;
> 
>      u ~= s; // ok
>      s ~= 'a'; // fails ???
> 
>      t ~= s; // fails???
>  }

s ~= a; works and is equivalent to s = s ~ a, i.e. "rebind s to the concatenation of s and a". It's an operation that does not mutate s's contents.

>> 4. It's very, very rare that you want to modify some random character in
>> a string, and when you do, use a char[] and then copy it back into a
>> string, or rebuild the string from slices!
> 
> It is not so rare in the type of applications that I do. 

You use char[] for that, and when you are done modifying, you can put things in a string.

> Are you saying that I can do this...?
> 
>  string func( string s )
>  {
>      char[] u;
>      u = s.dup;
>      u[somerandomspot] = 'a';
>      return u;
>  }

This is sound, but tricky to typecheck. I'll have to get back to you on that one.

> or this ... ?
> 
>  void func( string ref s )
>  {
>      char[] u;
>      u = s.dup;
>      u[somerandomspot] = 'a';
>      s = u;
>  }

Same as above.

> or this ... ?
> 
>  string func( string ref s )
>  {
>      char[] u;
>      string t;
>      u = s.dup;
>      u[somerandomspot] = 'a';
>      t = u;
>  }

These last three examples work because u is temporary and not aliased with anything. If it were, the code would be unsound. Such cases are tricky to typecheck in the general case, but we'll look into a solution that allows such expressive idioms without also taking risks.

> What I don't get here is the phrase "copy it back into a string". This
> sounds ambiguous to me. It could mean, "construct a new string containing
> the updated data", or "replace the original string reference with a
> reference to the updated data". But I was starting to think that
> 'invariant' meant that you couldn't change the reference value at all, so I
> don't see how one could change an 'invariant' parameter or construct an
> 'invariant' local /variable/.

Indeed, 'invariant' meant that the referenced data is invariant. The actual variable can be rebound to any other invariant data. It's like watching TV: you can always change your choice of watching, but you can't modify the actual contents of programs - unless you take special measures :o).


Andrei
March 19, 2007
Andrei Alexandrescu (See Website For Email) wrote:
> By the way, a couple of days ago I've had an idea. Many people here ask
> for a "string" alias because char[] is a tad hard to type and uneasy on
> the eyes. Probably a good candidate would be:
> 
> alias invariant char[] string;
> 

Now that I like. Reminds me very much of Python.

-- 
Kirk McDonald
http://kirkmcdonald.blogspot.com
Pyd: Connecting D and Python
http://pyd.dsource.org
March 19, 2007
kris wrote:
> Andrei Alexandrescu (See Website For Email) wrote:
>> (Reposted after cleanup :o))
>>
>> kris wrote:
>>
>>> How about some further example? Given these sigs:
>>>
>>> struct Foo
>>> {
>>>    int a;
>>> }
>>>
>>> class Bar
>>> {
>>>    char[] b;
>>> }
>>>
>>> void fooish (inout Foo foo) {}
>>>
>>> char[] barish (Bar bar) {return bar.b;}
>>>
>>>
>>> 1) how do I modify the function decls to have the compiler *enforce* readonly upon the referenced content? e.g. Foo.a and Bar.b cannot be mutated within the function body?
>>
>>
>> void fooish (const ref Foo foo) {}
>>
>> char[] barish (const Bar bar) {return bar.b;}
>>
>>> 2) how does the fooish() decl change to have the compiler *enforce* a readonly-view of the returned bar.b?
>>
>>
>> const char[] barish (const Bar bar) {return bar.b;}
>>
>>> 3) where fooish() has a Foo* rather than an inout Foo, is there any change to the modified decl vis-a-vis #1?
>>
>>
>> void fooish (const Foo* foo) {}
>>
> 
> Good; thanks. BTW: #1 was a tricky question, as Daniel spotted, since it would seem that the above answer permits an escaping mutable ref. I imagine that's not covered in the *cough* scope of the const domain since it can be sidestepped easily?

Nobody said all examples must compile :o). In short, you won't be able to go const -> ~const without a cast, and the cast is never defined to harbor defined behavior.

>> By the way, a couple of days ago I've had an idea. Many people here ask
>> for a "string" alias because char[] is a tad hard to type and uneasy on
>> the eyes. Probably a good candidate would be:
>>
>> alias invariant char[] string;
>>
>> The resulting type is pretty damn slick:
>>
>> 1. You can alias it and slice it til you get blue in the face - no
>> confusion ever, because nobody can overwrite the contents underneath
>> your alias.
>>
>> 2. The EnsureCString(ref string) function will reallocate and copy the
>> string at most once and only for a slice, never for the "original"
>> string which was zero-terminated during construction.
>>
>> 3. The ~= operator works (somewhat surprisingly).
>>
>> 4. It's very, very rare that you want to modify some random character in
>> a string, and when you do, use a char[] and then copy it back into a
>> string, or rebuild the string from slices!
>>
>> 5. The Java experience (where String is immutable and StringBuffer is
>> the mutable, build-me-piecemeal object) turned out to be largely
>> successful, modulo syntax details that are not so elegant.
>>
>> 6. It meshes great with literals, which are exactly invariant arrays of
>> characters:
>>
>> string hi = "Hello!"; // no dup needed! And it's 100% safe!
>>
>> So it looks like we have solved the string issue in an unexpectedly
>> elegant way - by simply combining two features: invariant and array.
> 
> 
> These are some of the reasons why a handful of us have been bitchin' about immutable views for so long.
> 
> Regarding the alias, you'd likely have to consider w/dchar variations also ... it becomes a bit messy as things progress, which is one of the reasons we never had a string alias in the first place -- at least now there's more weight to the argument for an alias

Yah. Probably the aliases will come as a troika, string, wstring, and dstring (for lack of more inspired names).


Andrei