March 19, 2007 Re: Extended Type Design: further examples | ||||
---|---|---|---|---|
| ||||
Posted in reply to Andrei Alexandrescu (See Website For Email) | 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 Re: Extended Type Design: further examples | ||||
---|---|---|---|---|
| ||||
Posted in reply to kris | (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 Re: Extended Type Design. | ||||
---|---|---|---|---|
| ||||
Posted in reply to Andrei Alexandrescu (See Website For Email) | 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 Re: Extended Type Design: further examples | ||||
---|---|---|---|---|
| ||||
Posted in reply to Andrei Alexandrescu (See Website For Email) | 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 Re: Extended Type Design: further examples | ||||
---|---|---|---|---|
| ||||
Posted in reply to Andrei Alexandrescu (See Website For Email) | 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 Re: Extended Type Design: further examples | ||||
---|---|---|---|---|
| ||||
Posted in reply to Andrei Alexandrescu (See Website For Email) | 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 Re: Extended Type Design: further examples | ||||
---|---|---|---|---|
| ||||
Posted in reply to Andrei Alexandrescu (See Website For Email) | 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 Re: Extended Type Design: further examples | ||||
---|---|---|---|---|
| ||||
Posted in reply to Derek Parnell | 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 Re: Extended Type Design: further examples | ||||
---|---|---|---|---|
| ||||
Posted in reply to Andrei Alexandrescu (See Website For Email) | 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 Re: Extended Type Design: further examples | ||||
---|---|---|---|---|
| ||||
Posted in reply to kris | 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 |
Copyright © 1999-2021 by the D Language Foundation