www.digitalmars.com         C & C++   DMDScript  

D - in,out,inout: new sematic proposal

reply Farmer <itsFarmer. freenet.de> writes:
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 
meaning to them as for object. Java and C# programmers will assume that 
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 
C# (you can change the array elements, but not return a newly created array 
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) 


*: I assumme that C, C++, C# and Java-programmers are spoilt by their odd 
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
next sibling parent reply "Sandor Hojtsy" <hojtsy index.hu> writes:
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.

 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

 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

 compilers will produce better code.

 Today even the worst optimizing compiler (sth. like Borlands RAD style

 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.

 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

 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,

 -ignore it: compiler does not ensure constness, the programmer is paid to
 take care of this. ( would be perfectly acceptible for an alpha compiler,

 think)

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 its

 so you are always working on the handle, except when using the

 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 not

     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
 meaning to them as for object. Java and C# programmers will assume that
 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
 C# (you can change the array elements, but not return a newly created

 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

 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

 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)


 *: I assumme that C, C++, C# and Java-programmers are spoilt by their odd
 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
next sibling parent antti.sykari housemarque.fi writes:
"Sandor Hojtsy" <hojtsy index.hu> writes:
 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.

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". Antti
Oct 31 2002
prev sibling parent reply Farmer <itsFarmer. freenet.de> writes:
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++-mistake 
 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 when writing C, C++, C# or Java code. So I will continue to use this semantic when I try to clarify some issues of my proposal.
 D: void foo(in short[5] var)
 translates to
 C: void foo(const short* var)

You mean void foo(const short* const var)

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 stuff
 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, because im used to it (kind of idiom in C/C++, Java, C#?) and it's less 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.
 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.

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. ---
 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 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).
 4. Const clutteres the declarations of functions and reference types
 too much.

Then, "in" clutters the declarations of functions too much.

already cluttered with in (optional), out, inout. Have a nice D.
Nov 01 2002
parent reply "Sandor Hojtsy" <hojtsy index.hu> writes:
"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++-mistake
 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; }

Conceptually, 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.
 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
 when writing C, C++, C# or Java code. So I will continue to use this
 semantic when I try to clarify some issues of my proposal.


 D: void foo(in short[5] var)
 translates to
 C: void foo(const short* var)

You mean void foo(const short* const var)

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.

I see.
 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,out

 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 )   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 stuff




 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, because im used to it (kind of idiom in C/C++, Java, C#?) and it's less to type. But both versions are ugly and not really fast (a structure is returned by value).

I don't find the first version ugly. How else would you return a structure? Return a reference to a local structure variable?
 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?
 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.

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.

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.
 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.
 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 mean all that stuff that makes up a complex real world application: Project specific classes (the thing that is thought up by yourself and

 team), common utility classes of your employer,
 runtime library of the programming language, and additional

 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).

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.
 4. Const clutteres the declarations of functions and reference types
 too much.

Then, "in" clutters the declarations of functions too much.

already cluttered with in (optional), out, inout.

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, Sandor
Nov 05 2002
parent reply Farmer <itsFarmer. freenet.de> writes:
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 ?

ASM    C  C++                           C#    Java
<------------------------------------------------>

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 happening, indeed C# uses "ref" whenever D uses "inout".
...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 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)
 {
 ...
 }
 

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.

#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."

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? 

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?
 

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.
 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?

Because, it is very easy to see what's happening for the maintenance- programmer. It also prevents a possible bug.
 
 
 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:

use it to accomplish the same task.
 
 Point move(int xd, int yd)
 {
    Point p = new Point(this);
    p.x += xd;
    p.y += yd;
    return p;
 }
 
 Now does it look good?

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.

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
parent reply "Sandor Hojtsy" <hojtsy index.hu> writes:
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 ?

 ASM    C  C++                           C#    Java
 <------------------------------------------------>

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.
...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 was not talking about references in general; my proposal is for an

 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.

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.
 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)
 {
 ...
 }

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.
 The solution is different terminonlogy.

#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 ;-)

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 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."

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.

I see.
 If you still want immutable "in" parameters for value types, then why
 are we arguing about how to specify/implement fast mutable "in"
 parameters?

C: foo(structType v) is superfluous even if you consider performance.

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.
 I don't find the first version ugly. How else would you return a
 structure? Return a reference to a local structure variable?

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.
 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 );

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"!
 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.

 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

afaik, 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
parent Farmer <itsFarmer. freenet.de> writes:
"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"

technical sense). My guess: It will inevitably become known as "C/C++/D".
 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 ?

 ASM    C  C++                           C#    Java
 <------------------------------------------------>

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.

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.
 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:

a language suitable for true general application programming. Java was not invented for that task. C# looks better, but since .Net/C# is complex to 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".

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.

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.

current/future D versions.
 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.

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.
 If you still want immutable "in" parameters for value types, then
 why are we arguing about how to specify/implement fast mutable "in"
 parameters?

C: foo(structType v) is superfluous even if you consider performance.

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.

[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.
 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.

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 here

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
prev sibling parent reply "Walter" <walter digitalmars.com> writes:
"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
next sibling parent reply "Robert W. Cunningham" <FlyPG users.sourceforge.net> writes:
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
next sibling parent Evan McClanahan <evan dontSPAMaltarinteractive.com> writes:
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
prev sibling next sibling parent "Walter" <walter digitalmars.com> writes:
"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
prev sibling parent reply Russell Lewis <spamhole-2001-07-16 deming-os.org> writes:
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
parent reply Pedro Alves <Pedro_member pathlink.com> writes:
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
parent "Mike Wynn" <mike.wynn l8night.co.uk> writes:
"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:
As for volatile, that only impacts optimization techniques.

Well, in C, volatile is usefull when I have a variable that is a

 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

 Because you never know when the state of the port will change, you'll have

 use volatile to force the program to really check the port, otherwise

 (maybe/probable) hitting an old cached value, and get wrong results.
 How does D solve that?

http://www.digitalmars.com/d/statement.html#volatile
Mar 09 2003
prev sibling parent reply Farmer <itsFarmer. freenet.de> writes:
"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
parent reply "Daniel Yokomiso" <daniel_yokomiso yahoo.com.br> writes:
"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:

 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,

 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

Hi, 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
parent Farmer <itsFarmer. freenet.de> writes:
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; 

java.lang.String
 abstract 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. 

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.

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.

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. 2)readonly (C#), final (Java): variable that cannot be changed after it 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