D - in,out,inout: new sematic proposal
- Farmer (286/286) Oct 30 2002 The design goals of D look really promising to me. [But ...]
- Sandor Hojtsy (64/333) Oct 31 2002 Interesting proposal.
- antti.sykari housemarque.fi (5/22) Oct 31 2002 This seems to be a matter of taste - in "Refactoring", Martin Fowler
- Farmer (239/279) Nov 01 2002 Subject: in,out,inout: new sematic proposal
- Sandor Hojtsy (104/312) Nov 05 2002 Conceptually, the "generated code" should not use the calleeVar paramete...
- Farmer (147/268) Nov 05 2002 Hi,
- Sandor Hojtsy (59/211) Nov 06 2002 Warning. Quite a long letter to go.
- Farmer (59/164) Nov 06 2002 ???
- Walter (31/59) Nov 30 2002 1) It does not aid optimization. For example, if you have the following:
- Robert W. Cunningham (102/141) Dec 02 2002 A systems programming language (SPL) requires two fundamental features
- Evan McClanahan (17/26) Dec 03 2002 First off, I'm in the C++ const is bad camp. It has too many variant
- Walter (20/38) Dec 14 2002 I am intimately familiar with how C compilers generate code, with and
- Russell Lewis (11/15) Dec 16 2002 Assembly language doesn't have 'const' or anything equivalent. So how
- Pedro Alves (8/9) Mar 09 2003 Well, in C, volatile is usefull when I have a variable that is a represe...
- Mike Wynn (8/17) Mar 09 2003 representation
- Farmer (28/33) Jan 13 2003 I have a simple question about current C++/D compiler implementations, j...
- Daniel Yokomiso (114/147) Jan 14 2003 just
- Farmer (68/95) Jan 19 2003 Hi,
The design goals of D look really promising to me. [But ...] Lately, I've seen a lot of complaints about the in/out semantic for D- functions. So I hope to provide a clean, easy to understand/maintain, bug preventing and efficient way of parameter passing. Note: All my assumptions are based on the current D spec and the DMD 0.46 compiler. All compiler implemention details that are mentioned in this proposal, are only meant to clarifie the semantic. Programmers (that includes compiler writers) need not care about these details. Hint: It might be better to read this post by starting from the end (there's the summary). It might be best to read this post, not at all :-) Enough talking, here it goes: 1a.) primitive types - in By primitive types I mean, int, shorts,char, float and pointers, etc. Primitive types are considered as value types. Semantic: The function receives a value, this value cannot be modified by the function. D: void foo(in short var) translates to C: void foo(const short var) Value types that are passed as in-parameters to functions cannot be changed within the function. Rationale: I assume that the only reason for the current semantic in C is: Parameters were (when C was invented) pushed onto the stack, and programmers could reuse the already allocate stack space, so non-optimizing compilers will produce better code. Today even the worst optimizing compiler (sth. like Borlands RAD style C++- Compiler) will produce identical code for this example: C-code: void foo(int parm) { if (parm < 0) parm=47; // provide some default value for further use } void foo(const int parm) { int maxlines=parm; if (parm < 0) maxlines=47; // provide some default value for further use } 1b.) primitive types - out/inout D: void foo(out short var) would translate to C: void foo(short* var) or C++: void foo(short var&) out: The function returns a value to the callee. inout: The function gets a value that the function can modify. Note: The difference between out and inout is well documented in Walter's spec. Note about pointers: The in/out/inout storage class (word taken from the D spec) ALWAYS refers to the physical value of a pointer (that stuff that C programmers tend to cast to an int type and back ;-). One might want to have some more control of the stuff referenced by pointers, but I say NO. Rationale: Pointers are a minor feature of D. Pointers feel right for C and C++ (I like them there). But in D they are much more bug prone; you would not use pointers in a function-interface, anyway. So I just don't care much about them. 2a.)fixed sized arrays - in Fixed sized arrays are value types, so the in/out/inout semantic is the same as for primitive types. D: void foo(in short[5] var) translates to C: void foo(const short* var) D: void foo(in short[5] var) { var[2]=5; // error: 'var' cannot be an lvalue } 2b.)fixed sized arrays - out/inout D: void foo(out short[5] var) translates to C: void foo(short* const var) example: D: void foo(out short[5] var) { var[2]=5; // ok; short[5] tmp; var[]=tmp[]; // ok, since D compiler makes a copy of 'tmp', right? } Note: According to the DMD 0.46 version the out and inout attribute is not allowed for fixed sized arrays. (waste of keywords, in my opinion - it's there so use it!) 3a.) structs - in Structs are value types, so the semantic of in,out,inout is same as for primitive types and fixed sized arrays. Note: All value types can be allocated on the stack. DMD Compiler (V0.46) allocates structs and fixed sized arrays onto the stack. So I suppose, Walter wants them to be value types. struct Point { int x,y; void setX(int x) {this.x=x;} } D: void foo(in Point var) translates to C: void foo(const Point* var) or C++: void foo(const Point& var) Rationale: [I can hardly think of a case where C++: void foo(Point var) is useful, when you see such a declaration, it is due to lazyness of (some) C++-programmers. Why would anyone want to pay the costs of copying a struct, when C++: void foo(const Point& var) provides the same safety? (Maybe one could save one indirection in the function body ? But I don't think that this could make code runs faster.)] In D (with this proposal) in,out and inout, DOES NOT specify whether parameter passing is by reference or value. The compiler just picks the most efficient one that complys with semantic that belongs to the storage class attribute. (Un)fortunately there is a little problem for current D: The function can call a function of a struct, which could change the struct. This is not a big issue, there a several ways to tackle this problem, e.g.: -ignore it: compiler does not ensure constness, the programmer is paid to take care of this. ( would be perfectly acceptible for an alpha compiler, I think) -let the programmer flag function members of structs, whether they are const or not (similar to C++). -let the compiler figure it out itself -do not allow functions for structs at all (classes are better for functions anyway, but struct might be faster due to stack allocation) I do not address this issue here, because first I want Walter answer some questions regarding "const" at the end of this post. 3b.) structs - out/inout struct Point { int x,y; } D: void foo(inout Point var) translates to C: void foo(Point* var) or C++: void foo(Point& var) No explanation, you already got the point, for sure. 4a.)Objects - in Objects are reference types. The storage modifiers apply to the reference handle, since you can only pass reference handles arround (you cannot copy a Object, just its handle), so you are always working on the handle, except when using the '.'operator. Note: Objects are always allocated in the garbage collected heap. class Logger {} D: void foo(in Logger o) translates to C++: void foo(Logger& const o) Rationale: Highly paid Java-programmers often write the following (but not me, I'm lowly paid ;-) Java (with non-OO extensions): class Result { int value; } /** * returns a Result object from the Database * return 0 if error; 7 if strange error; 1 on success */ int getFromDB(Result o) { o=new Result(); o.value=47; // BUG; callee's object is not changed! return 1; } in D: int getFromDB(in Result o) { o=new Result(); // compiler error: 'o' cannot be an lvalue, o.value=47; return 1; } 4b.)Objects - out/inout class Logger {} D: void foo(out Logger o) translates to C++: void foo(Logger*& o) Rationale: -Allows to do sth. that Java cannot do ;-) Return multiple new objects from a function. -There is currently no concept for const for classes in D anyway. So there is no other way to do it. 5a.) dynamic/assoziative Arrays - in Dynamic Arrays are kind of reference types. (They are not true reference types) Note: The elements of dynamic/assoziative Arrays is always allocate in the garbage collected heap. A very small part, namely the length is allocate onto the stack. D: void foo(in list[]) translates to C: void foo(listData* const, const int listLength) Rationale: Dynamic Arrays are kind of reference type, so in/out/inout has the same arrays are Objects, but they are not (in D). With the proposed parameter passing semantic, passing arrays will have the same semantic as in Java or or return an array with a changed length). So these programmers will feel at home. I understand that from a purly practical viewpoint, one might wish to make the entire list const. But having a clean language will prove to be more worthy. 5b.) dynamic/assoziative Arrays - out/inout D: void foo(out list[]) translates to C++: void foo(listData*, int listLength&) SUMMARY ------- value types: primitive types, pointers, fixed sized arrays and structs: ----------- 'in': Callee 'sends' a value to the function. The function can NEITHER return a changed value to the callee, NOR can it change the local copy of this value that might exist for some combinations of value type and compiler implementation. 'out': Function returns a value to the callee. The callee provides the memory into which the function stores the return value. 'inout': Callee 'sends' a value to the function. The function can read and change value. The function operates directly on the memory, which the callee uses to 'send' the value to the function. reference types: objects, dynamic/assoziative Arrays ("kind of" only) --------------- 'in': Callee 'sends' a reference handle to the function. The function can can change the referenced element(s), but not it cannot change the reference handle. (Consequently the function cannot return a new a reference handle object to the callee.) 'out': Function returns a reference handle to the callee. (Consequently the function must return a new reference object to the callee.) 'inout': Callee 'sends' a reference handle to the function. The function can read and change the referenced element(s). Furthermore it can change the reference handle. (Consequently the function can return a new reference object to the callee.) Learning rule ------------- The storage specifiers in, out and inout always refer to the lowest possible level of indirection, an assembler programmer can think of. Learning advice for non-programmers* ------------- Think up the parameters of functions, as it is taught to students in their university lessons (with in, out and maybe in out, of course). Never think about references and constness, as C++-programmers do ! Never think about all that wired stuff that is mentioned in this proposal ! Now use the conceived attributes straight with you D-function, it should work. (Except for fixed sized arrays, because students are taught to not use them) languages, so these instruction will not work for them. -------------------------------------------------------------------------- Questions about "C++ const" to Walter: ------------------------------------- Though I did not find anything in the D spec (this night) regarding "C++ const" , I do remember that you (Walter) said, that the "const" concept of C++ does not really pay off - so you do not want to provide a similar concept for D. My problem is, that I did not get the point what the bad points of "C++ const" are EXACTLY. Here are my assumptions about bad points of the "C++ const" concept that you might have. 1. Const does not pay off, because to few C++ programmers actually use it. You can not use const objects with frameworks, because they have to many non-const functions. 2. Const does not pay off, because when programming with large frameworks/codebase, too many functions are inherently non-const: A reference to a const object is almost useless, because at some point you simply must call one of these non-const functions. So, you end up using const casts all the time. (alternatively you can store two versions of every reference) 3. It is too disheartening that const inherently is not usefull for good optimizations. 3. It is too disheartening that const cannot be used for optimizations, because the C++ semantic of const/the common programming practice with const in C++, prevent any optimizations. 4. Const clutteres the declarations of functions and reference types too much. 5. Const complicates the rules for function overloading, function overriding, class inheritence and interface inheritance, etc. too much. 6. It is too difficult to make a compiler that checks for constness. Have a nice D.
Oct 30 2002
Interesting proposal. Yet, I think it leaves several things unanswered. See below. "Farmer" <itsFarmer. freenet.de> wrote in message news:Xns92B816E513F51itsFarmer 63.105.9.61...The design goals of D look really promising to me. [But ...] Lately, I've seen a lot of complaints about the in/out semantic for D- functions. So I hope to provide a clean, easy to understand/maintain, bug preventing and efficient way of parameter passing. Note: All my assumptions are based on the current D spec and the DMD 0.46 compiler. All compiler implemention details that are mentioned in this proposal, are only meant to clarifie the semantic. Programmers (that includes compiler writers) need not care about these details.*snap*1a.) primitive types - in By primitive types I mean, int, shorts,char, float and pointers, etc. Primitive types are considered as value types. Semantic: The function receives a value, this value cannot be modified by the function. D: void foo(in short var) translates to C: void foo(const short var) Value types that are passed as in-parameters to functions cannot bechangedwithin the function. Rationale: I assume that the only reason for the current semantic in C is: Parameters were (when C was invented) pushed onto the stack, and programmers could reuse the already allocate stack space, sonon-optimizingcompilers will produce better code. Today even the worst optimizing compiler (sth. like Borlands RAD styleC++-Compiler) will produce identical code for this example: C-code: void foo(int parm) { if (parm < 0) parm=47; // provide some default value for further use } void foo(const int parm) { int maxlines=parm; if (parm < 0) maxlines=47; // provide some default value for further use }Comparing those two functions, I think the first one is cleaner. You don't need to give two names (variables) to the same concept, which you can later use intermixed. And you can avoid an extra line. I think this is a good example for allowing changing privitive types as "in" parameter.1b.) primitive types - out/inout D: void foo(out short var) would translate to C: void foo(short* var)You mean void foo(short* const var)or C++: void foo(short var&) out: The function returns a value to the callee. inout: The function gets a value that the function can modify. Note: The difference between out and inout is well documented in Walter's spec.*snap*2a.)fixed sized arrays - in Fixed sized arrays are value types, so the in/out/inout semantic is the same as for primitive types. D: void foo(in short[5] var) translates to C: void foo(const short* var)You mean void foo(const short* const var)D: void foo(in short[5] var) { var[2]=5; // error: 'var' cannot be an lvalue } 2b.)fixed sized arrays - out/inout D: void foo(out short[5] var) translates to C: void foo(short* const var) example: D: void foo(out short[5] var) { var[2]=5; // ok; short[5] tmp; var[]=tmp[]; // ok, since D compiler makes a copy of 'tmp', right? } Note: According to the DMD 0.46 version the out and inout attribute is not allowed for fixed sized arrays. (waste of keywords, in my opinion - it's there so use it!)Since you can change array contents of "in" parameters, what more could "out" do for fixed size arrays?3a.) structs - in Structs are value types, so the semantic of in,out,inout is same as for primitive types and fixed sized arrays. Note: All value types can be allocated on the stack. DMD Compiler (V0.46) allocates structs and fixed sized arrays onto the stack. So I suppose, Walter wants them to be value types. struct Point { int x,y; void setX(int x) {this.x=x;} } D: void foo(in Point var) translates to C: void foo(const Point* var)You mean void foo(const Point* const var)or C++: void foo(const Point& var) Rationale: [I can hardly think of a case where C++: void foo(Point var) is useful, when you see such a declaration, it is due to lazyness of(some)C++-programmers. Why would anyone want to pay the costs of copying a struct, when C++: void foo(const Point& var) provides the same safety? (Maybe one could save one indirection in the function body ? But I don't think that this could make code runs faster.)]*No.* Point move(Point a, int x, int y) { a.x += x; a.y += y; return a; } Is cleaner and *faster* than: Point move(const Point &a, int x, int y) { Point result = a; result.x += x; result.y += y; return result; }In D (with this proposal) in,out and inout, DOES NOT specify whether parameter passing is by reference or value. The compiler just picks the most efficient one that complys with semantic that belongs to the storage class attribute.I don't like this. Withouth specification, you can not rely on the different semantic behaviour of passing by reference or value (copy). And you have to manually *emulate* them, if you need one of them.(Un)fortunately there is a little problem for current D: The function can call a function of a struct, which could change the struct. This is not a big issue, there a several ways to tackle this problem,e.g.:-ignore it: compiler does not ensure constness, the programmer is paid to take care of this. ( would be perfectly acceptible for an alpha compiler,Ithink)Acceptable for an alpha compiler, yes. For the specification, no.-let the programmer flag function members of structs, whether they are const or not (similar to C++). -let the compiler figure it out itself -do not allow functions for structs at all (classes are better for functions anyway, but struct might be faster due to stack allocation) I do not address this issue here, because first I want Walter answer some questions regarding "const" at the end of this post. 3b.) structs - out/inout struct Point { int x,y; } D: void foo(inout Point var) translates to C: void foo(Point* var) or C++: void foo(Point& var) No explanation, you already got the point, for sure. 4a.)Objects - in Objects are reference types. The storage modifiers apply to the reference handle, since you can only pass reference handles arround (you cannot copy a Object, just itshandle),so you are always working on the handle, except when using the'.'operator.Note: Objects are always allocated in the garbage collected heap. class Logger {} D: void foo(in Logger o) translates to C++: void foo(Logger& const o)There is no such syntax in C++. You mean void foo(Logger& o)Rationale: Highly paid Java-programmers often write the following (but not me, I'm lowly paid ;-) Java (with non-OO extensions): class Result { int value; } /** * returns a Result object from the Database * return 0 if error; 7 if strange error; 1 on success */ int getFromDB(Result o) { o=new Result(); o.value=47; // BUG; callee's object is notchanged!return 1; } in D: int getFromDB(in Result o) { o=new Result(); // compiler error: 'o' cannot be an lvalue, o.value=47; return 1; } 4b.)Objects - out/inout class Logger {} D: void foo(out Logger o) translates to C++: void foo(Logger*& o) Rationale: -Allows to do sth. that Java cannot do ;-) Return multiple new objects from a function. -There is currently no concept for const for classes in D anyway. So there is no other way to do it. 5a.) dynamic/assoziative Arrays - in Dynamic Arrays are kind of reference types. (They are not true reference types) Note: The elements of dynamic/assoziative Arrays is always allocate in the garbage collected heap. A very small part, namely the length is allocate onto the stack. D: void foo(in list[]) translates to C: void foo(listData* const, const int listLength) Rationale: Dynamic Arrays are kind of reference type, so in/out/inout has the same arrays are Objects, but they are not (in D). With the proposed parameter passing semantic, passing arrays will have the same semantic as in Java orarrayor return an array with a changed length). So these programmers will feel at home. I understand that from a purly practical viewpoint, one might wish to make the entire list const. But having a clean language will prove to be more worthy. 5b.) dynamic/assoziative Arrays - out/inout D: void foo(out list[]) translates to C++: void foo(listData*, int listLength&) SUMMARY ------- value types: primitive types, pointers, fixed sized arrays and structs: ----------- 'in': Callee 'sends' a value to the function. The function can NEITHER return a changed value to the callee, NOR can it change the local copy of this value that might exist for some combinations of value type and compiler implementation. 'out': Function returns a value to the callee. The callee provides the memory into which the function stores the return value. 'inout': Callee 'sends' a value to the function. The function can read and change value. The function operates directly on the memory, which the callee uses to 'send' the value to the function. reference types: objects, dynamic/assoziative Arrays ("kind of" only) --------------- 'in': Callee 'sends' a reference handle to the function. The function can can change the referenced element(s), but not it cannot change the reference handle. (Consequently the function cannot return a new a reference handle object to the callee.) 'out': Function returns a reference handle to the callee. (Consequentlythefunction must return a new reference object to the callee.) 'inout': Callee 'sends' a reference handle to the function. The function can read and change the referenced element(s). Furthermore it can change the reference handle. (Consequently the function can return a newreferenceobject to the callee.) Learning rule ------------- The storage specifiers in, out and inout always refer to the lowest possible level of indirection, an assembler programmer can think of. Learning advice for non-programmers* ------------- Think up the parameters of functions, as it is taught to students in their university lessons (with in, out and maybe in out, of course). Never think about references and constness, as C++-programmers do ! Never think about all that wired stuff that is mentioned in this proposal!Now use the conceived attributes straight with you D-function, it should work. (Except for fixed sized arrays, because students are taught to not use them) languages, so these instruction will not work for them. -------------------------------------------------------------------------- Questions about "C++ const" to Walter: ------------------------------------- Though I did not find anything in the D spec (this night) regarding "C++ const" , I do remember that you (Walter) said, that the "const" concept of C++ does not really pay off - so you do not want to provide a similar concept for D. My problem is, that I did not get the point what the bad points of "C++ const" are EXACTLY. Here are my assumptions about bad points of the "C++ const" concept that you might have. 1. Const does not pay off, because to few C++ programmers actually use it. You can not use const objects with frameworks, because they have to many non-const functions.What kind of framework are you refering to?2. Const does not pay off, because when programming with large frameworks/codebase, too many functions are inherently non-const: A reference to a const object is almost useless, because at some point you simply must call one of these non-const functions. So, you end up using const casts all the time. (alternatively you can store two versions of every reference)You never ever need to store two version of the reference because of constness. You can always call const member functions through non-const references.3. It is too disheartening that const inherently is not usefull for good optimizations.At least it is not bad for optimization. And it is good for other things.3. It is too disheartening that const cannot be used for optimizations, because the C++ semantic of const/the common programming practice with const in C++, prevent any optimizations.Casting away const is bad, and - in any finely designed language - unnecessary. If that is what you refer to.4. Const clutteres the declarations of functions and reference types too much.Then, "in" clutters the declarations of functions too much.5. Const complicates the rules for function overloading, function overriding, class inheritence and interface inheritance, etc. too much.It depends on the specification. One can make up simpler rules than C++.6. It is too difficult to make a compiler that checks for constness.I doubt. Sandor
Oct 31 2002
"Sandor Hojtsy" <hojtsy index.hu> writes:This seems to be a matter of taste - in "Refactoring", Martin Fowler suggests that formal parameters should not be changed. The name of the refactoring is "Remove Assignments to Parameters". AnttiC-code: void foo(int parm) { if (parm < 0) parm=47; // provide some default value for further use } void foo(const int parm) { int maxlines=parm; if (parm < 0) maxlines=47; // provide some default value for further use }Comparing those two functions, I think the first one is cleaner. You don't need to give two names (variables) to the same concept, which you can later use intermixed. And you can avoid an extra line. I think this is a good example for allowing changing privitive types as "in" parameter.
Oct 31 2002
Subject: in,out,inout: new sematic proposal Newsgroups: Digital Mars News:D Followup-To: poster Thanks, for taking the trouble to fully read my last post. Thanks again, for catching my stupid C++-mistakeC++: void foo(Logger& const o)Unfortunately, my last post was quite misleading. Flagging any in-parameter as "const", so programmers cannot reuse the variablename, is only a minor topic of my proposal. It is not what I consider as the new semantic. You can actually transform the proposal to a version that does not require "Remove Assignments to Parameters". example: struct Point { int x, y } D: void foo(in Point calleeVar) { printPoint(calleeVar); calleeVar.x=5; // no error, since only a local version is changed } Compilers could still pass it by reference, by generating this C++ code: void fooCpp(const Point& calleeVar) { Point temporary=calleeVar; printPoint(calleeVar); temporary.x=5; } But this would not make things easier for compiler writers, since compilers must check for constness, anyway. That said, I still favor the "Remove Assignments to Parameters" semantic of my proposal. I would like it for D. But I do not like it semantic when I try to clarify some issues of my proposal.When I wrote "D func declaration" translates to "C func declaration." I do not think of translating valid D-code to valid C-code. "Tanslates to" means, when I were compiler writer, I would emit assembler code, the same way as it is done for that C-function declaration. By leaving the pointer non-const, I wanted to stress the fact, that a compiler can reuse the stack address (or register) of the pointer variable (so, it is not truly const). So there is no performance penalty for "Remove Assignments to Parameters". Both versions seem correct to me, my version is simply a bit weirder. The new semantic has two major changes compared to D as implemented by DMD 0.46. I.) The programmer cannot force the compiler to place a struct onto the stack for parameter passing. (Actually the compiler always decides how to shuffle parameters to and from functions, so 'out' does not mean by reference, on RISC-cpu's the compiler might use registers to do this) Rationale: The compiler can better decide how to shuffle values between functions around. (Programmers a prone to make wrong assumptions what is the fast way to do it) II.) 2a.)fixed sized arrays - in Fixed sized arrays are value types, so the in/out/inout semantic is the same as for primitive types. D: void foo(in short[5] var) translates to C: void foo(const short* var) D: void foo(in short[5] var) { var[2]=5; // error: 'var' cannot be an lvalue } FIXED sized arrays cannot be changed by the function body. Note: the semantic for dynamic arrays is entirely different, since they are a kind of reference type, not a value type). 2b.)fixed sized arrays - out/inout D: void foo(out short[5] var) translates to C: void foo(short* var) // in my last post, I wrote C: void foo(short* const var), but I prefer the above version (there is no semantic difference, anyway). example: D: void foo(out short[5] var) { var[2]=5; // ok; short[5] tmp; var[]=tmp[]; // ok, since D compiler makes a copy of 'tmp', right? } In the current DMD implementation it seems that 'in' means basically pass by value and inout/out simply means pass by reference. To get the proper meaning of the D function declaration, you have to first transform it to the corresponding C function declaration, because C-semantic of "in,out and inout", applys. III.)extern (C) functions Last time I forgot this issue. The in, out, and inout attributes can only be applied to D functions. Extern C, Pascal, Windows functions are actually C-Functions not D-Functions. So the syntax and semantic for parameters passing is C-style. The compiler assures that D-types can be passed properly to C-Functions. example D: extern (C) int foo(const char* s ) //'const' is allowed for C extern (C) int foo2(char* s, int* v) extern (C) int foo3(char* s, const int* v) void dfunc(in char[] string, in int value) { int i=foo(&string[0]); // ok i=foo2(&string[0], &value); // error: 'value' cannot be use as an lvalue i=foo3(&string[0], &value); // ok } ------------------------------- Not so important stuffD: void foo(in short[5] var) translates to C: void foo(const short* var)You mean void foo(const short* const var)Point move(Point a, int x, int y) { a.x += x; a.y += y; return a; } Is cleaner and *faster* than: Point move(const Point &a, int x, int y) { Point result = a; result.x += x; result.y += y; return result; }When I do C++, I normally use the later version, when the function is a public member of a class. Because, the fact that the functionbody needs to change a lokal version of this variable is unimportant for the callee. When I'm writing the a private method, I use the first version, to type. But both versions are ugly and not really fast (a structure is returned by value). Heres it is in D: Point move(Point a, int x, int y) { Point result = a; result.x += x; result.y += y; return result; } Same as in C++, does not look that bad at all. or 'better' (at least slower, I assume) class Point { private: int x; int y; public: void move(int x, int y) { this.x=x; this.y=y; } } Looks really good if you like OO-programming.Few programmers do like this. But specifications for programming languages should leave as much freedom to the compiler implementers as possible. Because different hardware-platforms have different needs. A good programmer can gain nothing from strict implementation rules. You either know how your compiler does implement certain features or you have no idea about your platform/compiler anyway. If you do not know your compiler, you cannot make the compiler produce better code. Explanation: Sandor, claimed (which might be true for his compiler/platform) that passing a struct by value (as shown in his example), is faster than passing it by const reference and making a local copy from it. I checked this with MS VC++ 6.0 SP5 (with processorpack) with the optimizing for speed settings. Since I'm not a cpu-cycle counter, I cannot say exactly which one is faster on an Athlon, Pentium IV or Pentium II or ... But I assume that both versions have practically the same speed. The functionbody of the passing by reference version is larger, but the parameter passing requires a less code. This results in a smaller overall code size. The attachment contains the dissembler code for the example. ---In D (with this proposal) in,out and inout, DOES NOT specify whether parameter passing is by reference or value. The compiler just picks the most efficient one that complys with semantic that belongs to the storage class attribute.I don't like this. Withouth specification, you can not rely on the different semantic behaviour of passing by reference or value (copy). And you have to manually *emulate* them, if you need one of them.I mean all that stuff that makes up a complex real world application: Project specific classes (the thing that is thought up by yourself and your team), common utility classes of your employer, runtime library of the programming language, and additional commercial/free libraries. You usually end up having a dozens of classes, hundreds of objects, all intermixed. Now the question is, how often can you use a const reference to other objects ? (without using const_cast).1. Const does not pay off, because to few C++ programmers actually use it. You can not use const objects with frameworks, because they have to many non-const functions.What kind of framework are you refering to?I assume that Walter may dislike "const" because functions in D are already cluttered with in (optional), out, inout. Have a nice D. begin 644 ByRefOrValue.txt M;7!A<FEN9R!B;W1H('9E<G-I;VYS+"!T:&4 <&%S<R!B>2!R969E<F5N8V4 M8V]D92!R97%U:7)E<R!O;F4 (G!U<V 1U!2(B!I;G-T<G5C=&EO;B!L97-S M;W8 (&5C>"QD=V]R9"!P='( 6V5S<%T-"B\O("` ("` ("`R+B!P=7-H(&5C M("!S=')U8W0 4&]I;G0 ;6]V95)E9BAC;VYS="!0;VEN="8 82P :6YT(' L M(&EN="!Y*0T*,3$Z("` >PT*,3(Z("` ("` (%!O:6YT(')E<W5L="`](&$[ M('!A<W-I;F< 8GD =F%L=64-"C$Y.B` (%!O:6YT(&UO=F5686QU92A0;VEN ` end4. Const clutteres the declarations of functions and reference types too much.Then, "in" clutters the declarations of functions too much.
Nov 01 2002
"Farmer" <itsFarmer. freenet.de> wrote in message news:Xns92B9BDA3ECC8AitsFarmer 63.105.9.61...Subject: in,out,inout: new sematic proposal Newsgroups: Digital Mars News:D Followup-To: poster Thanks, for taking the trouble to fully read my last post. Thanks again, for catching my stupid C++-mistakeConceptually, the "generated code" should not use the calleeVar parameter in later calls, because the local variable may change before the call. If the optimizing compiler detects that it will not change it can still insert the reference. void fooCpp(const Point& calleeVar) { Point temporary=calleeVar; printPoint(temporary); // <--- note this line temporary.x=5; } From the user and the conceptual point of view, both of your examples use pass by value, and I agree that the compiler should be free to choose from these two, and several other implementations of pass by value. I don't think you have invented any new semantics. These are just different compiler implementations for the same semantic rule. The C++ specification includes a statement similar to (excuse me, I don't have the specs with me now): "The compiler implementation is free to be different from this specs, if it can be proven that no user will ever notice the difference." I think the same applies to D. A problem may arise if you try to call a function from a different compilation module. What kind of function call should the compiler insert into the object file? How can you guarantee that all modules calling the function will use the same call method? This kind of optimization seems to be possible only at link time, but reqires compilation. This would intermix the clear sequential process of compilation which Walter is fond of.C++: void foo(Logger& const o)Unfortunately, my last post was quite misleading. Flagging any in-parameter as "const", so programmers cannot reuse the variablename, is only a minor topic of my proposal. It is not what I consider as the new semantic. You can actually transform the proposal to a version that does not require "Remove Assignments to Parameters". example: struct Point { int x, y } D: void foo(in Point calleeVar) { printPoint(calleeVar); calleeVar.x=5; // no error, since only a local version is changed } Compilers could still pass it by reference, by generating this C++ code: void fooCpp(const Point& calleeVar) { Point temporary=calleeVar; printPoint(calleeVar); temporary.x=5; }But this would not make things easier for compiler writers, since compilers must check for constness, anyway. That said, I still favor the "Remove Assignments to Parameters" semantic of my proposal. I would like it for D. But I do not like it semantic when I try to clarify some issues of my proposal.I see.When I wrote "D func declaration" translates to "C func declaration." I do not think of translating valid D-code to valid C-code. "Tanslates to" means, when I were compiler writer, I would emit assembler code, the same way as it is done for that C-function declaration.D: void foo(in short[5] var) translates to C: void foo(const short* var)You mean void foo(const short* const var)By leaving the pointer non-const, I wanted to stress the fact, that a compiler can reuse the stack address (or register) of the pointer variable (so, it is not truly const). So there is no performance penalty for "Remove Assignments to Parameters".I don't see how can the compiler "reuse the stack address of the pointer variable". Reuse for what? And after reusing how would you refer to the originaly pointed value? How is this connected to the performance penality for "Remove Assignments to Parameters"?The new semantic has two major changes compared to D as implemented by DMD 0.46. I.) The programmer cannot force the compiler to place a struct onto the stack for parameter passing. (Actually the compiler always decides how to shuffle parameters to and from functions, so 'out' does not mean by reference, on RISC-cpu's the compiler might use registers to do this) Rationale: The compiler can better decide how to shuffle values between functions around. (Programmers a prone to make wrong assumptions what is the fast way to do it)Well, yes. But if user code is not affected, I don't see why you need to put his statement to the specification at all.II.) 2a.)fixed sized arrays - in Fixed sized arrays are value types, so the in/out/inout semantic is the same as for primitive types. D: void foo(in short[5] var) translates to C: void foo(const short* var) D: void foo(in short[5] var) { var[2]=5; // error: 'var' cannot be an lvalue } FIXED sized arrays cannot be changed by the function body. Note: the semantic for dynamic arrays is entirely different, since they are a kind of reference type, not a value type).If you still want immutable "in" parameters for value types, then why are we arguing about how to specify/implement fast mutable "in" parameters?2b.)fixed sized arrays - out/inout D: void foo(out short[5] var) translates to C: void foo(short* var) // in my last post, I wrote C: void foo(short* const var), but I prefer the above version (there is no semantic difference, anyway). example: D: void foo(out short[5] var) { var[2]=5; // ok; short[5] tmp; var[]=tmp[]; // ok, since D compiler makes a copy of 'tmp', right? } In the current DMD implementation it seems that 'in' means basically pass by value and inout/out simply means pass by reference. To get the proper meaning of the D function declaration, you have to first transform it to the corresponding C function declaration, because C-semantic of "in,outandinout", applys. III.)extern (C) functions Last time I forgot this issue. The in, out, and inout attributes can only be applied to D functions. Extern C, Pascal, Windows functions are actually C-Functions not D-Functions. So the syntax and semantic for parameters passing is C-style. The compiler assures that D-types can be passed properly to C-Functions. example D: extern (C) int foo(const char* s ) file://'const' is allowed for C extern (C) int foo2(char* s, int* v) extern (C) int foo3(char* s, const int* v) void dfunc(in char[] string, in int value) { int i=foo(&string[0]); // ok i=foo2(&string[0], &value); // error: 'value' cannot be use as an lvalue i=foo3(&string[0], &value); // ok } ------------------------------- Not so important stuffI don't find the first version ugly. How else would you return a structure? Return a reference to a local structure variable?Point move(Point a, int x, int y) { a.x += x; a.y += y; return a; } Is cleaner and *faster* than: Point move(const Point &a, int x, int y) { Point result = a; result.x += x; result.y += y; return result; }When I do C++, I normally use the later version, when the function is a public member of a class. Because, the fact that the functionbody needs to change a lokal version of this variable is unimportant for the callee. When I'm writing the a private method, I use the first version, to type. But both versions are ugly and not really fast (a structure is returned by value).Heres it is in D: Point move(Point a, int x, int y) { Point result = a; result.x += x; result.y += y; return result; } Same as in C++, does not look that bad at all.Not that bad because of what?or 'better' (at least slower, I assume) class Point { private: int x; int y; public: void move(int x, int y) { this.x=x; this.y=y; } } Looks really good if you like OO-programming.Yes it looks good, but does a different thing. To do the same as the examples above, it should rather be: Point move(int xd, int yd) { Point p = new Point(this); p.x += xd; p.y += yd; return p; } Now does it look good?Thanks for going into details. I checked the dissasembly, and it proves that they are practically the same speed and I was wrong with the speed comparsion.Few programmers do like this. But specifications for programming languages should leave as much freedom to the compiler implementers as possible. Because different hardware-platforms have different needs. A good programmer can gain nothing from strict implementation rules. You either know how your compiler does implement certain features or you have no idea about your platform/compiler anyway. If you do not know your compiler, you cannot make the compiler produce better code. Explanation: Sandor, claimed (which might be true for his compiler/platform) that passing a struct by value (as shown in his example), is faster than passing it by const reference and making a local copy from it. I checked this with MS VC++ 6.0 SP5 (with processorpack) with the optimizing for speed settings. Since I'm not a cpu-cycle counter, I cannot say exactly which one is faster on an Athlon, Pentium IV or Pentium II or ... But I assume that both versions have practically the same speed.In D (with this proposal) in,out and inout, DOES NOT specify whether parameter passing is by reference or value. The compiler just picks the most efficient one that complys with semantic that belongs to the storage class attribute.I don't like this. Withouth specification, you can not rely on the different semantic behaviour of passing by reference or value (copy). And you have to manually *emulate* them, if you need one of them.The functionbody of the passing by reference version is larger, but the parameter passing requires a less code. This results in a smaller overall code size.Which may be a different issue from speed.yourI mean all that stuff that makes up a complex real world application: Project specific classes (the thing that is thought up by yourself and1. Const does not pay off, because to few C++ programmers actually use it. You can not use const objects with frameworks, because they have to many non-const functions.What kind of framework are you refering to?team), common utility classes of your employer, runtime library of the programming language, and additionalcommercial/freelibraries. You usually end up having a dozens of classes, hundreds of objects, all intermixed. Now the question is, how often can you use a const reference to other objects ? (without using const_cast).In C++ const can only be useful, if the framework creator, (or library programmer), designed his system with the "const" concept in mind. For example standard Windows API functions don't specify const for the parameters they don't wan't to change. This makes using const with Windows API almost impossible. For example, the first parameter of the CreateFile function has the type of "char *", and not "const char *". Therefore CreateFile does not sign a contract that it will not change the first parameter. CreateFile can change it if it likes to. So you should not pass a string literal as a filename! Now that is *inconvinient*, at least. But if you are creating the framework, const can be used as a contract. The compiler could and should check that the function don't violate the contract by changing the refered value. This can help bug-shooting at compile time. About half of library functions can and therefore should sign the contract to leave some parameters unmodified. The compiler can help you find the places when the functions violate the contract, most of which will surely be bugs.This decalaration is ugly: void foo(const inout a) { ... } But it is ugly not because the parameter list is long. The problem seems to be that how can an "inout" parameter be const. The solution is different terminonlogy. The "inout" keyword was used here to force passing by reference, but you would not like to use this reference to change the original value. References are used not only to change the original value. They are also usefull if you would always like to see the current value of the reffered variable, not some old value which it had at the time of copying. References has three semantical features: 1) you can change the original value in real-time, and 2) you can see the actual value if it was changed by somebody else 3) constructor is not called upon creation I don't think the word "inout" expresses these concepts. It expresses feature 1) partially, and the other two: not really! So I propose again to use the "ref" instead of "inout" In D you can not have one of these features withouth the other two. In C++ you can have 2) and 3) withouth having 1), by using const reference. I would be happy with: void foo(const ref a) { ... } Yours, SandorI assume that Walter may dislike "const" because functions in D are already cluttered with in (optional), out, inout.4. Const clutteres the declarations of functions and reference types too much.Then, "in" clutters the declarations of functions too much.
Nov 05 2002
Hi, well, I feel that I can't really provide definite answers, because Walter has not yet answered the big question: What is D meant to be ? Is D practically C++ with garbage collector (though reference counting might be supported, too), DBC and better arrays ? Is D, what C++ is to C ? So the final result will be "C/C++/D" ? Or is D the successor to C++, as defined by 'a general purpose language biased to system programming' ? Or is D the successor to C++, as defined by 'a general purpose language, as defined by contemporary perception' ? If D is not supposed to become "C/C++/D", where should it fit according to this oversimplyfied schema ?: complexity easy to use completeness easy to learn performance easy to maintain realtime simplicity open standard rapid development object oriented controlled by big company bugfreeness ? fun ? <------------------------------------------------> Does anybody know where to put in Eiffel ? ;-) ---- Sandor, I don't reply to all your comments, because I think that I basically agree with them, cannot add important facts to them and I don't see that they could object to the new in/out/inout semantics. I might have missed an important issue: Please ask if you think an issue needs discussion.This decalaration is ugly: void foo(const inout a) { ... } But it is ugly not because the parameter list is long. The problem seems to be that how can an "inout" parameter be const. The solution is different terminonlogy. The "inout" keyword was used here to force passing by reference, but you would not like to use this reference to change the original value. References are used not only to change the original value. They are also usefull if you would always like to see the current value of the reffered variable, not some old value which it had at the time of copying. References has three semantical features: 1) you can change the original value in real-time, and 2) you can see the actual value if it was changed by somebody else 3) constructor is not called upon creation I don't think the word "inout" expresses these concepts. It expresses feature 1) partially, and the other two: not really! So I propose again to use the "ref" instead of "inout""inout" currently is a plain lie to me, "ref" would describe better whats...References has three semantical features: 1) you can change the original value in real-time, and 2) you can see the actual value if it was changed by somebody else 3) constructor is not called upon creationI was not talking about references in general; my proposal is for an easier way to interface with functions. Are you think of sth. like this ? void main() { structType v; ref structType r=v; // no copy is made here } I don't think that is needed, because you can use pointers for that.In D you can not have one of these features withouth the other two. In C++ you can have 2) and 3) withouth having 1), by using const reference. I would be happy with: void foo(const ref a) { ... }I would be happy, if you had to write void(in const a*) If you need some low level semantic, then pointers would fit. Unfortunately there is no const for pointers in D (yet!). But that could be solved easily.The solution is different terminonlogy.I would just use the preprocessor of my brain: #define ref inout Now, it's easy to understand "C/C++/D". Walter, doesn't have to change two lines of the spec and one in his compiler ;-) I consider the following odd: C++: void foo(structType v) // there is no real need for this one same as D: void foo(in structType v) C++ void foo(const structType& v) simliar D: void foo(inout structType v) or maybe D: void foo(ref structType v) Why support a superfluous feature in a new language ? another issue is void foo(in fixedArrayType v); // by reference, no copy is made but functionbody() { char[5] a1,a2; a1[]=a2[]; // assignment results in a copy } Looks like D immitates C-style arrays.The C++ specification includes a statement similar to (excuse me, I don't have the specs with me now): "The compiler implementation is free to be different from this specs, if it can be proven that no user will ever notice the difference."Interesting, now I understand why C++-compilers don't optimise all my C++ smart-pointers, because if they did, I would *notice* that my programs would be faster and would have a smaller code size.A problem may arise if you try to call a function from a different compilation module. What kind of function call should the compiler insert into the object file? How can you guarantee that all modules calling the function will use the same call method? This kind of optimization seems to be possible only at link time, but reqires compilation. This would intermix the clear sequential process of compilation which Walter is fond of.Always use the byreference calling convention for structs, as it is always reasonably fast. A very good optimizer does intermodule optimizing (see Intel C/C++ compiler), anyway. Therefore it could choose the best calling convention.If you still want immutable "in" parameters for value types, then why are we arguing about how to specify/implement fast mutable "in" parameters?Just want to prove, that the calling convention C: foo(structType v) is superfluous even if you consider performance.I don't find the first version ugly. How else would you return a structure? Return a reference to a local structure variable?C++: I would not return a struct at all. You don't have to do this because it is (at least usually) more efficient to pass in a (non-const) struct to a function. No API I have ever seen returns structs. (the C++ standard lib isn't an API) Sometimes you do returns structs/classes in C++ because a) it's more convenient b) required for some classes: typically smart-pointers, iteraters, etc. But since you have a GC in D. It is easier to return a reference to a class object.Because, it is very easy to see what's happening for the maintenance- programmer. It also prevents a possible bug.Heres it is in D: Point move(Point a, int x, int y) { Point result = a; result.x += x; result.y += y; return result; } Same as in C++, does not look that bad at all.Not that bad because of what?Right. It's more OO-style, it does a different thing. But I suppose you can use it to accomplish the same task.or 'better' (at least slower, I assume) class Point { private: int x; int y; public: void move(int x, int y) { this.x=x; this.y=y; } } Looks really good if you like OO-programming.Yes it looks good, but does a different thing. To do the same as the examples above, it should rather be:Point move(int xd, int yd) { Point p = new Point(this); p.x += xd; p.y += yd; return p; } Now does it look good?The function body constantly produces garbage, the user of the function cannot prevent this. If it's done in the OO-way, users may be able to reuse already created object-instances of the Point-class.In C++ const can only be useful, if the framework creator, (or library programmer), designed his system with the "const" concept in mind. For example standard Windows API functions don't specify const for the parameters they don't wan't to change. This makes using const with Windows API almost impossible. For example, the first parameter of the CreateFile function has the type of "char *", and not "const char *". Therefore CreateFile does not sign a contract that it will not change the first parameter. CreateFile can change it if it likes to. So you should not pass a string literal as a filename! Now that is *inconvinient*, at least.No. Here is the declaration from winbase.h: typedef const CHAR* LPCSTR WINBASEAPI HANDLE WINAPI CreateFileA( LPCSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode, LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile );But if you are creating the framework, const can be used as a contract. The compiler could and should check that the function don't violate the contract by changing the refered value. This can help bug-shooting at compile time. About half of library functions can and therefore should sign the contract to leave some parameters unmodified. The compiler can help you find the places when the functions violate the contract, most of which will surely be bugs.I also think, that for reference types a const modifier could be useful. My proposal does not interfere with const in anyway. For value types const is not really needed (they could be allowed for language completeness): C++: void foo() { int n1; const int n2; n1=n2; // can assign non-const type to const type structType s1; const structType s2; s1=s2; // can assign non-const type to const type // C++ mixed with D reference type // -> won't compile with an ansi C++-compiler classType c1; const classType c2; c2=c1; // cannot assign non-const type to const type // -> const types is really different from non-const } But currently D would require: foo(inout const structType) or foo(ref const structType) StructType is a value type, in my perception const has no meaning to value types. Thus current parameter passing semantics look strange for me.From the user and the conceptual point of view, both of your examples use pass by value, and I agree that the compiler should be free to choose from these two, and several other implementations of pass by value. I don't think you have invented any new semantics. These are just different compiler implementations for the same semantic rule.Passing of structs and fixed sized arrays have another behaviour than that mentioned in the D-spec (Ok. I don't see the behaviour really mentioned in the spec). It's a new concept, which is different from the C++-way. Though, the compiler-implementation will be very similar. Have a nice D. 'Farmer'
Nov 05 2002
Warning. Quite a long letter to go. "Farmer" <itsFarmer. freenet.de> wrote in message news:Xns92BDEFCAD76FEitsFarmer 63.105.9.61...Hi, well, I feel that I can't really provide definite answers, because Walter has not yet answered the big question: What is D meant to be ?As we was already told by Walter: "What C++ should have been", and "Successor to C"If D is not supposed to become "C/C++/D", where should it fit according to this oversimplyfied schema ?: complexity easy to use completeness easy to learn performance easy to maintain realtime simplicity open standard rapid development object oriented controlled by big company bugfreeness ? fun ? <------------------------------------------------>Afaik, it aims to be the best of both worlds. With todays computing power and experience, compilers could be able to compile a more intuitive language effectively. It seems that you can almost get the fast developement time of Java, together with the fast execution time of C/ASM.easier...References has three semantical features: 1) you can change the original value in real-time, and 2) you can see the actual value if it was changed by somebody else 3) constructor is not called upon creationI was not talking about references in general; my proposal is for anway to interface with functions. Are you think of sth. like this ? void main() { structType v; ref structType r=v; // no copy is made here } I don't think that is needed, because you can use pointers for that.That kind of usage of references is connected with parameter passing. It wouldn't be clever to create a reference to a local variable in the same function. But: class Updater { const ref int source; ref int target; int mul; Updater(const ref int source_i, ref int target_i, mul_i) { source = source_i; // line 8 target = target_i; mul = mul_i; } doUpdate() { target = source * mul; } } Apart from const, you can do something similar with pointers.UnfortunatelyIn D you can not have one of these features withouth the other two. In C++ you can have 2) and 3) withouth having 1), by using const reference. I would be happy with: void foo(const ref a) { ... }I would be happy, if you had to write void(in const a*) If you need some low level semantic, then pointers would fit.there is no const for pointers in D (yet!). But that could be solved easily.Yes. But there is a widespread myth: "Pointers are evil, and with D you only need them to interface to C". If I remember right, pointers may interfere with garbage collection.Any serious programmer is capable of such "brain preprocessing", and it is inevitable to rely on it. But I think we should minimize the need for it, by using the most intuitive keywords, and structure.The solution is different terminonlogy.I would just use the preprocessor of my brain: #define ref inout Now, it's easy to understand "C/C++/D". Walter, doesn't have to change two lines of the spec and one in his compiler ;-)I consider the following odd: C++: void foo(structType v) // there is no real need for this one same as D: void foo(in structType v) C++ void foo(const structType& v) simliar D: void foo(inout structType v) or maybe D: void foo(ref structType v) Why support a superfluous feature in a new language ?In C++, it was not superfluous. In the firts case the constructor is run, so the result may be different. In D, no consturtor is run, but taking into account the "raii" feature of D, the result may also be different.:)The C++ specification includes a statement similar to (excuse me, I don't have the specs with me now): "The compiler implementation is free to be different from this specs, if it can be proven that no user will ever notice the difference."Interesting, now I understand why C++-compilers don't optimise all my C++ smart-pointers, because if they did, I would *notice* that my programs would be faster and would have a smaller code size.I see.A problem may arise if you try to call a function from a different compilation module. What kind of function call should the compiler insert into the object file? How can you guarantee that all modules calling the function will use the same call method? This kind of optimization seems to be possible only at link time, but reqires compilation. This would intermix the clear sequential process of compilation which Walter is fond of.Always use the byreference calling convention for structs, as it is always reasonably fast. A very good optimizer does intermodule optimizing (see Intel C/C++ compiler), anyway. Therefore it could choose the best calling convention.You proved that there are no actual performace difference, when using small structures. But there are other differences. Such as when using references, changes to the refered data is immediately visible to you, whereas when using value copying, the copy does not change if the original value is changed. In some cases the first is desirable in some cases the second.If you still want immutable "in" parameters for value types, then why are we arguing about how to specify/implement fast mutable "in" parameters?Just want to prove, that the calling convention C: foo(structType v) is superfluous even if you consider performance.isI don't find the first version ugly. How else would you return a structure? Return a reference to a local structure variable?C++: I would not return a struct at all. You don't have to do this because it(at least usually) more efficient to pass in a (non-const) struct to a function. No API I have ever seen returns structs. (the C++ standard lib isn't an API)Yeah, an API has to be effecient. But in the user code, at certain places, it may be simpler to get back a value from a function, rather that passing a reference to an object you created beforehand. Just like with an int or double type. However, if the compiler likes to, it can restructure the code to avoid the actual copy.Sorry, I was wrong with CreateFile. Nevertheless const is only a problem if it is not used. Nobody says: "lets change CreateFile declaration so that the type of first parameter is non-const"!In C++ const can only be useful, if the framework creator, (or library programmer), designed his system with the "const" concept in mind. For example standard Windows API functions don't specify const for the parameters they don't wan't to change. This makes using const with Windows API almost impossible. For example, the first parameter of the CreateFile function has the type of "char *", and not "const char *". Therefore CreateFile does not sign a contract that it will not change the first parameter. CreateFile can change it if it likes to. So you should not pass a string literal as a filename! Now that is *inconvinient*, at least.No. Here is the declaration from winbase.h: typedef const CHAR* LPCSTR WINBASEAPI HANDLE WINAPI CreateFileA( LPCSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode, LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile );MyBut if you are creating the framework, const can be used as a contract. The compiler could and should check that the function don't violate the contract by changing the refered value. This can help bug-shooting at compile time. About half of library functions can and therefore should sign the contract to leave some parameters unmodified. The compiler can help you find the places when the functions violate the contract, most of which will surely be bugs.I also think, that for reference types a const modifier could be useful.proposal does not interfere with const in anyway. For value types const is not really needed (they could be allowed for language completeness): C++: void foo() { int n1; const int n2; n1=n2; // can assign non-const type to const type structType s1; const structType s2; s1=s2; // can assign non-const type to const typeafaik, there is no casting here// C++ mixed with D reference type // -> won't compile with an ansi C++-compiler classType c1; const classType c2; c2=c1; // cannot assign non-const type to const type // -> const types is really different from non-const } But currently D would require: foo(inout const structType) or foo(ref const structType) StructType is a value type, in my perception const has no meaning to value types. Thus current parameter passing semantics look strange for me.
Nov 06 2002
"Sandor Hojtsy" <hojtsy index.hu> wrote in news:aqb74i$1p86$1 digitaldaemon.com:Warning. Quite a long letter to go.??? It actually is the shortest message in this thread, except for Antti's post.As we was already told by Walter: "What C++ should have been", and "Successor to C"I see. In my perception it is already the successor to "C/C++" (in a technical sense). My guess: It will inevitably become known as "C/C++/D".The phrase "what you don't need/use, you won't have to pay for" is true for performance considerations. But when it comes to simplicity, you can only compromise. If you want/need everything C provides, you'll basically get everything C has, including its drawbacks. Of course, everything made up by man's thoughts can be improved a bit.If D is not supposed to become "C/C++/D", where should it fit according to this oversimplyfied schema ?: complexity easy to use completeness easy to learn performance easy to maintain realtime simplicity open standard rapid development object oriented controlled by big company bugfreeness ? fun ? <------------------------------------------------>Afaik, it aims to be the best of both worlds. With todays computing power and experience, compilers could be able to compile a more intuitive language effectively. It seems that you can almost get the fast developement time of Java, together with the fast execution time of C/ASM.That kind of usage of references is connected with parameter passing. It wouldn't be clever to create a reference to a local variable in the same function. But:I wanted to separate parameter passing from reference:. I'm in a quest for a language suitable for true general application programming. Java was not implement it won't become very portable. Not to mention that for political reasons, few companys will try to port it, anyway. I need clean, easy maintainable code for maxium business profit (of my bosses). But never mind, I can gain as much money by using unsuitable languages as I can gain with great languages. And the same rule applys to my bosses. The code below is a real maintanance nightmare for me. I had never to write anything like that. Obviously, you are doing same very different kind of programming-tasks than I do. Consequently, I cannot provide anything helpful to the D language. My thoughts are different because my needs are different. How can you be sure that, the passed reference is always valid ? It might be allocated on the stack. If I have to do such quick and dirty stuff, I would use pointers, so everybody sees what I'm doing (doing some dirty stuff, which requires some specific unusual semantics).Yes. But there is a widespread myth: "Pointers are evil, and with D you only need them to interface to C".They are not evil. They are powerful, rather bug-prone, and hard to understand. Sometimes they hinder good optimizations by the compiler. Still, I would only need/use them to interface with C-api.If I remember right, pointers may interfere with garbage collection.My guess is that they don't interfere, as long as you doing things the way as C++-reference types work.Any serious programmer is capable of such "brain preprocessing", and it is inevitable to rely on it. But I think we should minimize the need for it, by using the most intuitive keywords, and structure.I fully agree. Keep looking out for some more misleading keywords in current/future D versions.[Since I know by now that D will become "C/C++/D", it no longer strikes my that everybody in the D-newsgroup (including myself) talks more about C++ than about D. It's likely to become D's doom.] I don't see how raii interfered with my proposal, since raii applys only for classes. The semantic for classes was not affected by my proposal.I consider the following odd: C++: void foo(structType v) // there is no real need for this one same as D: void foo(in structType v) C++ void foo(const structType& v) simliar D: void foo(inout structType v) or maybe D: void foo(ref structType v) Why support a superfluous feature in a new language ?In C++, it was not superfluous. In the firts case the constructor is run, so the result may be different. In D, no consturtor is run, but taking into account the "raii" feature of D, the result may also be different.[I already mentioned it in this post] I don't have the need for such low- level semantics in general, whenever I have I use pointers. But honestly, I simply have the wrong background for such thoughts.You proved that there are no actual performace difference, when using small structures. But there are other differences. Such as when using references, changes to the refered data is immediately visible to you, whereas when using value copying, the copy does not change if the original value is changed. In some cases the first is desirable in some cases the second.If you still want immutable "in" parameters for value types, then why are we arguing about how to specify/implement fast mutable "in" parameters?Just want to prove, that the calling convention C: foo(structType v) is superfluous even if you consider performance.Yeah, an API has to be effecient. But in the user code, at certain places, it may be simpler to get back a value from a function, rather that passing a reference to an object you created beforehand. Just like with an int or double type. However, if the compiler likes to, it can restructure the code to avoid the actual copy.Right. Though, I swear that I am NEVER going to return a struct-type in D ( if I ever got an employer that would let me write in D). Though, I did not ban it in my proposal for language completeness considerations.For value types const is not really needed (they could be allowed for language completeness): C++: void foo() { int n1; const int n2; n1=n2; // can assign non-const type to const type structType s1; const structType s2; s1=s2; // can assign non-const type to const type// C++ mixed with D reference type // -> won't compile with an ansi C++-compiler classType c1; const classType c2; c2=c1; // cannot assign non-const type to const type // -> const types is really different from non-const } But currently D would require: foo(inout const structType) or foo(ref const structType) StructType is a value type, in my perception const has no meaning to value types. Thus current parameter passing semantics look strange for me.afaik, there is no casting hereYou are right. My example is wrong. Every variable must be toggled to become a sensible example. Then it reads "cannot assign const *reference* type to non-const *reference* type". [Note that this restriction does not apply to value types.] Have a nice "C/C++/D". 'Farmer'
Nov 06 2002
"Farmer" <itsFarmer. freenet.de> wrote in message news:Xns92B816E513F51itsFarmer 63.105.9.61...Questions about "C++ const" to Walter: ------------------------------------- Though I did not find anything in the D spec (this night) regarding "C++ const" , I do remember that you (Walter) said, that the "const" concept of C++ does not really pay off - so you do not want to provide a similar concept for D. My problem is, that I did not get the point what the bad points of "C++ const" are EXACTLY.1) It does not aid optimization. For example, if you have the following: int *p; const int *q = p; a = *q; *p = b; c = *q; Oh well, *q has changed, so the compiler can make no use of the programmer having called it const. While this example is trivial, I've run into enough real world cases (by turning that optimization on) that subtly break due to dependencies like this that a real world compiler cannot do those optimizations. It does no good to say such programs are broken - it can be a daunting task to figure out why a complex program broke with such optimizations turned on, and nobody will make the effort when the program "works" with Brand X compiler. 2) Many people strongly disagree with me on this point: in many years and lots of code using 'const', it simply never has exposed a bug in my experience. I just stopped using it and haven't missed it. 3) You can legally cast away const-ness. Hence, as a contract, it is unreliable. 4) The 'mutable' keyword is another hole in it. 5) Const as a type modifier permeates the type system, and has effects spilling all over places it has no business doing so. For example, it affects the partial specialization rules of templates, not in any clarifying way, but just adding more layers of complexity and special case rules. 6) Const-correct code seems to be simply loaded with const const here a const there a const const that I find distracting and annoying. (Yes, that's a personal opinion!) I'm not saying const has zero benefit - it does have some benefit. But its costs exceed its benefits.Here are my assumptions about bad points of the "C++ const" concept that you might have. 1. Const does not pay off, because to few C++ programmers actually use it. You can not use const objects with frameworks, because they have to many non-const functions. 2. Const does not pay off, because when programming with large frameworks/codebase, too many functions are inherently non-const: A reference to a const object is almost useless, because at some point you simply must call one of these non-const functions. So, you end up using const casts all the time. (alternatively you can store two versions of every reference) 3. It is too disheartening that const inherently is not usefull for good optimizations. 3. It is too disheartening that const cannot be used for optimizations, because the C++ semantic of const/the common programming practice with const in C++, prevent any optimizations. 4. Const clutteres the declarations of functions and reference types too much. 5. Const complicates the rules for function overloading, function overriding, class inheritence and interface inheritance, etc. too much. 6. It is too difficult to make a compiler that checks for constness.
Nov 30 2002
A systems programming language (SPL) requires two fundamental features of its type system in order to meet any minimal definition of being an SPL: 'const' and 'volatile' (or equivalent). Even Ada has them, in very constrained but still VERY useful forms. "Const" (and 'volatile', while we're at it) does not have to have universal applicability in order to be valuable and useful. "Sloppy" use of const should not be encouraged, and shouldn't be tolerated when detected. In my C code, I find using "gcc -Wall" helps quite a bit. Walter wrote:... 1) It does not aid optimization. For example, if you have the following: int *p; const int *q = p; a = *q; *p = b; c = *q; Oh well, *q has changed, so the compiler can make no use of the programmer having called it const. While this example is trivial, I've run into enough real world cases (by turning that optimization on) that subtly break due to dependencies like this that a real world compiler cannot do those optimizations. It does no good to say such programs are broken - it can be a daunting task to figure out why a complex program broke with such optimizations turned on, and nobody will make the effort when the program "works" with Brand X compiler.This is caught by "gcc -Wall" and flagged with a warning. I'd make it an error in D. Forbid it!2) Many people strongly disagree with me on this point: in many years and lots of code using 'const', it simply never has exposed a bug in my experience. I just stopped using it and haven't missed it.I use const LOTS simply to get rid of #defines: const char LF = '\n'; // Enum works too... Or for strings I use often: const char * const errMsgHdr = "ERROR: "; // Double const is needed! More realistically, in embedded code: // A pointer to a volatile 8-bit hardware register: const unsigned char * volatile pHardware = (unsigned char *)0xFEEDFACE; Without const and volatile both, the above hardware register would likely become 'static', with the programmer (NOT the compiler) being tasked with "remembering" not to step on them! This is something compilers should do for us. Of course, a C or ASM library could be written to take care of the need for volatile in D. But then again, I can do that for ANY non-SPL. That's how OSes written in Smalltalk and Lisp get the low-level hardware stuff done. Why? Because Smalltalk and Lisp are NOT Systems Programming Languages! But not even an external library can provide const-like properties. I'd probably need to use assembler to force a variable to be allocated in the segment containing the object code, and pray that D will still be able to find and use it. That may work for a device driver, but it requires a non-D OS to provide the segment protection needed via MMU programming. Too many work-arounds make a language non-SPL.3) You can legally cast away const-ness. Hence, as a contract, it is unreliable.Let's change that in D! Make it as difficult as possible to cast away constness. You can always emit a Big Fat Greek Warning whenever const is used: "WARNING: Const Considered Harmful!"4) The 'mutable' keyword is another hole in it.I don't use 'mutable'... But can't this be handled with another noisy warning?5) Const as a type modifier permeates the type system, and has effects spilling all over places it has no business doing so. For example, it affects the partial specialization rules of templates, not in any clarifying way, but just adding more layers of complexity and special case rules.Disallow its use in such circumstances! Yes, this will require some up-front checking for such use, but no back-end support at all. Permit const to apply only to fundamental types, and pointers to such types (that's all I really need). No const structs or anything else. I doubt there is any great need for structs to have const members. I'm willing to violate OO doctrine and software engineering practices WHEN JUSTIFIED to access whatever minimalist const or volatile syntax is implemented in D. Perhaps I'll need to make all my consts and volatiles global, and put them in their own file. Ugly, but at least I'll have the feature present in some form to use when I need it. If the type system is so fragile and difficult, then move const and volatile as far to the edge of it as possible. I'll gladly accept that. But don't just leave them out!6) Const-correct code seems to be simply loaded with const const here a const there a const const that I find distracting and annoying. (Yes, that's a personal opinion!)Yes! It *should* be ugly! That will discourage its use. But when it is needed, it should NOT always be impossible. Merely difficult (and ugly) will suffice. D has already made this trade-off at least once, and it is a good one to make, especially for "risky" features.I'm not saying const has zero benefit - it does have some benefit. But its costs exceed its benefits.Contain const's costs by constraining const's contexts. Don't kill the entire concept! Both const and volatile are VITAL to any Systems Programming language that is intended to be used to do any systems programming (as opposed to applications programming). <exapseration=ON> Has anyone tried to write a device driver in D? How about a small OS (or a port of uCOS) that boots itself? Those are the kids of things Systems Programming Languages are created for. I don't think they can be done in D without lots of work-arounds in assembler that C doesn't need: D doesn't play nice with hardware, especially when the processor has a cache. Ergo, (IMHO) D is not (yet) a systems programming language. These fundamental type system features (const and volatile) must be part of D (in some form), or else D's moniker of being a "Systems Programming Language" is false by definition. It should be replaced with something more appropriate, such as "Yet Another Non-Systems Programming Language, But One That's Easier For C Programmers To Use." Walter, all your points AGAINST const are valid! Just because you know of many misuses for and difficulties with 'const' doesn't mean there still aren't many VALID needs and uses for const and volatile. Just because you haven't encountered them and can't envision them does not mean they don't exist. It only means I suck at explaining them. I wish I could craft a better argument, or provide the needed insight and education. Despite several tries, I have failed miserably to convince you. As I've said repeatedly, I'm no language guru. But I know what I need my tools to do, and D doesn't do what's needed for system programming. I already use about a dozen languages (some far better than others), and I have all the non-system programming languages I need. I don't need another one, even if it is as good as D. I'm running out of reasons to keep arguing for adding 'const' and 'volatile' to D. The door appears to be firmly shut, and there is no point in banging my head against it or shouting at it any longer. D is **NOT** a Systems Programming Language. Please call it something else so others like myself (who program embedded systems, real-time systems, device drivers and operating systems for a living) won't be encouraged to waste their time and breath on it. Or you can simply TRUST ME that some minimal form of 'const' and 'volatile' are mandatory for systems programming, and add them to the D language specification. </exasperation> -BobC
Dec 02 2002
Robert W. Cunningham wrote:A systems programming language (SPL) requires two fundamental features of its type system in order to meet any minimal definition of being an SPL: 'const' and 'volatile' (or equivalent). Even Ada has them, in very constrained but still VERY useful forms. "Const" (and 'volatile', while we're at it) does not have to have universal applicability in order to be valuable and useful. "Sloppy" use of const should not be encouraged, and shouldn't be tolerated when detected. In my C code, I find using "gcc -Wall" helps quite a bit.First off, I'm in the C++ const is bad camp. It has too many variant meanings, and as Walter has said, it doesn't really prevent all that many bugs, IMO. const, as it stands now in D, is fine with me. It means constant, and cannot be changed at runtime. It's a binding contract, which is what const should be. The only think that I would add, if it's possible, is to add const type functions, although with another keyword. Basically, it would be another contract, with no passed in variables, pointers, or references being changeable in the body of that particular function. Anything not in the local scope being assigned a value of any kind would be an error. I don't know if this is possible, from a compiler writer's standpoint, but of all the variant meanings of const, this is the only one that I would support adding to D. Like I said, I don't know about the keyword (other than it needs to be called something other than const) but I'm sure that we can come up with something that fits the semantics well. Evan
Dec 03 2002
"Robert W. Cunningham" <FlyPG users.sourceforge.net> wrote in message news:3DEC11FC.DB684E1C users.sourceforge.net...Has anyone tried to write a device driver in D? How about a small OS (or a port of uCOS) that boots itself? Those are the kids of things Systems Programming Languages are created for. I don't think they can be done in D without lots of work-arounds in assembler that C doesn't need: D doesn't play nice with hardware, especially when the processor has a cache. Ergo, (IMHO) D is not (yet) a systems programming language.I am intimately familiar with how C compilers generate code, with and without const and volatile. There just isn't much to be gained with it, and a lot of trouble is caused by it. D, however, does support the const as a storage class, as in: const foo = 3; D also supports the volatile statement, which prevents caching of values across the statement boundaries. The combination of this, the const storage class, and the ability to access arbitrary addresses with pointers, I think fills the bill. D also has, unlike C or C++, a defined interface to assembler. Take a look at the linux kernel source, it is full of random bits of assembler. That doesn't mean that C isn't a systems programming language. What it does mean is that the awful gnuc inline assembler leaves a lot of room for improvement, which D does.Walter, all your points AGAINST const are valid! Just because you know of many misuses for and difficulties with 'const' doesn't mean there still aren't many VALID needs and uses for const and volatile. Just because you haven't encountered them and can't envision them does not mean they don't exist. It only means I suck at explaining them. I wish I could craft a better argument, or provide the needed insight and education. Despite several tries, I have failed miserably to convince you.I do respect your opinion on this, and I have been equally hard at work trying to convince you otherwise!Or you can simply TRUST ME that some minimal form of 'const' and 'volatile' are mandatory for systems programming, and add them to the D language specification.They are there in the (minimal) form mentioned earlier. And yes, I have written device drivers in both C and assembler.
Dec 14 2002
Robert W. Cunningham wrote:A systems programming language (SPL) requires two fundamental features of its type system in order to meet any minimal definition of being an SPL: 'const' and 'volatile' (or equivalent). Even Ada has them, in very constrained but still VERY useful forms.Assembly language doesn't have 'const' or anything equivalent. So how can you say it is required or a system programming language? As for volatile, that only impacts optimization techniques. Since assembly language programs are normally not optimized by the assembler, volatile is not needed. Likewise, you can turn off all optimizations in a compiler and thus volatile won't be necessary there, either. Thus, neither is actually necessary. IMHO, Walter's design, while not perfect, is an incremental advance over C/C++. It allows us to use volatile statements to read & write things, but gives us more optimization than C allowed.
Dec 16 2002
Russell Lewis <spamhole-2001-07-16 deming-os.org> Wrote:As for volatile, that only impacts optimization techniques.Well, in C, volatile is usefull when I have a variable that is a representation of an I/O port that is connected to some hardware you're monitoring. For instance, say your program is deciding something based on the state of a sensor. Because you never know when the state of the port will change, you'll have to use volatile to force the program to really check the port, otherwise you're (maybe/probable) hitting an old cached value, and get wrong results. How does D solve that?
Mar 09 2003
"Pedro Alves" <Pedro_member pathlink.com> wrote in message news:b4f1eo$1dd9$1 digitaldaemon.com...Russell Lewis <spamhole-2001-07-16 deming-os.org> Wrote:representationAs for volatile, that only impacts optimization techniques.Well, in C, volatile is usefull when I have a variable that is aof an I/O port that is connected to some hardware you're monitoring. For instance, say your program is deciding something based on the state of asensor.Because you never know when the state of the port will change, you'll havetouse volatile to force the program to really check the port, otherwiseyou're(maybe/probable) hitting an old cached value, and get wrong results. How does D solve that?volatile ! http://www.digitalmars.com/d/statement.html#volatile
Mar 09 2003
"Walter" <walter digitalmars.com> wrote in news:asccu1$h8l$1 digitaldaemon.com:5) Const as a type modifier permeates the type system, and has effects spilling all over places it has no business doing so. For example, it affects the partial specialization rules of templates, not in any clarifying way, but just adding more layers of complexity and special case rules.I have a simple question about current C++/D compiler implementations, just to assure that I'm on the right way: Would it make the D compiler implementation easier if D treated "const someRefType" as the imaginary base-type of the type "someRefTyp" ? All rules for overloading, type compatibilty, partial template specialization, etc. that are used for inherited types are simply applied to const. Sure, the type hierarchy of const is a bit special: example: class Base {} class Child : Base {} now the imaginary inheritance tree would be: a)"const Child" inherits "const Base". but b)Child inherits "const Child" inherits Base inherits "const Base". I suppose that you can deduce most rules of C++ regarding const with this simple notion. One exception I know of, are the partial specialization rules of C++ templates. But these rules look counter-intuitive and are often useless. The rules deduced from my notion of const do not look worse than those of C++ to me. Maybe they are even better. Could anyone provide an example that proves that my simple notion of const cannot fit the bill ? Farmer
Jan 13 2003
"Farmer" <itsFarmer. freenet.de> escreveu na mensagem news:Xns9303EEFE9D5FitsFarmer 63.105.9.61..."Walter" <walter digitalmars.com> wrote in news:asccu1$h8l$1 digitaldaemon.com:just5) Const as a type modifier permeates the type system, and has effects spilling all over places it has no business doing so. For example, it affects the partial specialization rules of templates, not in any clarifying way, but just adding more layers of complexity and special case rules.I have a simple question about current C++/D compiler implementations,to assure that I'm on the right way: Would it make the D compiler implementation easier if D treated "const someRefType" as the imaginary base-type of the type "someRefTyp" ? All rules for overloading, type compatibilty, partial template specialization, etc. that are used for inherited types are simply applied to const. Sure, the type hierarchy of const is a bit special: example: class Base {} class Child : Base {} now the imaginary inheritance tree would be: a)"const Child" inherits "const Base". but b)Child inherits "const Child" inherits Base inherits "const Base". I suppose that you can deduce most rules of C++ regarding const with this simple notion. One exception I know of, are the partial specialization rules of C++ templates. But these rules look counter-intuitive and are often useless. The rules deduced from my notion of const do not look worse than those of C++ to me. Maybe they are even better. Could anyone provide an example that proves that my simple notion of const cannot fit the bill ? FarmerHi, This subject is one of those continuous discussions on Usenet. comp.lang.eiffel has some posts talking about this, regarding CQS (command/query separation) and lots of people talked about it. Here's my thoughts about it. There are two views of what "const" means when applied to types: (1) this is constant and will never change; or (2) this is mutable but YOU can't change. Each gives a whole different world to the compiler and to the programmer. Also there's const applied to functions, both with two different views: (A) this function is a pure function, you can use equational reasoning about it; or (B) this function won't change any of it's parameters (including implicit this for methods), or that's what I want you to think. When you create a const ancestor type to any type, you want it to mean both const for type (either 1 or 2) with interfaces defined for the type's const functions (either A or B). These four possible combinations can give us lots of headache. So let's think about them: (1A) this means functional objects, that is objects with value semantics. Structs aren't true value objects but C/C++ folk usually talk about them being so. Anyway you could define: class Color { const Color darker(); const Color lighter(); const int alpha(); const int red(); const int green(); const int blue(); const float hue(); const float saturation(); const float brightness(); const char[] toString() void printOn(OutputStream out) } and a type const Color would be defined with all these methods in it. Note that const in the methods applies to the method, not to the result type, so char[] from toString() is mutable and Color from darker() and lighter() too, not that it means something. But it gives you two functions that map const Color domain to Color domain. (1B) this is similar to 1A, but includes trick functions. In the same Color class we could define this: class Color { // same as before const int cyan(); const int magenta(); const int yellow(); const int black(); } The four new methods are const, but inside their definitions they break the rules lazily caching the values so multiple uses won't result in multiple computations. All cache mechanisms for pure-functions must "cast away the constness". (2A) Useful for restricting user's access to mutable methods of types. class Car { const void lookAt(); const void wash(); void drive(); void crash(); } class SomeoneElse { void driveInto(Car target); } abstract class Driver { abstract (const Car) lendCarTo(const Driver any); abstract Car lendCarTo(const GoodDriver good); } class LameDriver : Driver { } class GoodDriver : Driver { } I think it's valid overloading. The GoodDriver class can access a more complete version of Car, because it will never call the mutable method crash(), but needs to use drive(). LameDriver's skills stop him from doing anything besides lookAt() and wash(). But while LameDriver lookAt() theCar SomeoneElse can driveInto(theCar) and crash it anyways, because they can mutate it. It's the same problem with collections and immutable interfaces, or using iterators while another thread changes the collection. (2B) Similar to 2A, but in this case drive() could be also declared const and secretly update the gas tank during all method calls. Each combination has it's strengths and weaknesses, now let's see how could we implement this mess of semantics in D: (1A) give a const modifier to classes and functions. It's similar to Sather immutable objects. It will help people define better semantics for their types (a Date class could be const, like it should be, not like some other OO languages do ;-) ). (1B) It's 1A plus a cast to allow secret mutations. Walter will love it ;-) (2A) give more things to the compiler play with. It's also a fun thing to toy with: slice operations give const type or type? Current D's array semantics (very pragmatic) use COA (like COW but appending instead of writing), so "type [index] = value" should be a operation of const type, but "type ~= value" should be a operation of type. (2B) more fun than 2B, after all you can always cast the constness away to make things weirder. AFAICS this is that const means lots of things, and each of these things is different from the others. We should think very carefully before changing any type system semantics with such huge change. No imperative language currently has this (and IIRC not hibrid language too). Best regards, Daniel Yokomiso. P.S.: When I implement my pet language, Eon, I'll give it immutable types and values, but it's goals and type system are different from D's. I'm entering on the second year of study to create it's type system (hundreds of papers read) and in the meantime I put and dropped immutable types several times. If someone is interested I plan to provide both 1A and 2A, but throught two different mechanisms. "Chaos! Panic! Disaster! (My work here is done)." --- Outgoing mail is certified Virus Free. Checked by AVG anti-virus system (http://www.grisoft.com). Version: 6.0.435 / Virus Database: 244 - Release Date: 30/12/2002
Jan 14 2003
Hi, my question "Could anyone provide an example that proves that my simple notion of const cannot fit the bill ?" should read: "Are all those complicated rules of C++ regarding "const", really necessary. Please show me examples, where my simple notion of const (mapping the const attribute to a class hierarchy), would not work in C++ or in D". By "const", I primarily think of "const" applied to class methods in C++. now my comments on your post: I think the 2B meaning of constness is most useful for D programmers.(2) this is mutable but YOU can't change. (B) this function won't change any of it's parameters (including implicit this for methods), or that's what I want you to think.Equational reasoning is not a concern for most D programmers. They usually think that functions do some work like I/O operations. The meaning(1) this is constant and will never change;is too limiting for most cases. Of course there could be uses: e.g. java.lang.Stringabstract class Driver { abstract (const Car) lendCarTo(const Driver any); abstract Car lendCarTo(const GoodDriver good); }I guess, you wanted to write abstract class Driver { abstract (const Car) lendCarTo(Driver any); abstract Car lendCarTo(GoodDriver good); } since the state of both Driver and GoodDriver instance will change. (now they have access to a car)I think it's valid overloading. The GoodDriver class can access a more complete version of Car, because it will never call the mutable method crash(), but needs to use drive(). LameDriver's skills stop him from doing anything besides lookAt() and wash(). But while LameDriver lookAt() theCar SomeoneElse can driveInto(theCar) and crash it anyways, because they can mutate it. It's the same problem with collections and immutable interfaces, or using iterators while another thread changes the collection.Right. But that is exactly the meaning that should be expressed by constness: You prevented the LameDriver from crashing your car. But a tornado still can do that (You cannot prevent that in real life ->a compiler can't do either). You need to design an indestructable car to get an immutable Car class.(2A) give more things to the compiler play with. It's also a fun thing to toy with: slice operations give const type or type? Current D's array semantics (very pragmatic) use COA (like COW but appending instead of writing), so "type [index] = value" should be a operation of const type, but "type ~= value" should be a operation of type. (2B) more fun than 2B, after all you can always cast the constness away to make things weirder.My knowledge about pure functions are non existent, so I'm surely missed some fun. Why should "type [index] = value" be an operation of const ? According to my simple rules for const. You could only get a "const slice" from a "const slice". But from a "nonconst slice" you can get both version.AFAICS this is that const means lots of things, and each of these things is different from the others. We should think very carefully before changing any type system semantics with such huge change.That's why I'm asking Walter whether it is possible to reduce the complexity of constness (for both compiler writers and programmers) in first placen, BEFORE I'm trying to work out a proposal to introduce const to D. And I think, it's likely to be impossible to introduce const to D's type system. The type system is already complex hardly any support for constness. Well, I have some thoughts about const, too: I feel that the following notions of constness could be useful to a programming language like D. typical keywords: meaning 1)const (D): compile time constants; no memory is allocated for these constants. has been initialized. 3)const (C++ in context of methods): logical (not necessarily bitwise) constness of UDT's, meaning that the object state cannot be change using this variable identifier. 4)__romable, const (Ansi C99 ?): variable can be stored in ROM-memory; its value will never change. [I don't need that, but maybe some embedded systems programmers will love it.] 5)__callconst: this attribute can only be applied to non-const arguments of functions. It should mean that the argument is not changed within the function. But the argument is usually stored in a global variable (or member variable ). This constness could be useful for container classes: E.g. a stack stores an non-const object with the push() method. This method does not change the object. Though, this method cannot take an const argument, because you want to get a non-const object back from the stack with the pop() method. Unfortunately I, see no solution for a D-compiler to enforce this behaviour -> this keyword could only be syntax sugar for writting self-documenting code. Of course, one could hack the language to catch at least the most obvious violations of "__callconst". Do you know of any language, that can express constness as required for container classes ? Farmer.
Jan 19 2003