www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Clarifying 'const' terminology

reply "Derek Parnell" <derek psych.ward> writes:

understanding about the meaning that is being applied to the term 'const'  
in these recent discussions.

To me, there seems to be two things being talked about.

(a) Data, which once initialized, is not to be modified for the run-time  
life of the application.

(b) Data, which once passed to some function, is not to be modified for  
the run-time life of that function.

Walter's proposed refinement of the 'in' keyword does nothing for type  
(a), and only limited support for type (b), in that it protects data from  
the called function but not from functions that that subsequently calls.  
It is only protection for one level deep. And the only data protected is  
class objects and not other reference types.

Is there any other types (or subtypes) of 'const' meanings being discussed  
or considered?

For example, are we distinguishing between compile-time protection and  
run-time protection?

My requirements for 'const' are almost covered by Walter's new proposal.  
I'm a quite concerned that if I tell Foo() that it can't change something,  
that Foo() can still tell Bar() to disregard my request and tell Bar()  
that it's okay to change it anyway.

I think that apart from array references, type (a) const can be acheived  
through using private members and public properties. I'm not sure how we  
can fully implement (a) with current D semantics.

-- 
Derek Parnell
Melbourne, Australia
Jul 07 2006
next sibling parent reply kris <foo bar.com> writes:
Good idea ~ I'll have a go:

Derek Parnell wrote:

 understanding about the meaning that is being applied to the term 
 'const'  in these recent discussions.
 
 To me, there seems to be two things being talked about.
 
 (a) Data, which once initialized, is not to be modified for the 
 run-time  life of the application.
There are the traditional 'constant' read-only data types, assigned at the point of declaration (e.g. char[]s = "foo";). It used to be that these were located into a distinct area of memory from read/write program variables and so on. This was done (partly) so that the read-only aspect could be trapped at runtime by the executing device. If you try to modify a D string constant on linux, you'll get a runtime error (Win32 D executables let you happily modify such things), so linux is applying read-only attributes to the segment where this constant data lives. However, the distinction is mostly limited to micro-controllers and other devices with limited RAM, but plenty of ROM -- such readonly constants are traditionally stored in ROM. There's a variation on this called "final", where the constant-value is actually determined at runtime. That is, you get one opportunity to assign a value to a final data type (such as a string) and that's it. Such a compiler will attach compile-time errors to additional assignments.
 (b) Data, which once passed to some function, is not to be modified for  
 the run-time life of that function.
This is the realm of where the discussion has been, vis-a-vis mutable and/or immutable attributes. Tis' a shame 'const' is such an overloaded term
 Walter's proposed refinement of the 'in' keyword does nothing for type  
 (a), and only limited support for type (b), in that it protects data 
 from  the called function but not from functions that that subsequently 
 calls.  It is only protection for one level deep. And the only data 
 protected is  class objects and not other reference types.
 
 Is there any other types (or subtypes) of 'const' meanings being 
 discussed  or considered?
Other than retaining the immutable attribute throughout a given call chain, the other primary subtype (I suppose) is the return value of a function or method.
 For example, are we distinguishing between compile-time protection and  
 run-time protection?
For the mutable/immutable aspect, I believe we're talking purely about the compile time aspect. To sum up the whole discussion, it's really about enhancing the contractual obligation between a caller and callee. For example: As it stands, a callee says "I accept a string as an argument" and the compiler traps those cases, at compile time, where a caller does not provide a string: generates a compile-time error. The mutable attribute enhances this so the callee can say "I need a mutable string, because I will modify it", or "I'm happy with just an immutable string since I use it for read-only purposes". A caller is then obligated to fulfill the (tighter) contract, in a manner similar to how it is currently obliged to provide just a string. The benefits of doing so are numerous, and would probably dilute the value of establishing terminology; so we can probably leave it at that for now?
 My requirements for 'const' are almost covered by Walter's new 
 proposal.  I'm a quite concerned that if I tell Foo() that it can't 
 change something,  that Foo() can still tell Bar() to disregard my 
 request and tell Bar()  that it's okay to change it anyway.
Yes, that is a notable weakness. Using an aggregate (struct or class) instead would give you the /propogation/ aspect required. This is in contrast to Walter's proposal, but requires the use of aggregates rather than, say, an array. BTW: this whole discussion is basically about array types, since others can happily be passed by value anyway. The one problem with using aggregates in this manner is related to return values. For example, you can make a String struct or class and control what each method does to the encapsulated content. At some point, though, the content will likely need exposing. Perhaps via a toString() method or via some other means. Simply duplicating the content at this point is a contentious issue, so some means of tagging the return value as 'immutable', and having that usage enforced, would seem to be necessary.
 I think that apart from array references, type (a) const can be 
 acheived  through using private members and public properties. I'm not 
 sure how we  can fully implement (a) with current D semantics.
Did you mean "... fully implement (b) with current D semantics" instead? As I understand things, it is array references that are at stake here. I don't think anything else is?
Jul 07 2006
parent reply Hasan Aljudy <hasan.aljudy gmail.com> writes:
kris wrote:
 Good idea ~ I'll have a go:
 
 Derek Parnell wrote:
 

 understanding about the meaning that is being applied to the term 
 'const'  in these recent discussions.
I am from a C++/Java background, yet I still don't fully understand const! The only thing I understand is that Java doesn't need it because it's higher-level than D, doesn't allow pointer manipulation, and has builtin immutable String class. I still don't understand why D needs it, or why do some people think so.
 <snip>
 My requirements for 'const' are almost covered by Walter's new 
 proposal.  I'm a quite concerned that if I tell Foo() that it can't 
 change something,  that Foo() can still tell Bar() to disregard my 
 request and tell Bar()  that it's okay to change it anyway.
Yes, that is a notable weakness. Using an aggregate (struct or class) instead would give you the /propogation/ aspect required. This is in contrast to Walter's proposal, but requires the use of aggregates rather than, say, an array. BTW: this whole discussion is basically about array types, since others can happily be passed by value anyway.
Then how about introducing a new type: read-only array reference. (The idea is inspired by Java's immutable string class) It would be a distinct type, but built into the language core. It has a different declaration syntax from regular arrays. It cannot be casted to anything at all, absolutly no pointer manipulation is allowed either. The content of the array cannot be modified (further explanation below). The reference itself *can* be modified to refer to another array, but it would still be a read-only reference. Any type of regular array reference (dynamic/static/associative) can be converted to a read-only array reference. For example, the syntax could be: int[$] x; //declare x as a readonly reference to an array of "int"s. using the $ might not be the best thing, but please bare with me .. that's not the main point. From now on, I'm gonna assume there's a nice syntax that's consistent with the rest of the D language. To ensure that the content of the array cannot be modified, some measures must be taken regarding what happens when you read an element from the array. Let's agree first on some terminology: Contained Type: if <code> T[$] x; </code> declares a read-only array reference to an array of T's, then T is the contained type. Now, in theory, the contained type can either be - a native type (int, float, byte ... enums .. etc) - a struct - a class (reference) - an array or a pointer. When an element is read from the array, what should we do? - if the contained type is a native type or an enum, then a copy of the element is returned. - if the contained type is a struct, then a copy is returned. That's simple .. but what if the contained type is a class or an array? Well, if it's a class, then it's a little bit complicated. I don't even know if we /need/ any kind of guarantees with an array of objects, but anyway! Let's assume that we do need this guarantee, and let's take a step back. The compiler shouldn't allow the contained type of a read-only array reference to be a class; instead, it should only allow it to be an interface that's derived from a generic (empty) interface called IReadOnly. This measure will not allow the compiler to always ensure that objects contained in such a way cannot be modified, however, it gives the author of the class a guarantee that if he subclasses IReadOnly the right way, then his objects won't be modifiable. Example: ------------------ interface IImmutableCircle : IReadOnly { int radius(); } class Circle : IImmutableCircle { private int mradius; public: int radius() { returm mradius; } void radius( int r ) { mradius = r; } } .... Circle[$] arr; // error, no class allowed as the contained type for a read-only reference IImutableCircle[$] arr; //ok, since IImmutableCircle derives from IReadOnly --------------------------- Of course the coder can mess up his code and add methods in IImmutableCircle that allow the object to be modified, but the important thing is that that's his own reponsibility; no user of his code can mess this up. If there's a loop-hole in this approach, then disregard it. I'm starting to think that the whole issue of protecting objects is unneeded. A similar approach can be taken if the contained type is an array. Either prevent that and require it to be a read-only array reference, or allow it because preventing it is too complicated or just un-needed. So, what do you think about this proposal? Is it practical? Does it solve most of the const issues? Does it even make sense, or is it just stupid all-together? I was gonna post this as a new topic in the main D ng, but I thought it might have been proposed before, so I present it here instead.
 
 The one problem with using aggregates in this manner is related to 
 return values. For example, you can make a String struct or class and 
 control what each method does to the encapsulated content. At some 
 point, though, the content will likely need exposing. Perhaps via a 
 toString() method or via some other means. Simply duplicating the 
 content at this point is a contentious issue, so some means of tagging 
 the return value as 'immutable', and having that usage enforced, would 
 seem to be necessary.
I think the above proposal should covers most of that. The only thing left is adding a toReadOnlyString() to the Object class, or something like that. A Java-like immutable string type can be declared with char[$] x; // x is an immutable string! Well, it's not really immutable, but the reference is read-only, so the string cannot be modified thru that reference.
Jul 07 2006
next sibling parent reply kris <foo bar.com> writes:
Hasan Aljudy wrote:
[snip]
 Then how about introducing a new type: read-only array reference.
 (The idea is inspired by Java's immutable string class)
 
 It would be a distinct type, but built into the language core.
 It has a different declaration syntax from regular arrays.
 It cannot be casted to anything at all, absolutly no pointer 
 manipulation is allowed either.
 The content of the array cannot be modified (further explanation below).
 The reference itself *can* be modified to refer to another array, but it 
 would still be a read-only reference.
 Any type of regular array reference (dynamic/static/associative) can be 
 converted to a read-only array reference.
Having a true set of readonly array[] types would certainly resolve all such issues that I've personally encountered with D, although some might balk at having another set of TypeInfo? On the other hand, that may be wholly preferable to implementing a type-modifier instead? It is also fully backward compatible ;) [snip other good stuff] Really don't think any special-cases are required here ~ if a readonly array, of any type, is used as an lValue in any manner, it is a compile-time error. Simple, neat, easy to understand. What's missing? * interfacing with extern(C) functions via pointers? * pointers in general, within D? I suspect the above could be handled in a minimalist manner? e.g. I suspect we could probably get by without readonly-pointers in D. I use pointers quite a bit as function arguments, but I can't think of a case where an array wouldn't suffice instead (in order to get immutable arguments). In other words, to simplify things as much as possible we should consider the minimal subset with the most mileage ~ the best bang for the buck * what else?
Jul 07 2006
parent reply Sean Kelly <sean f4.ca> writes:
kris wrote:
 Hasan Aljudy wrote:
 [snip]
 Then how about introducing a new type: read-only array reference.
 (The idea is inspired by Java's immutable string class)

 It would be a distinct type, but built into the language core.
 It has a different declaration syntax from regular arrays.
 It cannot be casted to anything at all, absolutly no pointer 
 manipulation is allowed either.
 The content of the array cannot be modified (further explanation below).
 The reference itself *can* be modified to refer to another array, but 
 it would still be a read-only reference.
 Any type of regular array reference (dynamic/static/associative) can 
 be converted to a read-only array reference.
Having a true set of readonly array[] types would certainly resolve all such issues that I've personally encountered with D, although some might balk at having another set of TypeInfo? On the other hand, that may be wholly preferable to implementing a type-modifier instead? It is also fully backward compatible ;) [snip other good stuff] Really don't think any special-cases are required here ~ if a readonly array, of any type, is used as an lValue in any manner, it is a compile-time error. Simple, neat, easy to understand.
A ReadOnly array would still need to access the contents of a standard array for this to be useful, so it couldn't be completely excluded from evaluation as an lvalue--initializations would be exempt. Sean
Jul 08 2006
parent Hasan Aljudy <hasan.aljudy gmail.com> writes:
Sean Kelly wrote:
 kris wrote:
 
 Hasan Aljudy wrote:
 [snip]

 Then how about introducing a new type: read-only array reference.
 (The idea is inspired by Java's immutable string class)

 It would be a distinct type, but built into the language core.
 It has a different declaration syntax from regular arrays.
 It cannot be casted to anything at all, absolutly no pointer 
 manipulation is allowed either.
 The content of the array cannot be modified (further explanation below).
 The reference itself *can* be modified to refer to another array, but 
 it would still be a read-only reference.
 Any type of regular array reference (dynamic/static/associative) can 
 be converted to a read-only array reference.
Having a true set of readonly array[] types would certainly resolve all such issues that I've personally encountered with D, although some might balk at having another set of TypeInfo? On the other hand, that may be wholly preferable to implementing a type-modifier instead? It is also fully backward compatible ;) [snip other good stuff] Really don't think any special-cases are required here ~ if a readonly array, of any type, is used as an lValue in any manner, it is a compile-time error. Simple, neat, easy to understand.
A ReadOnly array would still need to access the contents of a standard array for this to be useful, so it couldn't be completely excluded from evaluation as an lvalue--initializations would be exempt. Sean
It's actually an array "reference" .. you can make it refer to (point to) any normal array, i.e. you can change what it refers to. You just can't use that reference to change the properties or contents of the array. You can use it to read the contents of the array it refers to. Think of it as an interface with read-only methods and no write methods.
Jul 08 2006
prev sibling next sibling parent BCS <BCS_member pathlink.com> writes:
In article <e8n6v9$2csb$1 digitaldaemon.com>, Hasan Aljudy says...

[...]
I am from a C++/Java background, yet I still don't fully understand const!
The only thing I understand is that Java doesn't need it because it's 
higher-level than D, doesn't allow pointer manipulation, and has builtin 
immutable String class.

I still don't understand why D needs it, or why do some people think so.
[...]
Then how about introducing a new type: read-only array reference.
(The idea is inspired by Java's immutable string class)

It would be a distinct type, but built into the language core.
It has a different declaration syntax from regular arrays.
It cannot be casted to anything at all, absolutly no pointer 
manipulation is allowed either.
The content of the array cannot be modified (further explanation below).
The reference itself *can* be modified to refer to another array, but it 
would still be a read-only reference.
Any type of regular array reference (dynamic/static/associative) can be 
converted to a read-only array reference.
[...] Except for the object stuff and, for the most part, the different declaration syntax, this is just about the same as the const type I have been wanting. A syntax that says "this array can't be changed" would cover about %95+ of what I want. Extending this to direct access to public members of classes and structs would put it up to %99+.
Jul 07 2006
prev sibling parent reply Bruno Medeiros <brunodomedeirosATgmail SPAM.com> writes:
Hasan Aljudy wrote:
 
 
 kris wrote:
 Good idea ~ I'll have a go:

 Derek Parnell wrote:


 understanding about the meaning that is being applied to the term 
 'const'  in these recent discussions.
I am from a C++/Java background, yet I still don't fully understand const! The only thing I understand is that Java doesn't need it because it's higher-level than D, doesn't allow pointer manipulation, and has builtin immutable String class. I still don't understand why D needs it, or why do some people think so.
 <snip>
 My requirements for 'const' are almost covered by Walter's new 
 proposal.  I'm a quite concerned that if I tell Foo() that it can't 
 change something,  that Foo() can still tell Bar() to disregard my 
 request and tell Bar()  that it's okay to change it anyway.
Yes, that is a notable weakness. Using an aggregate (struct or class) instead would give you the /propogation/ aspect required. This is in contrast to Walter's proposal, but requires the use of aggregates rather than, say, an array. BTW: this whole discussion is basically about array types, since others can happily be passed by value anyway.
Then how about introducing a new type: read-only array reference. (The idea is inspired by Java's immutable string class) It would be a distinct type, but built into the language core. It has a different declaration syntax from regular arrays. It cannot be casted to anything at all, absolutly no pointer manipulation is allowed either. The content of the array cannot be modified (further explanation below). The reference itself *can* be modified to refer to another array, but it would still be a read-only reference. Any type of regular array reference (dynamic/static/associative) can be converted to a read-only array reference. For example, the syntax could be: int[$] x; //declare x as a readonly reference to an array of "int"s. using the $ might not be the best thing, but please bare with me .. that's not the main point. From now on, I'm gonna assume there's a nice syntax that's consistent with the rest of the D language. To ensure that the content of the array cannot be modified, some measures must be taken regarding what happens when you read an element from the array. Let's agree first on some terminology: Contained Type: if <code> T[$] x; </code> declares a read-only array reference to an array of T's, then T is the contained type. Now, in theory, the contained type can either be - a native type (int, float, byte ... enums .. etc) - a struct - a class (reference) - an array or a pointer. When an element is read from the array, what should we do? - if the contained type is a native type or an enum, then a copy of the element is returned. - if the contained type is a struct, then a copy is returned. That's simple .. but what if the contained type is a class or an array? Well, if it's a class, then it's a little bit complicated. I don't even know if we /need/ any kind of guarantees with an array of objects, but anyway! Let's assume that we do need this guarantee, and let's take a step back. The compiler shouldn't allow the contained type of a read-only array reference to be a class; instead, it should only allow it to be an interface that's derived from a generic (empty) interface called IReadOnly. This measure will not allow the compiler to always ensure that objects contained in such a way cannot be modified, however, it gives the author of the class a guarantee that if he subclasses IReadOnly the right way, then his objects won't be modifiable. Example: ------------------ interface IImmutableCircle : IReadOnly { int radius(); } class Circle : IImmutableCircle { private int mradius; public: int radius() { returm mradius; } void radius( int r ) { mradius = r; } } ..... Circle[$] arr; // error, no class allowed as the contained type for a read-only reference IImutableCircle[$] arr; //ok, since IImmutableCircle derives from IReadOnly --------------------------- Of course the coder can mess up his code and add methods in IImmutableCircle that allow the object to be modified, but the important thing is that that's his own reponsibility; no user of his code can mess this up. If there's a loop-hole in this approach, then disregard it. I'm starting to think that the whole issue of protecting objects is unneeded. A similar approach can be taken if the contained type is an array. Either prevent that and require it to be a read-only array reference, or allow it because preventing it is too complicated or just un-needed. So, what do you think about this proposal? Is it practical? Does it solve most of the const issues? Does it even make sense, or is it just stupid all-together? I was gonna post this as a new topic in the main D ng, but I thought it might have been proposed before, so I present it here instead.
 The one problem with using aggregates in this manner is related to 
 return values. For example, you can make a String struct or class and 
 control what each method does to the encapsulated content. At some 
 point, though, the content will likely need exposing. Perhaps via a 
 toString() method or via some other means. Simply duplicating the 
 content at this point is a contentious issue, so some means of tagging 
 the return value as 'immutable', and having that usage enforced, would 
 seem to be necessary.
I think the above proposal should covers most of that. The only thing left is adding a toReadOnlyString() to the Object class, or something like that. A Java-like immutable string type can be declared with char[$] x; // x is an immutable string! Well, it's not really immutable, but the reference is read-only, so the string cannot be modified thru that reference.
Indeed, if I understood your proposal correctly, I think it is unpractical, if not down-right senseless. :p Why? Ok, first, what do we want exactly? We want a mechanism that allows us to specify a readonly contract for a variable, that is, declare a variable (or function parameter) that should be a "read-only view" (using Andrei Alexandrescu's term) of another data/object. Your proposal allows us to do that, but one has to encapsulate/declare the var, inside a "constant array of the type of that var"? Why not just declare it as a "constant var of it's respective type"? For example, if I want to declare a function that receives a readonly Object, I have to: ... func(Object[$] obj) { obj[0].doStuff(); int b = obj[0].num; Have to access obj as an array, even though it is only one Object? Why not just: ... func(readonly Object obj) { obj.doStuff(); int b = obj.num; Similarly, on the return type: Object[$] func(...) { obj2 = new Object(); ... // **** Want to return a readonly obj2: **** // Have to allocate an array and encapsulate obj2 inside it? : auto objAR = new Object[$]; // how do I even specify the length?? objAR[0] = obj2; return objAR; } instead of just: readonly Object func(...) { obj2 = new Object(); ... // **** Want to return a readonly obj2: **** return obj2; } Plus, your proposal is omissive in many important aspects. For example, how about the transitive property? If I have: int **[$] cptr; can I change *(cptr[0]) or **(cptr[0])? Similarly for a struct: struct Fuu { int[] iar; } Fuu[$] cfuu; can I change (cfuu[0]).iar[ix] ? (where ix is some index) And your method for determining how one can use a readonly object seems way cumbersome(have to define an interface), and likely less efficient(have to use interfaces). I mean, having to define an interface for each class, with the methods that are allowed in the readonly object?? Compare this to C++ (and Javari), where you just add a keyword to the methods that can be called by a readonly(const) instance: In C++ it is: void aMethod(...) const { ... } In Javari it is: void aMethod(...) readonly { ... } (Note: basically, what is being done above is specifying the constness of the 'this' implicit parameter) I must quote what I've said in the OP: Implementing a mechanism such as this is actually quite complex (in terms of design itself) because of several special cases which need more language constructs. It's not just adding the "immutable" keyword as some people seem to think. To know what I'm talking about exactly one should read this quite informative paper: http://pag.csail.mit.edu/pubs/ref-immutability-oopsla2005-abstract.html I haven't read it all yet, but I plan to do so soon, since everyone is talking about const again. And I would say reading it should likely be mandatory for anyone who wants to venture thinking and posting how the const system should work. It describes the issues one faces when implementing such a system, and the reference immutability system described there is perhaps a good starting point for a system for D BTW, Javari is the Java modification described in that paper, which has reference immutability added to the Java language. -- Bruno Medeiros - CS/E student http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
Jul 08 2006
parent reply Hasan Aljudy <hasan.aljudy gmail.com> writes:
Bruno Medeiros wrote:
 Hasan Aljudy wrote:
<snip proposal>
 So, what do you think about this proposal?
 Is it practical? Does it solve most of the const issues?
 Does it even make sense, or is it just stupid all-together?
<snip>
 
 
 Indeed, if I understood your proposal correctly, I think it is 
 unpractical, if not down-right senseless. :p
 
 Why?
 Ok, first, what do we want exactly? We want a mechanism that allows us 
 to specify a readonly contract for a variable, that is, declare a 
 variable (or function parameter) that should be a "read-only view" 
 (using Andrei Alexandrescu's term) of another data/object.
No, if you check my proposal, it was a reply to kris's statement: "this whole discussion is basically about array types, since others can happily be passed by value anyway." and that is also my understanding. structs, enums, and native types can hapily be passed by value - no const feature needed. classes can provide interfaces for read-only access, or a class can be made immutable - no const feature needed. The only thing left is arrays. Look at Java, it has one of the best/richest/most robust libraries I've ever seen, and it doesn't have any const feature per se. The only thing it has that D doesn't is immutable strings. So, according to my understanding of the issue, if D would have read-only array references, the const issue would mostly be solved.
 
 Your proposal allows us to do that, but one has to encapsulate/declare 
 the var, inside a "constant array of the type of that var"? Why not just 
 declare it as a "constant var of it's respective type"? For example, if 
 I want to declare a function that receives a readonly Object, I have to:
 
   ... func(Object[$] obj) {
      obj[0].doStuff();
      int b = obj[0].num;
No, if you do this, according to my proposal, then you cannot modify the list of "Object" references inside "obj", but there's no guarantee on the actual instances that are referred to by these references.
 Have to access obj as an array, even though it is only one Object?
 Why not just:
 
   ... func(readonly Object obj) {
      obj.doStuff();
      int b = obj.num;
 
 Similarly, on the return type:
 
<snip>
 
   readonly Object func(...) {
     obj2 = new Object();
     ...
 
     // **** Want to return a readonly obj2: ****
     return obj2;
   }
 
I am under the impression that C++ like constness is out of the question <g>
 
 Plus, your proposal is omissive in many important aspects. For example, 
 how about the transitive property? If I have:
   int **[$] cptr;
 can I change *(cptr[0]) or **(cptr[0])? Similarly for a struct:
   struct Fuu { int[] iar; }
   Fuu[$] cfuu;
 can I change (cfuu[0]).iar[ix] ?  (where ix is some index)
you can do what you want with the array reference .. when ever you access an element of the array you're getting a copy of that element. if that element is a pointer, you're getting a copy of the pointer. if you don't want this to happen, don't frickin use pointers when you don't want others to manipulate your array!! <snip>
 
 BTW, Javari is the Java modification described in that paper, which has 
 reference immutability added to the Java language.
 
I need to ask .. what did javari *really* add to java? Is there something that's impossible with java but possible with javari? . . . no? I thought so.
Jul 08 2006
next sibling parent reply Bruno Medeiros <brunodomedeirosATgmail SPAM.com> writes:
Hasan Aljudy wrote:
 
 
 Bruno Medeiros wrote:
 Hasan Aljudy wrote:
<snip proposal>
 So, what do you think about this proposal?
 Is it practical? Does it solve most of the const issues?
 Does it even make sense, or is it just stupid all-together?
<snip>
 Indeed, if I understood your proposal correctly, I think it is 
 unpractical, if not down-right senseless. :p

 Why?
 Ok, first, what do we want exactly? We want a mechanism that allows us 
 to specify a readonly contract for a variable, that is, declare a 
 variable (or function parameter) that should be a "read-only view" 
 (using Andrei Alexandrescu's term) of another data/object.
No, if you check my proposal, it was a reply to kris's statement: "this whole discussion is basically about array types, since others can happily be passed by value anyway." and that is also my understanding.
If that is your premise,then that system would work for it, but I don't agree with such a premise. I have to say I didn't understand Kris's context in the surroundings statements, like the "Using an aggregate (struct or class) instead would give you the /propogation/ aspect required. This is in contrast to Walter's proposal, but requires the use of aggregates rather than, say, an array.", but in any case it seems to me an immutability mechanism is needed for any kind of reference type, like Objects too, not just arrays. I mean, if I have a function that has an Object parameter and I want to specify and enforce that the Object should not be changed in the function, what am I supposed to do?
 BTW, Javari is the Java modification described in that paper, which 
 has reference immutability added to the Java language.
I need to ask .. what did javari *really* add to java? Is there something that's impossible with java but possible with javari? .. .. .. no? I thought so.
Impossible? No, not in the strict sense. But "better", "more powerful", "more expressive", etc., then yes, in my opinion it does. Do refrain from using the word "possible" in comparisons between languages. You'll be asking for the typical reply that any Turing complete language can do the same things as any other such language, so we might as well be using Assembler. -- Bruno Medeiros - CS/E student http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
Jul 09 2006
parent Hasan Aljudy <hasan.aljudy gmail.com> writes:
Bruno Medeiros wrote:
 Hasan Aljudy wrote:
 
 Bruno Medeiros wrote:

 Hasan Aljudy wrote:
<snip proposal>
 So, what do you think about this proposal?
 Is it practical? Does it solve most of the const issues?
 Does it even make sense, or is it just stupid all-together?
<snip>
 Indeed, if I understood your proposal correctly, I think it is 
 unpractical, if not down-right senseless. :p

 Why?
 Ok, first, what do we want exactly? We want a mechanism that allows 
 us to specify a readonly contract for a variable, that is, declare a 
 variable (or function parameter) that should be a "read-only view" 
 (using Andrei Alexandrescu's term) of another data/object.
No, if you check my proposal, it was a reply to kris's statement: "this whole discussion is basically about array types, since others can happily be passed by value anyway." and that is also my understanding.
If that is your premise,then that system would work for it, but I don't agree with such a premise. I have to say I didn't understand Kris's context in the surroundings statements, like the "Using an aggregate (struct or class) instead would give you the /propogation/ aspect required. This is in contrast to Walter's proposal, but requires the use of aggregates rather than, say, an array.", but in any case it seems to me an immutability mechanism is needed for any kind of reference type, like Objects too, not just arrays. I mean, if I have a function that has an Object parameter and I want to specify and enforce that the Object should not be changed in the function, what am I supposed to do?
 BTW, Javari is the Java modification described in that paper, which 
 has reference immutability added to the Java language.
I need to ask .. what did javari *really* add to java? Is there something that's impossible with java but possible with javari? .. .. .. no? I thought so.
Impossible? No, not in the strict sense. But "better", "more powerful", "more expressive", etc., then yes, in my opinion it does. Do refrain from using the word "possible" in comparisons between languages. You'll be asking for the typical reply that any Turing complete language can do the same things as any other such language, so we might as well be using Assembler.
What I meant is .. Java already has one of the most powerfull libraries I've seen (not that I've seen much anyways, mind you). I was wondering what does javari has to offer that java doesn't? or let me say, what does it add? When we talk about 'possibilities' in this context, we're not talking about algorithms. I mean for instance, in the C langauge: void x( int * p ); It's impossible for the function x to detect whether p points to an array or just a single int. and if it's array, it's impossible to know the size of the array.
Jul 09 2006
prev sibling parent BCS <BCS_member pathlink.com> writes:
In article <e8pa25$1ud6$1 digitaldaemon.com>, Hasan Aljudy says...
Bruno Medeiros wrote:
[...]
 
 Plus, your proposal is omissive in many important aspects. For example, 
 how about the transitive property? If I have:
[...]
you can do what you want with the array reference ..
when ever you access an element of the array you're getting a copy of 
that element.
if that element is a pointer, you're getting a copy of the pointer.
if you don't want this to happen, don't frickin use pointers when you 
don't want others to manipulate your array!!
Ok, then how do you pass the common type of char[][] to a function? Const more than one level deep is necessary in my mind. I'd recommend that a const reference can't refer to a non const reference. It has the advantage of being simple and easy to understand. Unless this has been shown to not work, I think that it would be informative to try it for a while. *Question for Walter*: How hard would it be to implement several const models, in parallel (DMD ver 0.162a, 0.163b,...) This would let people try them and see how well they work. [...]
I need to ask .. what did javari *really* add to java?
Is there something that's impossible with java but possible with javari?

no?
I thought so.
Const doesn't make anything new possible, it makes things impossible, and that's the point.
Jul 09 2006
prev sibling parent reply Bruno Medeiros <brunodomedeirosATgmail SPAM.com> writes:
Derek Parnell wrote:

 understanding about the meaning that is being applied to the term 
 'const' in these recent discussions.
 
I think many times people talked a bit vaguely with their meaning of const.
 To me, there seems to be two things being talked about.
 
 (a) Data, which once initialized, is not to be modified for the run-time 
 life of the application.
 
 (b) Data, which once passed to some function, is not to be modified for 
 the run-time life of that function.
 
 Walter's proposed refinement of the 'in' keyword does nothing for type 
 (a), and only limited support for type (b), in that it protects data 
 from the called function but not from functions that that subsequently 
 calls. It is only protection for one level deep. And the only data 
 protected is class objects and not other reference types.
 
 Is there any other types (or subtypes) of 'const' meanings being 
 discussed or considered?
 
 For example, are we distinguishing between compile-time protection and 
 run-time protection?
 
'const', in a general sense, is a mechanism to specify whether a variable/data/object can or can not modified, and enforce that contract (at compile time). 'const' is usually in the sense of (b), that is, some parts of code may modify the data, while others can only read it (thus it is used for ownership management). That's the case for C++. Enforcing the contract means you cannot assign a const variable to a non-const one, or similarly, pass a const variable to a function that expects a non-const argument. The only way to subvert this is with a cast (which should be the only way to subvert anything). There are some variations in const semantics. In C++, const is a type-modifier, meaning that const can be applied to each type element, and not the "variable as whole". Best to give an example. In C++ you can have: const int * iptr = ...; int const * iptr = ...; const int const * iptr = ...; each with different meanings: const int * iptr = ...; // cannot modify the pointed int, can modify the pointer int const * iptr = ...; // can modify the pointed int, cannot modify the pointer (the pointer is const) const int const * iptr = ...; // Cannot modify the pointed int, nor cannot modify the pointer In this usual meaning (C++), there are no guarantees that the data will not actually change (by someone else for example) or that it is a compile constant. (That is why I say the use of this notion of const is for ownership management). In D, 'const' is not a type modifier but what Walter calls a "storage modifier" (or storage attribute), but I think "variable modifier" is perhaps a better term. D's const is applied not to each of many possible type elements, but to the whole variable, like private or other protection attributes (and as such there can be only one const per declaration). D's const also has two different meanings (a bad thing IMO), and they're both different from C++'s: * const var declaration with an initializer: -> the value of the variable is a compile time constant. The variable has no storage, it becomes an alias for it's initializer literal. (and has such one can't take the address of it) * const var declaration without an initializer: -> the var can be initialized in constructors only, and on all other places it's value is read-only. The variable has normal storage. This is what is (much more properly) called 'final' in other languages. There are some more possible variations. There can be a const that has the same meaning as C++'s (you cannot change the var), but which is a variable modifier instead of a type modifier. But in this case there is a prominent issue of whether the read-only/immutability property is transitive or not. For example (calling this behavior 'readonly'): readonly int **ptr; Can one change *ptr and **ptr ? Or just ptr? And there are quite some more issues to consider when implementing a 'const' mechanism such as this one. (see below)
 My requirements for 'const' are almost covered by Walter's new proposal. 
 I'm a quite concerned that if I tell Foo() that it can't change 
 something, that Foo() can still tell Bar() to disregard my request and 
 tell Bar() that it's okay to change it anyway.
 
 I think that apart from array references, type (a) const can be acheived 
 through using private members and public properties. I'm not sure how we 
 can fully implement (a) with current D semantics.
 
 --Derek Parnell
 Melbourne, Australia
I'm assuming that in that that last "(a)" you meant "(b)". Implementing a mechanism such as this is actually quite complex (in terms of design itself) because of several special cases which need more language constructs. It's not just adding the "immutable" keyword as some people seem to think. To know what I'm talking about exactly one should read this quite informative paper: http://pag.csail.mit.edu/pubs/ref-immutability-oopsla2005-abstract.html I haven't read it all yet, but I plan to do so soon, since everyone is talking about const again. And I would say reading it should likely be mandatory for anyone who wants to venture thinking and posting how the const system should work. It describes the issues one faces when implementing such a system, and the reference immutability system described there is perhaps a good starting point for a system for D. -- Bruno Medeiros - CS/E student http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
Jul 08 2006
next sibling parent reply Kirk McDonald <kirklin.mcdonald gmail.com> writes:
Bruno Medeiros wrote:
 Derek Parnell wrote:
 

 understanding about the meaning that is being applied to the term 
 'const' in these recent discussions.
I think many times people talked a bit vaguely with their meaning of const.
 To me, there seems to be two things being talked about.

 (a) Data, which once initialized, is not to be modified for the 
 run-time life of the application.

 (b) Data, which once passed to some function, is not to be modified 
 for the run-time life of that function.

 Walter's proposed refinement of the 'in' keyword does nothing for type 
 (a), and only limited support for type (b), in that it protects data 
 from the called function but not from functions that that subsequently 
 calls. It is only protection for one level deep. And the only data 
 protected is class objects and not other reference types.

 Is there any other types (or subtypes) of 'const' meanings being 
 discussed or considered?

 For example, are we distinguishing between compile-time protection and 
 run-time protection?
'const', in a general sense, is a mechanism to specify whether a variable/data/object can or can not modified, and enforce that contract (at compile time). 'const' is usually in the sense of (b), that is, some parts of code may modify the data, while others can only read it (thus it is used for ownership management). That's the case for C++. Enforcing the contract means you cannot assign a const variable to a non-const one, or similarly, pass a const variable to a function that expects a non-const argument. The only way to subvert this is with a cast (which should be the only way to subvert anything). There are some variations in const semantics. In C++, const is a type-modifier, meaning that const can be applied to each type element, and not the "variable as whole". Best to give an example. In C++ you can have: const int * iptr = ...; int const * iptr = ...; const int const * iptr = ...; each with different meanings: const int * iptr = ...; // cannot modify the pointed int, can modify the pointer int const * iptr = ...; // can modify the pointed int, cannot modify the pointer (the pointer is const) const int const * iptr = ...; // Cannot modify the pointed int, nor cannot modify the pointer
There's another place you can put 'const' in C++: class A { void bar(int i); int foo() const; }; This "const" ensures that the member function A::foo does not modify the instance of A that you are calling it on. (If the function does do so, it is a compile error.) This is an issue when you are dealing with const instances of A: const A a; a.bar(12); // ERROR! a is const, A::bar is not a.foo(); // Okay I actually view this as necessary if we get a way to declare an instance as immutable in D. (Honestly, I have few issues with C++ const, though this "const-by-default for class instances" idea has merit, too.)
 In this usual meaning (C++), there are no guarantees that the data will 
 not actually change (by someone else for example) or that it is a 
 compile constant. (That is why I say the use of this notion of const is 
 for ownership management).
 
 In D, 'const' is not a type modifier but what Walter calls a "storage 
 modifier" (or storage attribute), but I think "variable modifier" is 
 perhaps a better term. D's const is applied not to each of many possible 
 type elements, but to the whole variable, like private or other 
 protection attributes (and as such there can be only one const per 
 declaration). D's const also has two different meanings (a bad thing 
 IMO), and they're both different from C++'s:
 
 * const var declaration with an initializer:
 -> the value of the variable is a compile time constant. The variable 
 has no storage, it becomes an alias for it's initializer literal. (and 
 has such one can't take the address of it)
 
 * const var declaration without an initializer:
 -> the var can be initialized in constructors only, and on all other 
 places it's value is read-only. The variable has normal storage. This is 
 what is (much more properly) called 'final' in other languages.
 
 There are some more possible variations. There can be a const that has 
 the same meaning as C++'s (you cannot change the var), but which is a 
 variable modifier instead of a type modifier. But in this case there is 
 a prominent issue of whether the read-only/immutability property is 
 transitive or not. For example (calling this behavior 'readonly'):
   readonly int **ptr;
 Can one change *ptr and **ptr ? Or just ptr?
 
 And there are quite some more issues to consider when implementing a 
 'const' mechanism such as this one. (see below)
 
 My requirements for 'const' are almost covered by Walter's new 
 proposal. I'm a quite concerned that if I tell Foo() that it can't 
 change something, that Foo() can still tell Bar() to disregard my 
 request and tell Bar() that it's okay to change it anyway.

 I think that apart from array references, type (a) const can be 
 acheived through using private members and public properties. I'm not 
 sure how we can fully implement (a) with current D semantics.

 --Derek Parnell
 Melbourne, Australia
I'm assuming that in that that last "(a)" you meant "(b)". Implementing a mechanism such as this is actually quite complex (in terms of design itself) because of several special cases which need more language constructs. It's not just adding the "immutable" keyword as some people seem to think. To know what I'm talking about exactly one should read this quite informative paper: http://pag.csail.mit.edu/pubs/ref-immutability-oopsla2005-abstract.html I haven't read it all yet, but I plan to do so soon, since everyone is talking about const again. And I would say reading it should likely be mandatory for anyone who wants to venture thinking and posting how the const system should work. It describes the issues one faces when implementing such a system, and the reference immutability system described there is perhaps a good starting point for a system for D.
-- Kirk McDonald Pyd: Wrapping Python with D http://dsource.org/projects/pyd/wiki
Jul 08 2006
parent reply "Derek Parnell" <derek psych.ward> writes:
On Sun, 09 Jul 2006 06:05:11 +1000, Kirk McDonald  
<kirklin.mcdonald gmail.com> wrote:



 There's another place you can put 'const' in C++:

 class A {
      void bar(int i);
      int foo() const;
 };

 This "const" ensures that the member function A::foo does not modify the  
 instance of A that you are calling it on. (If the function does do so,  
 it is a compile error.) This is an issue when you are dealing with const  
 instances of A:

 const A a;

 a.bar(12); // ERROR! a is const, A::bar is not
 a.foo();   // Okay

 I actually view this as necessary if we get a way to declare an instance  
 as immutable in D. (Honestly, I have few issues with C++ const, though  
 this "const-by-default for class instances" idea has merit, too.)
I can see why this would be a good thing in C++, because the declaration of A::foo() and the definition of A::foo() is not necessarily in the same source file or even written by the same person. This style of 'const' is a way to document a design decision in source code and thus have the compiler enforce the design. However, in D they are one and the same thing - a class member must be declared and defined at the same time. So if the design decision is that A.foo() is not allowed to change any of the data members in an A object, there is no way to get the compiler to know about that design decision. So maybe it would be a useful debugging aid. -- Derek Parnell Melbourne, Australia
Jul 08 2006
parent reply Bruno Medeiros <brunodomedeirosATgmail SPAM.com> writes:
Derek Parnell wrote:
 On Sun, 09 Jul 2006 06:05:11 +1000, Kirk McDonald 
 <kirklin.mcdonald gmail.com> wrote:
 
 
 
 There's another place you can put 'const' in C++:

 class A {
      void bar(int i);
      int foo() const;
 };

 This "const" ensures that the member function A::foo does not modify 
 the instance of A that you are calling it on. (If the function does do 
 so, it is a compile error.) This is an issue when you are dealing with 
 const instances of A:

 const A a;

 a.bar(12); // ERROR! a is const, A::bar is not
 a.foo();   // Okay

 I actually view this as necessary if we get a way to declare an 
 instance as immutable in D. (Honestly, I have few issues with C++ 
 const, though this "const-by-default for class instances" idea has 
 merit, too.)
I can see why this would be a good thing in C++, because the declaration of A::foo() and the definition of A::foo() is not necessarily in the same source file or even written by the same person. This style of 'const' is a way to document a design decision in source code and thus have the compiler enforce the design. However, in D they are one and the same thing - a class member must be declared and defined at the same time. So if the design decision is that A.foo() is not allowed to change any of the data members in an A object, there is no way to get the compiler to know about that design decision. So maybe it would be a useful debugging aid. --Derek Parnell Melbourne, Australia
"However, in D they are one and the same thing - a class member must be declared and defined at the same time." Not entirely correct, remember d header files? What happens in D, is that if there is a definition, then there must also be declaration (they're the same actually). But there can be just a definition: class Foo { void func(); } This means that in D, just as C++, there is no way for the compiler to know in all cases whether the func changes or not the object instance. Thus, it is necessary for the user to specify it. -- Bruno Medeiros - CS/E student http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
Jul 09 2006
next sibling parent Hasan Aljudy <hasan.aljudy gmail.com> writes:
Bruno Medeiros wrote:
<snip>
 
 
 "However, in D they are one and the same thing - a class member must be 
 declared and defined at the same time."
 Not entirely correct, remember d header files? What happens in D, is 
 that if there is a definition, then there must also be declaration 
 (they're the same actually). But there can be just a definition:
 
   class Foo {
     void func();
   }
 
 This means that in D, just as C++, there is no way for the compiler to 
 know in all cases whether the func changes or not the object instance. 
 Thus, it is necessary for the user to specify it.
 
However, practically speaking, in D (unlike C++) the interface is probably always derived from the actual implementation.
Jul 09 2006
prev sibling parent BCS <BCS pathlink.com> writes:
Bruno Medeiros wrote:
 Derek Parnell wrote:
 
 On Sun, 09 Jul 2006 06:05:11 +1000, Kirk McDonald 
 <kirklin.mcdonald gmail.com> wrote:



 There's another place you can put 'const' in C++:

 class A {
      void bar(int i);
      int foo() const;
 };

 This "const" ensures that the member function A::foo does not modify 
 the instance of A that you are calling it on. (If the function does 
 do so, it is a compile error.) This is an issue when you are dealing 
 with const instances of A:

 const A a;

 a.bar(12); // ERROR! a is const, A::bar is not
 a.foo();   // Okay

 I actually view this as necessary if we get a way to declare an 
 instance as immutable in D. (Honestly, I have few issues with C++ 
 const, though this "const-by-default for class instances" idea has 
 merit, too.)
I can see why this would be a good thing in C++, because the declaration of A::foo() and the definition of A::foo() is not necessarily in the same source file or even written by the same person. This style of 'const' is a way to document a design decision in source code and thus have the compiler enforce the design. However, in D they are one and the same thing - a class member must be declared and defined at the same time. So if the design decision is that A.foo() is not allowed to change any of the data members in an A object, there is no way to get the compiler to know about that design decision. So maybe it would be a useful debugging aid. --Derek Parnell Melbourne, Australia
"However, in D they are one and the same thing - a class member must be declared and defined at the same time." Not entirely correct, remember d header files? What happens in D, is that if there is a definition, then there must also be declaration (they're the same actually). But there can be just a definition: class Foo { void func(); } This means that in D, just as C++, there is no way for the compiler to know in all cases whether the func changes or not the object instance. Thus, it is necessary for the user to specify it.
Another reason that the compiler can't tell is derived classes. Additionally, letting the compiler check for const methods would requirer the checking algorithm to be specified by D. Some algorithms will say "go" in the example class is const, some will say it isn't. class foo { char[] format; this(char[] f){ format = f.dup; } char[] go(int i) { int j; char[] f = format; // f points to member data foreach(int k, char c; f) if("%" = c) { j = k; break; } if("%" == f[j]) { // copy f (might not be noticed) f = f[0..j+1]~toString(i)~f[j+1..$]; f[j] = " "; // assign to f } return f; } }
Jul 10 2006
prev sibling next sibling parent reply Deewiant <deewiant.doesnotlike.spam gmail.com> writes:
Bruno Medeiros wrote:
 There are some variations in const semantics. In C++, const is a
 type-modifier, meaning that const can be applied to each type element,
 and not the "variable as whole". Best to give an example. In C++ you can
 have:
   const int * iptr = ...;
   int const * iptr = ...;
   const int const * iptr = ...;
 each with different meanings:
 
   const int * iptr = ...;
   // cannot modify the pointed int, can modify the pointer
 
   int const * iptr = ...;
   // can modify the pointed int, cannot modify the pointer (the pointer
 is const)
 
   const int const * iptr = ...;
   // Cannot modify the pointed int, nor cannot modify the pointer
 
Actually, the former two are equivalent. To clarify (equivalent types are paired on one line): const int, int const const int *, int const * const int * const, int const * const int * const There's only one way to write that last one: a constant pointer to a non-constant integer. This is why I always write "const" second when writing C(++): it'd be inconsistent with the way const pointers are written out to do otherwise. Plus, they can be read right-to-left more easily. ("const int * const" reads as "constant pointer to an integer --- no, wait, a constant integer" to me <g>)
Jul 08 2006
parent Bruno Medeiros <brunodomedeirosATgmail SPAM.com> writes:
Deewiant wrote:
 Bruno Medeiros wrote:
 There are some variations in const semantics. In C++, const is a
 type-modifier, meaning that const can be applied to each type element,
 and not the "variable as whole". Best to give an example. In C++ you can
 have:
   const int * iptr = ...;
   int const * iptr = ...;
   const int const * iptr = ...;
 each with different meanings:

   const int * iptr = ...;
   // cannot modify the pointed int, can modify the pointer

   int const * iptr = ...;
   // can modify the pointed int, cannot modify the pointer (the pointer
 is const)

   const int const * iptr = ...;
   // Cannot modify the pointed int, nor cannot modify the pointer
Actually, the former two are equivalent. To clarify (equivalent types are paired on one line): const int, int const const int *, int const * const int * const, int const * const int * const
Oops, that's right, thanks for correcting me. I should have tested it, it's not the first time it got const declarations mixed up, damn C++ ... :p -- Bruno Medeiros - CS/E student http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
Jul 08 2006
prev sibling next sibling parent reply Sean Kelly <sean f4.ca> writes:
Bruno Medeiros wrote:
 Derek Parnell wrote:

 understanding about the meaning that is being applied to the term 
 'const' in these recent discussions.
I think many times people talked a bit vaguely with their meaning of const.
 To me, there seems to be two things being talked about.

 (a) Data, which once initialized, is not to be modified for the 
 run-time life of the application.

 (b) Data, which once passed to some function, is not to be modified 
 for the run-time life of that function.

 Walter's proposed refinement of the 'in' keyword does nothing for type 
 (a), and only limited support for type (b), in that it protects data 
 from the called function but not from functions that that subsequently 
 calls. It is only protection for one level deep. And the only data 
 protected is class objects and not other reference types.

 Is there any other types (or subtypes) of 'const' meanings being 
 discussed or considered?

 For example, are we distinguishing between compile-time protection and 
 run-time protection?
'const', in a general sense, is a mechanism to specify whether a variable/data/object can or can not modified, and enforce that contract (at compile time). 'const' is usually in the sense of (b), that is, some parts of code may modify the data, while others can only read it (thus it is used for ownership management). That's the case for C++. Enforcing the contract means you cannot assign a const variable to a non-const one, or similarly, pass a const variable to a function that expects a non-const argument. The only way to subvert this is with a cast (which should be the only way to subvert anything).
In the realm of compiler optimization techniques, there's another issue as well: can the compiler assume that, because it is evaluating a const reference, that the underlying data will not change while that reference is in use. This is the truly sticky part, and is I think a large reason why Walter hasn't settled on anything yet. For example, consider "const by default": void readWrite( inout T val ) {} void readOnly( in T val ) {} T val; readWrite( inout val ); ... readOnly( val ); In the above, because a mutable reference to val is obtained before readOnly is called, can the compiler assume that the contents of val will not change while readOnly is being evaluated? Can it ever make that assumption for the entire life of the program after readWrite is called?
 There are some variations in const semantics. In C++, const is a 
 type-modifier, meaning that const can be applied to each type element, 
 and not the "variable as whole". Best to give an example. In C++ you can 
 have:
   const int * iptr = ...;
   int const * iptr = ...;
These two are actually identical. Though things can get a bit tricky if 'int' is replaced by a typedef involving a reference type, which is why the latter form tends to be preferred these days--it is unambiguous, unlike the former form.
   const int const * iptr = ...;
 each with different meanings:
I believe this is the same as the above, you merely applied the const to int twice. What you probably meant for these three was: const int * iptr = ...; // mutable ptr to const int int const * iptr = ...; // same as previous line int * const iptr = ...; // const ptr to mutable int const int * const iptr = ...; // const ptr to const int int const * const iptr = ...; // same as previous line To return to the typedef issue for a moment, if you do: typedef char* pchar; template<class T> void func( const T val ) {} pchar val = "hello"; func( val ); then it's not entirely clear whether the val in func is a const pointer to mutable data or vice-versa. Thus the preferred method is: template<class T> void func( T const val ) {} Where it's obvious that the 'const' will apply to the pointer if one exists. Sean
Jul 08 2006
next sibling parent reply "Derek Parnell" <derek psych.ward> writes:
On Sun, 09 Jul 2006 08:03:52 +1000, Sean Kelly <sean f4.ca> wrote:



 In the realm of compiler optimization techniques, there's another issue  
 as well: can the compiler assume that, because it is evaluating a const  
 reference, that the underlying data will not change while that reference  
 is in use.
Just to clarify, are you saying that while a function that was passed a 'const' reference is running, can the compiler assume that so other concurrently running function will change the passed data?
 This is the truly sticky part, and is I think a large reason why Walter  
 hasn't settled on anything yet.  For example, consider "const by  
 default":

      void readWrite( inout T val ) {}
      void readOnly( in T val ) {}

      T val;

      readWrite( inout val );
      ...
      readOnly( val );

 In the above, because a mutable reference to val is obtained before  
 readOnly is called, can the compiler assume that the contents of val  
 will not change while readOnly is being evaluated?  Can it ever make  
 that assumption for the entire life of the program after readWrite is  
 called?
If functions are not being run concurrently, I think it can. Because while the code in readOnly() is executing, how can code in other functions execute if they are not called somehow by readOnly itself? Or am I missing something (again <g>). -- Derek Parnell Melbourne, Australia
Jul 08 2006
parent Sean Kelly <sean f4.ca> writes:
Derek Parnell wrote:
 On Sun, 09 Jul 2006 08:03:52 +1000, Sean Kelly <sean f4.ca> wrote:
 
 
 
 In the realm of compiler optimization techniques, there's another 
 issue as well: can the compiler assume that, because it is evaluating 
 a const reference, that the underlying data will not change while that 
 reference is in use.
Just to clarify, are you saying that while a function that was passed a 'const' reference is running, can the compiler assume that so other concurrently running function will change the passed data?
Yes, be it in another thread or merely called through an opaque function pointer inside the const-qualified code. But at some point I think the user simply has to promise not to be evil so the compiler can go on with its job. I'm just not sure exactly where the line should be drawn. Sean
Jul 08 2006
prev sibling next sibling parent Dave <Dave_member pathlink.com> writes:
Sean Kelly wrote:
 Bruno Medeiros wrote:
 Derek Parnell wrote:

 understanding about the meaning that is being applied to the term 
 'const' in these recent discussions.
I think many times people talked a bit vaguely with their meaning of const.
 To me, there seems to be two things being talked about.

 (a) Data, which once initialized, is not to be modified for the 
 run-time life of the application.

 (b) Data, which once passed to some function, is not to be modified 
 for the run-time life of that function.

 Walter's proposed refinement of the 'in' keyword does nothing for 
 type (a), and only limited support for type (b), in that it protects 
 data from the called function but not from functions that that 
 subsequently calls. It is only protection for one level deep. And the 
 only data protected is class objects and not other reference types.

 Is there any other types (or subtypes) of 'const' meanings being 
 discussed or considered?

 For example, are we distinguishing between compile-time protection 
 and run-time protection?
'const', in a general sense, is a mechanism to specify whether a variable/data/object can or can not modified, and enforce that contract (at compile time). 'const' is usually in the sense of (b), that is, some parts of code may modify the data, while others can only read it (thus it is used for ownership management). That's the case for C++. Enforcing the contract means you cannot assign a const variable to a non-const one, or similarly, pass a const variable to a function that expects a non-const argument. The only way to subvert this is with a cast (which should be the only way to subvert anything).
In the realm of compiler optimization techniques, there's another issue as well: can the compiler assume that, because it is evaluating a const reference, that the underlying data will not change while that reference is in use. This is the truly sticky part, and is I think a large reason why Walter hasn't settled on anything yet. For example, consider "const by default": void readWrite( inout T val ) {} void readOnly( in T val ) {} T val; readWrite( inout val ); ... readOnly( val ); In the above, because a mutable reference to val is obtained before readOnly is called, can the compiler assume that the contents of val will not change while readOnly is being evaluated? Can it ever make that assumption for the entire life of the program after readWrite is called?
For either const by default or most of Walter's 'in' proposal (for byref function parameters), there may be a solution at the bottom of: http://www.digitalmars.com/drn-bin/wwwnews?digitalmars.D/39757 It does rely on a non-release-build runtime check (just like array bounds checking) so it isn't perfect, but at least it's consistent with other language and reference implementation features, and as such I think the optimization techniques you mention could be safely applied for 'const' parameters. That would truly be cool IMHO. I think this would make const-by-default or 'in' parameters a lot easier to debug and safer for passing byref args. around in multi-threaded apps. too. Even for non-release builds the assert(x !is y) would be substantially faster than bounds checking in simple loops so the overhead isn't that terrible.
 There are some variations in const semantics. In C++, const is a 
 type-modifier, meaning that const can be applied to each type element, 
 and not the "variable as whole". Best to give an example. In C++ you 
 can have:
   const int * iptr = ...;
   int const * iptr = ...;
These two are actually identical. Though things can get a bit tricky if 'int' is replaced by a typedef involving a reference type, which is why the latter form tends to be preferred these days--it is unambiguous, unlike the former form.
   const int const * iptr = ...;
 each with different meanings:
I believe this is the same as the above, you merely applied the const to int twice. What you probably meant for these three was: const int * iptr = ...; // mutable ptr to const int int const * iptr = ...; // same as previous line int * const iptr = ...; // const ptr to mutable int const int * const iptr = ...; // const ptr to const int int const * const iptr = ...; // same as previous line To return to the typedef issue for a moment, if you do: typedef char* pchar; template<class T> void func( const T val ) {} pchar val = "hello"; func( val ); then it's not entirely clear whether the val in func is a const pointer to mutable data or vice-versa. Thus the preferred method is: template<class T> void func( T const val ) {} Where it's obvious that the 'const' will apply to the pointer if one exists. Sean
Jul 08 2006
prev sibling parent Bruno Medeiros <brunodomedeirosATgmail SPAM.com> writes:
Sean Kelly wrote:
 Bruno Medeiros wrote:
 Derek Parnell wrote:

 understanding about the meaning that is being applied to the term 
 'const' in these recent discussions.
I think many times people talked a bit vaguely with their meaning of const.
 To me, there seems to be two things being talked about.

 (a) Data, which once initialized, is not to be modified for the 
 run-time life of the application.

 (b) Data, which once passed to some function, is not to be modified 
 for the run-time life of that function.

 Walter's proposed refinement of the 'in' keyword does nothing for 
 type (a), and only limited support for type (b), in that it protects 
 data from the called function but not from functions that that 
 subsequently calls. It is only protection for one level deep. And the 
 only data protected is class objects and not other reference types.

 Is there any other types (or subtypes) of 'const' meanings being 
 discussed or considered?

 For example, are we distinguishing between compile-time protection 
 and run-time protection?
'const', in a general sense, is a mechanism to specify whether a variable/data/object can or can not modified, and enforce that contract (at compile time). 'const' is usually in the sense of (b), that is, some parts of code may modify the data, while others can only read it (thus it is used for ownership management). That's the case for C++. Enforcing the contract means you cannot assign a const variable to a non-const one, or similarly, pass a const variable to a function that expects a non-const argument. The only way to subvert this is with a cast (which should be the only way to subvert anything).
In the realm of compiler optimization techniques, there's another issue as well: can the compiler assume that, because it is evaluating a const reference, that the underlying data will not change while that reference is in use. This is the truly sticky part, and is I think a large reason why Walter hasn't settled on anything yet. For example, consider "const by default": void readWrite( inout T val ) {} void readOnly( in T val ) {} T val; readWrite( inout val ); ... readOnly( val ); In the above, because a mutable reference to val is obtained before readOnly is called, can the compiler assume that the contents of val will not change while readOnly is being evaluated? Can it ever make that assumption for the entire life of the program after readWrite is called?
Yes, but one has to realize that that is a different thing from the readonly view type of immutability, which is used for ownership management. You can't have 'const' or any other keyword mean both things. C++'s 'const' has only the meaning of a readonly view type of contract. It does not mean the data cannot change (although in case of a value type, it indeed can't). I recall the discussion between Walter and Alexei where Walter complained that C++'s const was not very useful because it didn't guarantee data was non-changing. Alexei took a while to make Walter understand that that was not the purpose and usefulness of the kind of const/language-construct that he was considering important (which was the readonly view construct).
 There are some variations in const semantics. In C++, const is a 
 type-modifier, meaning that const can be applied to each type element, 
 and not the "variable as whole". Best to give an example. In C++ you 
 can have:
   const int * iptr = ...;
   int const * iptr = ...;
These two are actually identical. Though things can get a bit tricky if 'int' is replaced by a typedef involving a reference type, which is why the latter form tends to be preferred these days--it is unambiguous, unlike the former form.
   const int const * iptr = ...;
 each with different meanings:
I believe this is the same as the above, you merely applied the const to int twice. What you probably meant for these three was: const int * iptr = ...; // mutable ptr to const int int const * iptr = ...; // same as previous line int * const iptr = ...; // const ptr to mutable int const int * const iptr = ...; // const ptr to const int int const * const iptr = ...; // same as previous line
Yes, Deewiant had pointed that out already. ;)
 To return to the typedef issue for a moment, if you do:
 
     typedef char* pchar;
 
     template<class T> void func( const T val ) {}
 
     pchar val = "hello";
 
     func( val );
 
 then it's not entirely clear whether the val in func is a const pointer 
 to mutable data or vice-versa.  Thus the preferred method is:
 
     template<class T> void func( T const val ) {}
 
 Where it's obvious that the 'const' will apply to the pointer if one 
 exists.
 
 
 Sean
Another of C++'s subtle trickinesses... I'll keep that in mind, if I have to deal with it in the future. -- Bruno Medeiros - CS/E student http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
Jul 09 2006
prev sibling parent "Derek Parnell" <derek psych.ward> writes:
On Sun, 09 Jul 2006 05:45:50 +1000, Bruno Medeiros  
<brunodomedeirosATgmail SPAM.com> wrote:

 Derek Parnell wrote:
 To me, there seems to be two things being talked about.
  (a) Data, which once initialized, is not to be modified for the  
 run-time life of the application.
  (b) Data, which once passed to some function, is not to be modified  
 for the run-time life of that function.
 I'm not sure how we can fully implement (a) with current D semantics.
 I'm assuming that in that that last "(a)" you meant "(b)".
No actually. I really did mean (a). So far as I understand it, all the discussions of 'const' still fall into either of these two categories. I know that compile-time constants are in category (a). Both styles of constants, as implemented in D, are of type (a) because once inititialized their value is not allowed to be changed ever by anything. Another type of 'const' that falls into (a) is the 'const' returned value. I see that as being that some function creates data and asks that no one change it while the data exists. This type of (a) is the one that I can't see how to implement in current D. The (b) category is easy to implement in D currently. Just pass a copy of the data to a function, then the function, and other functions that it may call, can play with the copy until it's heart's content with no harm done (apart from side effects <g>). But this method is can obviously be a performance hit, plus it doesn't help track down mistakes in the called functions - that is when the called functions accidently attempt to change data it knows it shouldn't. So in summary, I believe that D does not completely implement either of the two 'const' categories at all. And everyone, including Walter, seems to agree on that point. What there seems to be some disagreement on is whether or not D is obliged to implement 'const'. -- Derek Parnell Melbourne, Australia
Jul 08 2006