www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Performance improvements for D / DMD compiler.

reply Dave <Dave_member pathlink.com> writes:
What's on your wish-list (let's leave the GC and other library functionality
out of this for now <g>)?

Here's mine:
   - Better floating point code generation
   - Put the tail-recursion optimization back in
   - Omit frame pointers w/ -O switch
   - Improved in-lining (don't arbitrarily exclude functions w/ inout
parameters and loops).
   - Auto-finalization.
Jan 19 2007
next sibling parent reply janderson <askme me.com> writes:
Dave wrote:
 
 What's on your wish-list (let's leave the GC and other library 
 functionality out of this for now <g>)?
 
 Here's mine:
   - Better floating point code generation
   - Put the tail-recursion optimization back in
   - Omit frame pointers w/ -O switch
   - Improved in-lining (don't arbitrarily exclude functions w/ inout 
 parameters and loops).
   - Auto-finalization.

The above and: - Better GC that uses meta data to only track pointers (rather then any number within a specific range). - Improved efficient library functions (perhaps tango will fix this). -Joel
Jan 19 2007
parent reply janderson <askme me.com> writes:
janderson wrote:
 Dave wrote:
 What's on your wish-list (let's leave the GC and other library 
 functionality out of this for now <g>)?

 Here's mine:
   - Better floating point code generation
   - Put the tail-recursion optimization back in
   - Omit frame pointers w/ -O switch
   - Improved in-lining (don't arbitrarily exclude functions w/ inout 
 parameters and loops).
   - Auto-finalization.

The above and: - Better GC that uses meta data to only track pointers (rather then any number within a specific range). - Improved efficient library functions (perhaps tango will fix this). -Joel

Also: Something like ned-malloc in the standard lib. -Joel
Jan 19 2007
parent janderson <askme me.com> writes:
janderson wrote:
 janderson wrote:
 Dave wrote:

Also: Something like ned-malloc in the standard lib. -Joel

I should add, compiler identifying cases where: 1) An allocation doesn't need to be put into the GC (ie it could be placed on the stack.) 2) Compiler work out at compile time when a particular piece of memory could be deleted (rather then complete GC). Ok we have auto however I'm talking about places where this can't be used. Of course what these cases are, needs more thought (and could be optional). However this is no where near the top of my performance wish list. -Joel
Jan 21 2007
prev sibling next sibling parent reply BCS <BCS pathlink.com> writes:
Dave wrote:
 
 What's on your wish-list (let's leave the GC and other library 
 functionality out of this for now <g>)?
 
 Here's mine:
   - Better floating point code generation
   - Put the tail-recursion optimization back in
   - Omit frame pointers w/ -O switch
   - Improved in-lining (don't arbitrarily exclude functions w/ inout 
 parameters and loops).
   - Auto-finalization.

Better const folding/handling, local const values are put into the stack frame even if they are never used. (disassemble a run of my bignum template to see this). preferential inlining of functions with lazy arguments or that take the form: fn(delegate() dg) { //short code dg(); //short code } aggressive de-virtualization of method calls.
Jan 19 2007
parent Frits van Bommel <fvbommel REMwOVExCAPSs.nl> writes:
BCS wrote:
 Better const folding/handling, local const values are put into the stack 
 frame even if they are never used. (disassemble a run of my bignum 
 template to see this).

On a related note: put global variables (especially constants) into special sections of the object file. That way they can be removed by --gc-sections if not used (or optimized out). Constants are frequently completely folded but still instantiated, this just adds bloat. This isn't really a performance optimization so much as managing its side-effects. (Also, why is a const int added to .data instead of .rodata?)
Jan 19 2007
prev sibling parent reply "Christian Kamm" <kamm nospam.de> writes:
 What's on your wish-list (let's leave the GC and other library  
 functionality out of this for now <g>)?

I'd like to see unneccessary copies being optimized away, especially of large structs passed to functions. The workaround // use inout to pass s by reference, it is not actually modified void foo(inout LargeStruct s) is just abusing inout. In my opinion it should be an in parameter and D should detect whether a copy is neccessary or not. It's not done already, is it? Christian
Jan 19 2007
parent reply "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
Christian Kamm wrote:
 What's on your wish-list (let's leave the GC and other library 
 functionality out of this for now <g>)?

I'd like to see unneccessary copies being optimized away, especially of large structs passed to functions. The workaround // use inout to pass s by reference, it is not actually modified void foo(inout LargeStruct s) is just abusing inout. In my opinion it should be an in parameter and D should detect whether a copy is neccessary or not. It's not done already, is it?

No, and for a good reason: foo(LargeStruct s1, LargeStruct s2) { ... } ... LargeStruct s; foo(s, s); Nobody would enjoy hidden aliasing of s1 and s2. Andrei
Jan 19 2007
next sibling parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
Andrei Alexandrescu (See Website For Email) wrote:
 Christian Kamm wrote:
 What's on your wish-list (let's leave the GC and other library 
 functionality out of this for now <g>)?

I'd like to see unneccessary copies being optimized away, especially of large structs passed to functions. The workaround // use inout to pass s by reference, it is not actually modified void foo(inout LargeStruct s) is just abusing inout. In my opinion it should be an in parameter and D should detect whether a copy is neccessary or not. It's not done already, is it?

No, and for a good reason: foo(LargeStruct s1, LargeStruct s2) { ... } ... LargeStruct s; foo(s, s); Nobody would enjoy hidden aliasing of s1 and s2. Andrei

I'm not really sure why that would be a problem, as long as the compiler has really determined that s1 and s2 aren't being modified by the function. Maybe you can elaborate? If it's effectively the same as a const reference, how can the aliasing make a big difference? But anyway, I don't think it's possible to change way arguments are passed on what the function body does with them. Say all you have is a .di file with this signature in it: void foo(LargeStruct s1, LargeStruct s2); How is the compiler going to know how to push the parameters on the stack to call foo? I think it may be possible, however, to establish a rule like "all struct parameters larger than a 'real' are actually passed by reference". Then it would be up to the compiler to make copies of any modified arguments within the body of the function. So the code generated for something like void foo(LargeStruct s1) { s1.x = 10; ...use s1... } would look more like: void foo(inout LargeStruct s1) { LargeStruct s1tmp = s1; s1tmp.x = 10; ...use s1tmp... } I guess you could call that "parameter copy-on-write". --bb
Jan 19 2007
parent janderson <askme me.com> writes:
I believe this is possible.  In particular since D generates its own 
header files.

I think it would be simple when the compiler knows the intentional (just 
like inlining works).  Perhaps with a DLL/lib the compiler could mark 
the functions as constant (or inout) and deal with it differently like 
you say.

Bill Baxter wrote:

 
 I'm not really sure why that would be a problem, as long as the compiler 
 has really determined that s1 and s2 aren't being modified by the 
 function.  Maybe you can elaborate?  If it's effectively the same as a 
 const reference, how can the aliasing make a big difference?
 
 But anyway, I don't think it's possible to change way arguments are 
 passed on what the function body does with them.  Say all you have is a 
 .di file with this signature in it:
   void foo(LargeStruct s1, LargeStruct s2);
 
 How is the compiler going to know how to push the parameters on the 
 stack to call foo?
 
 I think it may be possible, however, to establish a rule like "all 
 struct parameters larger than a 'real' are actually passed by 
 reference".  Then it would be up to the compiler to make copies of any 
 modified arguments within the body of the function.
 
 So the code generated for something like
 
 void foo(LargeStruct s1) {
    s1.x = 10;
    ...use s1...
 }
 
 would look more like:
 
 void foo(inout LargeStruct s1) {
    LargeStruct s1tmp = s1;
    s1tmp.x = 10;
    ...use s1tmp...
 }
 
 I guess you could call that "parameter copy-on-write".
 
 --bb
 

Jan 19 2007
prev sibling parent reply Walter Bright <newshound digitalmars.com> writes:
Andrei Alexandrescu (See Website For Email) wrote:
 No, and for a good reason:
 
 foo(LargeStruct s1, LargeStruct s2)
 {
   ...
 }
 ...
 LargeStruct s;
 foo(s, s);
 
 Nobody would enjoy hidden aliasing of s1 and s2.

There's another aliasing case: LargeStruct s; foo(s); ... here some other thread modifies s ... And foo() suddenly has its argument values change unexpectedly. Value and reference type usages mostly overlap, but they are fundamentally different, and having the compiler switch between the two in some implementation-defined manner (as suggested by others more than once) is not going to work. If one is needing reference behavior from a struct, seriously consider making it a class instead. Or even just take the address of it and pass the pointer.
Jan 19 2007
next sibling parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
Walter Bright wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
 No, and for a good reason:

 foo(LargeStruct s1, LargeStruct s2)
 {
   ...
 }
 ...
 LargeStruct s;
 foo(s, s);

 Nobody would enjoy hidden aliasing of s1 and s2.

There's another aliasing case: LargeStruct s; foo(s); ... here some other thread modifies s ... And foo() suddenly has its argument values change unexpectedly. Value and reference type usages mostly overlap, but they are fundamentally different, and having the compiler switch between the two in some implementation-defined manner (as suggested by others more than once) is not going to work. If one is needing reference behavior from a struct, seriously consider making it a class instead. Or even just take the address of it and pass the pointer.

If we're going to pass a pointer to it, then we might as well just make it inout and avoid having to explicitly use '&' to take the address. The question is how can we write functions in D that take big structs as parameters in such a way that it is: A) efficient and B) guaranteed not to change the caller's value In C++ the answer is const LargeStruct&. In D the answer is... --bb
Jan 20 2007
next sibling parent reply Walter Bright <newshound digitalmars.com> writes:
Bill Baxter wrote:
 The question is how can we write functions in D that take big structs as 
 parameters in such a way that it is:
 A) efficient and
 B) guaranteed not to change the caller's value
 
 In C++ the answer is const LargeStruct&.

C++ doesn't *guarantee* it won't change. There are at least two legal ways to do it.
 In D the answer is...

There aren't const references in D. But the 3 ways to pass it by pointer are: 1) inout 2) take the address and use a pointer 3) make it a class instead
Jan 20 2007
next sibling parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
Walter Bright wrote:
 Bill Baxter wrote:
 The question is how can we write functions in D that take big structs 
 as parameters in such a way that it is:
 A) efficient and
 B) guaranteed not to change the caller's value

 In C++ the answer is const LargeStruct&.

C++ doesn't *guarantee* it won't change. There are at least two legal ways to do it.

Very true. What I'm after is really a concise way to both a) document to readers of the code that it won't change and b) get the compiler to zap me if I accidentally try to modify it.
 In D the answer is...

There aren't const references in D. But the 3 ways to pass it by pointer are: 1) inout 2) take the address and use a pointer 3) make it a class instead

And none of those accomplishes a or b. --bb
Jan 20 2007
next sibling parent reply Kyle Furlong <kylefurlong gmail.com> writes:
Bill Baxter wrote:
 Walter Bright wrote:
 Bill Baxter wrote:
 The question is how can we write functions in D that take big structs 
 as parameters in such a way that it is:
 A) efficient and
 B) guaranteed not to change the caller's value

 In C++ the answer is const LargeStruct&.

C++ doesn't *guarantee* it won't change. There are at least two legal ways to do it.

Very true. What I'm after is really a concise way to both a) document to readers of the code that it won't change and b) get the compiler to zap me if I accidentally try to modify it.
 In D the answer is...

There aren't const references in D. But the 3 ways to pass it by pointer are: 1) inout 2) take the address and use a pointer 3) make it a class instead

And none of those accomplishes a or b. --bb

Ah the great const debate. I have a feeling that this discussion will be a major part of D 2.0.
Jan 20 2007
parent reply "Rioshin an'Harthen" <rharth75 hotmail.com> writes:
"Kyle Furlong" <kylefurlong gmail.com> wrote:
 Ah the great const debate. I have a feeling that this discussion will be a 
 major part of D 2.0.

I wish we'd gone the const by default way before releasing 1.0, no matter how much code would've been broken... It's so much harder now to get something as good into the language, as it's more likely that production code is being written with D, and we have to think of backwards compatibility. That wasn't much of an issue before.
Jan 20 2007
parent reply Lars Ivar Igesund <larsivar igesund.net> writes:
Rioshin an'Harthen wrote:

 "Kyle Furlong" <kylefurlong gmail.com> wrote:
 Ah the great const debate. I have a feeling that this discussion will be
 a major part of D 2.0.

I wish we'd gone the const by default way before releasing 1.0, no matter how much code would've been broken... It's so much harder now to get something as good into the language, as it's more likely that production code is being written with D, and we have to think of backwards compatibility. That wasn't much of an issue before.

This is true, by offputting this particular discussion for a resolution, the breaking when it is finally fixed will be much greater, whereas the current problematic behaviour will frustrate more than it did (after all, the D user mass seems to grow), possibly making the next round of discussions even harder to follow. -- Lars Ivar Igesund blog at http://larsivi.net DSource & #D: larsivi Dancing the Tango
Jan 24 2007
parent Lars Ivar Igesund <larsivar igesund.net> writes:
Lars Ivar Igesund wrote:

 Rioshin an'Harthen wrote:
 
 "Kyle Furlong" <kylefurlong gmail.com> wrote:
 Ah the great const debate. I have a feeling that this discussion will be
 a major part of D 2.0.

I wish we'd gone the const by default way before releasing 1.0, no matter how much code would've been broken... It's so much harder now to get something as good into the language, as it's more likely that production code is being written with D, and we have to think of backwards compatibility. That wasn't much of an issue before.

This is true, by offputting this particular discussion for a resolution, the breaking when it is finally fixed will be much greater, whereas the current problematic behaviour will frustrate more than it did (after all, the D user mass seems to grow), possibly making the next round of discussions even harder to follow.

I suppose I should read the _whole_ thread prior to posting such opinions, but it was so darned long! Going through all of it, it seems like we may gain a proper resolution soonish, which will be better than even later :) -- Lars Ivar Igesund blog at http://larsivi.net DSource & #D: larsivi Dancing the Tango
Jan 24 2007
prev sibling next sibling parent reply Chris Nicholson-Sauls <ibisbasenji gmail.com> writes:
Bill Baxter wrote:
 Walter Bright wrote:
 Bill Baxter wrote:
 The question is how can we write functions in D that take big structs 
 as parameters in such a way that it is:
 A) efficient and
 B) guaranteed not to change the caller's value

 In C++ the answer is const LargeStruct&.

C++ doesn't *guarantee* it won't change. There are at least two legal ways to do it.

Very true. What I'm after is really a concise way to both a) document to readers of the code that it won't change and b) get the compiler to zap me if I accidentally try to modify it.
 In D the answer is...

There aren't const references in D. But the 3 ways to pass it by pointer are: 1) inout 2) take the address and use a pointer 3) make it a class instead

And none of those accomplishes a or b. --bb

Just a thought: B) can be accomplished with an Interface that only exposes gettors. Granted, though, that 1) it means defining everything you want to be read-only as a property, but these would be optimized away by inlining anyhow, 2) you have to write an Interface (which isn't a big deal, IMHO, but I know some people can be annoyed with them). Also, you would want to make the implementor class and all methods 'final' to avoid potential virtuality overhead. Something akin to: <code> interface IFoo { int alpha () ; int beta () ; } final class Foo : IFoo { private { int m_alpha ; int m_beta ; } final int alpha () { return m_alpha ; } final int beta () { return m_beta ; } final int alpha (int x) { return m_alpha = x ; } final int beta (int x) { return m_beta = x ; } } void somefunc (IFoo obj) { // do stuff with obj.alpha and obj.beta } auto myfoo = new Foo; somefunc(myfoo); </code> -- Chris Nicholson-Sauls
Jan 20 2007
next sibling parent Bill Baxter <dnewsgroup billbaxter.com> writes:
Chris Nicholson-Sauls wrote:
 Bill Baxter wrote:
 Walter Bright wrote:
 Bill Baxter wrote:
 The question is how can we write functions in D that take big 
 structs as parameters in such a way that it is:
 A) efficient and
 B) guaranteed not to change the caller's value

 In C++ the answer is const LargeStruct&.

C++ doesn't *guarantee* it won't change. There are at least two legal ways to do it.

Very true. What I'm after is really a concise way to both a) document to readers of the code that it won't change and b) get the compiler to zap me if I accidentally try to modify it.
 In D the answer is...

There aren't const references in D. But the 3 ways to pass it by pointer are: 1) inout 2) take the address and use a pointer 3) make it a class instead

And none of those accomplishes a or b. --bb

Just a thought: B) can be accomplished with an Interface that only exposes gettors. Granted, though, that 1) it means defining everything you want to be read-only as a property, but these would be optimized away by inlining anyhow, 2) you have to write an Interface (which isn't a big deal, IMHO, but I know some people can be annoyed with them). Also, you would want to make the implementor class and all methods 'final' to avoid potential virtuality overhead. Something akin to: <code> interface IFoo { int alpha () ; int beta () ; } final class Foo : IFoo { private { int m_alpha ; int m_beta ; } final int alpha () { return m_alpha ; } final int beta () { return m_beta ; } final int alpha (int x) { return m_alpha = x ; } final int beta (int x) { return m_beta = x ; } } void somefunc (IFoo obj) { // do stuff with obj.alpha and obj.beta } auto myfoo = new Foo; somefunc(myfoo); </code> -- Chris Nicholson-Sauls

That's pretty clever, but it's an awful lot superfluous cruft for something that should just be as simple as struct Foo { int m_alpha, m_beta; } Reminds me of a quote I heard once about knitting needles and eyeballs. --bb
Jan 20 2007
prev sibling parent reply "Andrei Alexandrescu (See Website for Email)" <SeeWebsiteForEmail erdani.org> writes:
Chris Nicholson-Sauls wrote:
 Just a thought: B) can be accomplished with an Interface that only 
 exposes gettors.

 <code>
 interface IFoo {
   int alpha () ;
   int beta  () ;
 }
 
 final class Foo : IFoo {
   private {
     int m_alpha ;
     int m_beta  ;
   }
 
   final int alpha () { return m_alpha ; }
   final int beta  () { return m_beta  ; }
 
   final int alpha (int x) { return m_alpha = x ; }
   final int beta  (int x) { return m_beta  = x ; }
 }
 
 void somefunc (IFoo obj) {
   // do stuff with obj.alpha and obj.beta
 }
 
 auto myfoo = new Foo;
 somefunc(myfoo);
 </code>

I think this is very interesting because it opens the door to a new implementation of const that I never thought of. D is slated to have full-fledged compile-time reflection, meaning that you can find out all information about any given symbol, during compilation. So it turns out that a possible path to implement const is to make it a template. Given an arbitrary struct Foo, you could define a template called Const such that Const!(Foo) materializes into a struct that holds a Foo* inside, and exposes only the non-mutating parts of Foo. So, given: struct Foo { int alpha, beta; void nonmutator() { assert(alpha * beta != 0); } void mutator() { alpha = beta; } } the template Const!(Foo) generates the code: struct Const!(Foo) { private Foo* pFoo; this(inout Foo foo) { pFoo = &foo; } int alpha() { return pFoo->alpha; } int beta() { return pFoo->beta; } void nonmutator() { pFoo->nonmutator(); } } The reflection mechanism would have to provide the information whether or not a given member function changes the object. The only drawback that I can think right now is that the compiler can't exploit this kind of constness with ease to generate better code; it's a "user-space" implementation with semantics that are hard to figure out at the compiler level. A minor drawback is that Const!(Foo) must be implicitly constructible from a Foo, but another in-design language feature (opImplicitCast) will take care of that. Andrei
Jan 20 2007
next sibling parent reply Chris Nicholson-Sauls <ibisbasenji gmail.com> writes:
Andrei Alexandrescu (See Website for Email) wrote:
 Chris Nicholson-Sauls wrote:
 Just a thought: B) can be accomplished with an Interface that only 
 exposes gettors.

 <code>
 interface IFoo {
   int alpha () ;
   int beta  () ;
 }

 final class Foo : IFoo {
   private {
     int m_alpha ;
     int m_beta  ;
   }

   final int alpha () { return m_alpha ; }
   final int beta  () { return m_beta  ; }

   final int alpha (int x) { return m_alpha = x ; }
   final int beta  (int x) { return m_beta  = x ; }
 }

 void somefunc (IFoo obj) {
   // do stuff with obj.alpha and obj.beta
 }

 auto myfoo = new Foo;
 somefunc(myfoo);
 </code>

I think this is very interesting because it opens the door to a new implementation of const that I never thought of. D is slated to have full-fledged compile-time reflection, meaning that you can find out all information about any given symbol, during compilation. So it turns out that a possible path to implement const is to make it a template. Given an arbitrary struct Foo, you could define a template called Const such that Const!(Foo) materializes into a struct that holds a Foo* inside, and exposes only the non-mutating parts of Foo. So, given: struct Foo { int alpha, beta; void nonmutator() { assert(alpha * beta != 0); } void mutator() { alpha = beta; } } the template Const!(Foo) generates the code: struct Const!(Foo) { private Foo* pFoo; this(inout Foo foo) { pFoo = &foo; } int alpha() { return pFoo->alpha; } int beta() { return pFoo->beta; } void nonmutator() { pFoo->nonmutator(); } } The reflection mechanism would have to provide the information whether or not a given member function changes the object. The only drawback that I can think right now is that the compiler can't exploit this kind of constness with ease to generate better code; it's a "user-space" implementation with semantics that are hard to figure out at the compiler level. A minor drawback is that Const!(Foo) must be implicitly constructible from a Foo, but another in-design language feature (opImplicitCast) will take care of that. Andrei

Its not a bad idea, although with a little more flavor added to Tuples, template Const might become more general and therefore more usable. Particularly if we could get an 'identifier' construct in templates such as has been asked for before, and specializations that mimic what 'is' expressions can do, and a true "static foreach". (I know, its a long list of wants.) struct Const (T : struct) { private T* ptr ; static Const!(T) opCall (inout T t) { ptr = &t; } static foreach (field; T.tupleof) { typeof(field) identifier(field.nameof) () { return *ptr.field; } } } struct Foo { int alpha, beta; } Foo myfoo; auto cfoo = Const!(Foo)(myfoo); It loses the mapping to functions, though. I almost have to admit a 'const' like parameter storage class might well be the way to go. Either the 'byref' that has been mentioned previously, or a reuse of either 'static' or (*sigh*) 'const'. -- Chris Nicholson-Sauls
Jan 20 2007
parent reply "Andrei Alexandrescu (See Website for Email)" <SeeWebsiteForEmail erdani.org> writes:
Chris Nicholson-Sauls wrote:
 Andrei Alexandrescu (See Website for Email) wrote:
 D is slated to have full-fledged compile-time reflection, meaning that
 you can find out all information about any given symbol, during
 compilation. So it turns out that a possible path to implement const is
 to make it a template. Given an arbitrary struct Foo, you could define a
 template called Const such that Const!(Foo) materializes into a struct
 that holds a Foo* inside, and exposes only the non-mutating parts of Foo.

 So, given:

 struct Foo
 {
   int alpha, beta;
   void nonmutator() { assert(alpha * beta != 0); }
   void mutator() { alpha = beta; }
 }

 the template Const!(Foo) generates the code:

 struct Const!(Foo)
 {
   private Foo* pFoo;
   this(inout Foo foo) { pFoo = &foo; }
   int alpha() { return pFoo->alpha; }
   int beta() { return pFoo->beta; }
   void nonmutator() { pFoo->nonmutator(); }
 }

 The reflection mechanism would have to provide the information whether
 or not a given member function changes the object.

 The only drawback that I can think right now is that the compiler can't
 exploit this kind of constness with ease to generate better code; it's a
 "user-space" implementation with semantics that are hard to figure out
 at the compiler level.

 A minor drawback is that Const!(Foo) must be implicitly constructible
 from a Foo, but another in-design language feature (opImplicitCast) will
 take care of that.


 Andrei

Its not a bad idea, although with a little more flavor added to Tuples, template Const might become more general and therefore more usable. Particularly if we could get an 'identifier' construct in templates such as has been asked for before, and specializations that mimic what 'is' expressions can do, and a true "static foreach". (I know, its a long list of wants.)

Let's see: 1. The 'identifier' construct is likely to make it in D 2.0. Walter and I discussed it (the feature moniker that we use is "new alias") and there is a clear understanding of its necessity. So - check that one. 2. static foreach is a definite go. It's necessary for compile-time reflection. Walter acknowledged that himself on several occasions. Check that one too. 3. Specializations that mimic what 'is' expressions can do... not sure I understand that one. Check it too :o).
 struct Const (T : struct) {
   private T* ptr ;
 
   static Const!(T) opCall (inout T t) { ptr = &t; }
 
   static foreach (field; T.tupleof) {
     typeof(field) identifier(field.nameof) () { return *ptr.field; }
   }
 }

I had in mind something a tad more comprehensive: struct Const (T : struct) { private T* ptr ; // This sucks. Hopefully this() will make it in. static Const!(T) opCall (inout T t) { ptr = &t; } static foreach (field; T.tupleof) { typeof(field) field() { return *ptr.field; } } static foreach (fun; T.functionsof) { static if (fun.mutator) continue; fun.result fun(fun.argsOf args) { return ptr->fun(args); } } }
 It loses the mapping to functions, though.  I almost have to admit a 
 'const' like parameter storage class might well be the way to go.  
 Either the 'byref' that has been mentioned previously, or a reuse of 
 either 'static' or (*sigh*) 'const'.

The problem is that D does not have a clear stance on what a storage class is, and how it is to be manipulated statically. It is unclear whether storage is part of the type or a separate attribute of an expression. If this is not solved, it will inflict unbounded amounts of pain onto template code. Wiring more storage classes (such as 'lazy' which I realized Wednesday was a bad idea as it murks things even more and is thoroughly ill-designed because it is a storage class that actually changes the syntax of the object use -- someday Walter should stand the pressure and stop adding features just because people say they seem cool) into the language is not going to help any. Heck that was a long sentence :o). Andrei
Jan 20 2007
parent Bruno Medeiros <brunodomedeiros+spam com.gmail> writes:
Andrei Alexandrescu (See Website for Email) wrote:
 
 The problem is that D does not have a clear stance on what a storage
 class is, and how it is to be manipulated statically. It is unclear
 whether storage is part of the type or a separate attribute of an
 expression. If this is not solved, it will inflict unbounded amounts of
 pain onto template code. Wiring more storage classes (such as 'lazy'
 which I realized Wednesday was a bad idea as it murks things even more
 and is thoroughly ill-designed because it is a storage class that
 actually changes the syntax of the object use -- someday Walter should
 stand the pressure and stop adding features just because people say they
 seem cool) into the language is not going to help any. Heck that was a
 long sentence :o).
 
 
 Andrei
 

only seems strange if you think of it as a storage class (i.e., some that does not fundamentally change the underlying type). -- Bruno Medeiros - MSc in CS/E student http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
Jan 21 2007
prev sibling next sibling parent reply Dave <Dave_member pathlink.com> writes:
Andrei Alexandrescu (See Website for Email) wrote:
 Chris Nicholson-Sauls wrote:
 Just a thought: B) can be accomplished with an Interface that only 
 exposes gettors.

 <code>
 interface IFoo {
   int alpha () ;
   int beta  () ;
 }

 final class Foo : IFoo {
   private {
     int m_alpha ;
     int m_beta  ;
   }

   final int alpha () { return m_alpha ; }
   final int beta  () { return m_beta  ; }

   final int alpha (int x) { return m_alpha = x ; }
   final int beta  (int x) { return m_beta  = x ; }
 }

 void somefunc (IFoo obj) {
   // do stuff with obj.alpha and obj.beta
 }

 auto myfoo = new Foo;
 somefunc(myfoo);
 </code>

I think this is very interesting because it opens the door to a new implementation of const that I never thought of. D is slated to have full-fledged compile-time reflection, meaning that you can find out all information about any given symbol, during compilation. So it turns out that a possible path to implement const is to make it a template. Given an arbitrary struct Foo, you could define a template called Const such that Const!(Foo) materializes into a struct that holds a Foo* inside, and exposes only the non-mutating parts of Foo. So, given: struct Foo { int alpha, beta; void nonmutator() { assert(alpha * beta != 0); } void mutator() { alpha = beta; } } the template Const!(Foo) generates the code: struct Const!(Foo) { private Foo* pFoo; this(inout Foo foo) { pFoo = &foo; } int alpha() { return pFoo->alpha; } int beta() { return pFoo->beta; } void nonmutator() { pFoo->nonmutator(); } }

But you could still trivially and legally subvert this 'const'-ness: Const!(Foo) c(Foo(100,200)); Foo* fp = *cast(Foo**)&c; fp.alpha = 10000; writefln(c.alpha); So according to what Walter has said in the past about 'semantically meaningful const' then it wouldn't be enough to base optimizations on (even if the compiler could glean useful information from this).
 The reflection mechanism would have to provide the information whether
 or not a given member function changes the object.
 
 The only drawback that I can think right now is that the compiler can't
 exploit this kind of constness with ease to generate better code; it's a
 "user-space" implementation with semantics that are hard to figure out
 at the compiler level.
 

Both "user-space" and "compiler-space" const has been discussed at length in the past, and Walter's response has always been (paraphrasing) "D won't have const unless it is semantically meaningful (100% enforceable) by the compiler", which will never happen with a language like D.
 A minor drawback is that Const!(Foo) must be implicitly constructible
 from a Foo, but another in-design language feature (opImplicitCast) will
 take care of that.
 
 
 Andrei

Jan 20 2007
parent reply "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
Dave wrote:
 Andrei Alexandrescu (See Website for Email) wrote:
 D is slated to have full-fledged compile-time reflection, meaning that
 you can find out all information about any given symbol, during
 compilation. So it turns out that a possible path to implement const is
 to make it a template. Given an arbitrary struct Foo, you could define a
 template called Const such that Const!(Foo) materializes into a struct
 that holds a Foo* inside, and exposes only the non-mutating parts of Foo.

 So, given:

 struct Foo
 {
   int alpha, beta;
   void nonmutator() { assert(alpha * beta != 0); }
   void mutator() { alpha = beta; }
 }

 the template Const!(Foo) generates the code:

 struct Const!(Foo)
 {
   private Foo* pFoo;
   this(inout Foo foo) { pFoo = &foo; }
   int alpha() { return pFoo->alpha; }
   int beta() { return pFoo->beta; }
   void nonmutator() { pFoo->nonmutator(); }
 }

But you could still trivially and legally subvert this 'const'-ness: Const!(Foo) c(Foo(100,200)); Foo* fp = *cast(Foo**)&c; fp.alpha = 10000; writefln(c.alpha);

Casts can break any design, so it's not phasing anyone off that they can break const. Besides, the fact that Const holds a pointer to the object as its first element is not something client code is allowed to rely on. This is a non-issue.
 So according to what Walter has said in the past about 'semantically 
 meaningful const' then it wouldn't be enough to base optimizations on 
 (even if the compiler could glean useful information from this).

With well-placed casts and simple operations you can subvert the workings of the garbage collector. The D compiler is not required to make every code that compiles, work as someone expects.
 The only drawback that I can think right now is that the compiler can't
 exploit this kind of constness with ease to generate better code; it's a
 "user-space" implementation with semantics that are hard to figure out
 at the compiler level.

Both "user-space" and "compiler-space" const has been discussed at length in the past, and Walter's response has always been (paraphrasing) "D won't have const unless it is semantically meaningful (100% enforceable) by the compiler", which will never happen with a language like D.

This statement must be understood in spirit. The spirit is, D shouldn't add a feature that fails spectacularly at providing a true guarantee, as C++ did. The design I suggested does without the actual feature. D 2.0 will have const. Walter wants it as much as the next guy. The challenge is indeed finding something that has a good complexity/performance ratio. What I like about the template const idea is that it is entirely automatic - the user doesn't have to annotate functions manually as being const or not. The template will extract with precision the part of a type that can be used without mutating the object. (Such a deduction can also be part of a built-in feature.) To start talking about const, D must first address the inout issue, which is nothing short of a source of radioactive decay that affects negatively random parts of the language, which otherwise has a sound design. Walter acknowledges that there is a problem, but I don't think he and I have the same understanding of its consequences (which I believe are enormous). Once inout is taken care of, we need to take a deep breath and address the issue of lazy (which is related), and only after that const can enter the debate table. Andrei
Jan 20 2007
parent reply Dave <Dave_member pathlink.com> writes:
Andrei Alexandrescu (See Website For Email) wrote:
 Dave wrote:
 Andrei Alexandrescu (See Website for Email) wrote:
 D is slated to have full-fledged compile-time reflection, meaning that
 you can find out all information about any given symbol, during
 compilation. So it turns out that a possible path to implement const is
 to make it a template. Given an arbitrary struct Foo, you could define a
 template called Const such that Const!(Foo) materializes into a struct
 that holds a Foo* inside, and exposes only the non-mutating parts of 
 Foo.

 So, given:

 struct Foo
 {
   int alpha, beta;
   void nonmutator() { assert(alpha * beta != 0); }
   void mutator() { alpha = beta; }
 }

 the template Const!(Foo) generates the code:

 struct Const!(Foo)
 {
   private Foo* pFoo;
   this(inout Foo foo) { pFoo = &foo; }
   int alpha() { return pFoo->alpha; }
   int beta() { return pFoo->beta; }
   void nonmutator() { pFoo->nonmutator(); }
 }

But you could still trivially and legally subvert this 'const'-ness: Const!(Foo) c(Foo(100,200)); Foo* fp = *cast(Foo**)&c; fp.alpha = 10000; writefln(c.alpha);

Casts can break any design, so it's not phasing anyone off that they can break const. Besides, the fact that Const holds a pointer to the object as its first element is not something client code is allowed to rely on. This is a non-issue.

I wish Walter would have agreed with many of us that it (casts breaking any design) is a "non-issue" for going ahead with a 'semantically meaningful const' a year ago. I hope you can convince him!
 So according to what Walter has said in the past about 'semantically 
 meaningful const' then it wouldn't be enough to base optimizations on 
 (even if the compiler could glean useful information from this).

With well-placed casts and simple operations you can subvert the workings of the garbage collector. The D compiler is not required to make every code that compiles, work as someone expects.

I agree. Just like in http://digitalmars.com/d/garbage.html where it's acknowledged that certain pointer operations should not be used on GC'd memory, I think a section like that on 'const' would be good enough as long as the compiler enforced what could reasonably be expected. "If you (intentionally) break it, you buy it."
 The only drawback that I can think right now is that the compiler can't
 exploit this kind of constness with ease to generate better code; it's a
 "user-space" implementation with semantics that are hard to figure out
 at the compiler level.

Both "user-space" and "compiler-space" const has been discussed at length in the past, and Walter's response has always been (paraphrasing) "D won't have const unless it is semantically meaningful (100% enforceable) by the compiler", which will never happen with a language like D.

This statement must be understood in spirit. The spirit is, D shouldn't add a feature that fails spectacularly at providing a true guarantee, as C++ did. The design I suggested does without the actual feature.

I completely agree with this sentiment. In earlier discussions, there were designs that went even further, for example not being able to take the address of or cast from a const parameter, etc. But IMO that sentiment has been subjectively and unevenly applied to different but often requested language requests over the years. Unfortunately the earlier const suggestions were a case where the same sentiment was not applied. IIRC, the majority of the "group" - many of which had a large amount of experience writing libraries with D - strongly favored a 'const' or 'readonly' even though the compiler could not always guarantee they couldn't be modified.
 D 2.0 will have const. Walter wants it as much as the next guy. The 
 challenge is indeed finding something that has a good 
 complexity/performance ratio.
 
 What I like about the template const idea is that it is entirely 
 automatic - the user doesn't have to annotate functions manually as 
 being const or not. The template will extract with precision the part of 
 a type that can be used without mutating the object. (Such a deduction 
 can also be part of a built-in feature.)
 
 To start talking about const, D must first address the inout issue, 
 which is nothing short of a source of radioactive decay that affects 
 negatively random parts of the language, which otherwise has a sound 
 design. Walter acknowledges that there is a problem, but I don't think 
 he and I have the same understanding of its consequences (which I 
 believe are enormous).
 
 Once inout is taken care of, we need to take a deep breath and address 
 the issue of lazy (which is related), and only after that const can 
 enter the debate table.
 
 
 Andrei

Jan 20 2007
parent reply "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
Dave wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
 Casts can break any design, so it's not phasing anyone off that they 
 can break const. Besides, the fact that Const holds a pointer to the 
 object as its first element is not something client code is allowed to 
 rely on. This is a non-issue.

I wish Walter would have agreed with many of us that it (casts breaking any design) is a "non-issue" for going ahead with a 'semantically meaningful const' a year ago. I hope you can convince him!

My statement about design-breaking casts needs qualifying. Walter (and I entirely agree with him) dislikes the fact that C++ actually _legalizes_ const removal via a cast. Consider the following C++ code: void Foo(const int & x) { const_cast<int&>(x) = 0; ) int main() { int x = 1; Foo(x); } This is 100% legal C++ code with defined behavior; casting away const is ok if the initial data was not const. This feature kicks const straight in the proverbials. A "design-breaking" cast is different: the language does not guarantee the behavior of code that uses it, and therefore can act under the assumption that it will not happen. This is key to introducing const in D in a manner that is useful to everybody.
 Both "user-space" and "compiler-space" const has been discussed at 
 length in the past, and Walter's response has always been 
 (paraphrasing) "D won't have const unless it is semantically 
 meaningful (100% enforceable) by the compiler", which will never 
 happen with a language like D.

This statement must be understood in spirit. The spirit is, D shouldn't add a feature that fails spectacularly at providing a true guarantee, as C++ did. The design I suggested does without the actual feature.

I completely agree with this sentiment. In earlier discussions, there were designs that went even further, for example not being able to take the address of or cast from a const parameter, etc. But IMO that sentiment has been subjectively and unevenly applied to different but often requested language requests over the years. Unfortunately the earlier const suggestions were a case where the same sentiment was not applied. IIRC, the majority of the "group" - many of which had a large amount of experience writing libraries with D - strongly favored a 'const' or 'readonly' even though the compiler could not always guarantee they couldn't be modified.

It is then possible they were wrong, or that they didn't come with a reasonable design. Speaking of which (famous last words to come), I don't want to sound harsh, but if there is one thing that's worse than language design by committee, that's got to be language design by community. Having too many cooks in the kitchen dilutes vision, which is probably the single most important thing in a language design. The few places where Walter made populist decisions are eminently visible in the language design (ah, "length", gotta love that one), and many feature requests that have been aired on this newsgroup are in dire need of design. Andrei
Jan 20 2007
next sibling parent reply Dave <Dave_member pathlink.com> writes:
Andrei Alexandrescu (See Website For Email) wrote:
 Dave wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
 Casts can break any design, so it's not phasing anyone off that they 
 can break const. Besides, the fact that Const holds a pointer to the 
 object as its first element is not something client code is allowed 
 to rely on. This is a non-issue.

I wish Walter would have agreed with many of us that it (casts breaking any design) is a "non-issue" for going ahead with a 'semantically meaningful const' a year ago. I hope you can convince him!

My statement about design-breaking casts needs qualifying. Walter (and I entirely agree with him) dislikes the fact that C++ actually _legalizes_ const removal via a cast. Consider the following C++ code: void Foo(const int & x) { const_cast<int&>(x) = 0; ) int main() { int x = 1; Foo(x); } This is 100% legal C++ code with defined behavior; casting away const is ok if the initial data was not const. This feature kicks const straight in the proverbials.

I think the general understanding was that D const would not be C++'s const (with all of the warts like the one you pointed out), but _also_ would need to go further and be a "guarantee".
 A "design-breaking" cast is different: the language does not guarantee 
 the behavior of code that uses it, and therefore can act under the 
 assumption that it will not happen. This is key to introducing const in 
 D in a manner that is useful to everybody.
 

IIRC, Walter has made mention of const truly being 'readonly', even to the point of wishing for some way to enforce it with hardware or OS support. It should be in the archives somewhere. Like I said I hope he can be or already is convinced otherwise since hardware / OS support isn't available, at least on the most popular platforms.
 Both "user-space" and "compiler-space" const has been discussed at 
 length in the past, and Walter's response has always been 
 (paraphrasing) "D won't have const unless it is semantically 
 meaningful (100% enforceable) by the compiler", which will never 
 happen with a language like D.

This statement must be understood in spirit. The spirit is, D shouldn't add a feature that fails spectacularly at providing a true guarantee, as C++ did. The design I suggested does without the actual feature.

I completely agree with this sentiment. In earlier discussions, there were designs that went even further, for example not being able to take the address of or cast from a const parameter, etc. But IMO that sentiment has been subjectively and unevenly applied to different but often requested language requests over the years. Unfortunately the earlier const suggestions were a case where the same sentiment was not applied. IIRC, the majority of the "group" - many of which had a large amount of experience writing libraries with D - strongly favored a 'const' or 'readonly' even though the compiler could not always guarantee they couldn't be modified.

It is then possible they were wrong, or that they didn't come with a reasonable design. Speaking of which (famous last words to come), I don't want to sound harsh, but if there is one thing that's worse than language design by committee, that's got to be language design by community. Having too many cooks in the kitchen dilutes vision, which is probably the single most important thing in a language design. The few places where Walter made populist decisions are eminently visible in the language design (ah, "length", gotta love that one), and many feature requests that have been aired on this newsgroup are in dire need of design.

I should qualify "designs".. There were a few posts that may be considered full-fledged "designs", but most of them were just ideas put forward on const and how to make it generally usable. The problem always came to be that none of them could guarantee what was understood as a prerequisite from Walter - truly read-only.
 
 Andrei

Jan 20 2007
next sibling parent "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
Dave wrote:
 IIRC, Walter has made mention of const truly being 'readonly', even to 
 the point of wishing for some way to enforce it with hardware or OS 
 support. It should be in the archives somewhere. Like I said I hope he 
 can be or already is convinced otherwise since hardware / OS support 
 isn't available, at least on the most popular platforms.

OS support is often available, but in too coarse a manner (page-level read-only) to be used pervasively at language level. C++ compilers of today go as far as massaging all read-only data of an executable in read-only pages, which has been quite effective, yet not applicable at a finer scale. Things have improved with regards to understanding the value of const. It turns out that true read-only is the least interesting of const's benefits, and that 'view-only domains' is a much more powerful paradigm. D will have const. The main issues wrt const are (1) fixing the inout disaster, (2) figuring a semantics for const that works with the current D and devising the appropriate blend of deduction and user-level annotation (C++ has too little of the former and imposes too much of the latter), and (3) figuring a priority of the implementation on D's busy agenda, where there are many much more exciting features than const.
 I should qualify "designs".. There were a few posts that may be 
 considered full-fledged "designs", but most of them were just ideas put 
 forward on const and how to make it generally usable. The problem always 
 came to be that none of them could guarantee what was understood as a 
 prerequisite from Walter - truly read-only.

Or none of them converted that lack of guarantee miraculously into a feature :o). (I'm more serious than I might seem!) Andrei
Jan 20 2007
prev sibling parent reply Walter Bright <newshound digitalmars.com> writes:
Dave wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
 My statement about design-breaking casts needs qualifying. Walter (and 
 I entirely agree with him) dislikes the fact that C++ actually 
 _legalizes_ const removal via a cast. Consider the following C++ code:

 void Foo(const int & x)
 {
   const_cast<int&>(x) = 0;
 )

 int main()
 {
   int x = 1;
   Foo(x);
 }

 This is 100% legal C++ code with defined behavior; casting away const 
 is ok if the initial data was not const. This feature kicks const 
 straight in the proverbials.

const (with all of the warts like the one you pointed out), but _also_ would need to go further and be a "guarantee".

My objections to the C++ const are: 1) there are *legal* ways to subvert it, meaning it is useless as a semantic aid and useless to the optimizer/code generator 2) it confuses a type modifier with a storage class (exhibiting muddled special case behaviors) 3) it's ugly and litters declarations 4) the use of overloading based on const is a workaround to another design flaw in C++ I've stated that I would put a const in if we could find a design without those shortcomings. I also believe D is better off with no const at all rather than a half-assed one.
 A "design-breaking" cast is different: the language does not guarantee 
 the behavior of code that uses it, and therefore can act under the 
 assumption that it will not happen. This is key to introducing const 
 in D in a manner that is useful to everybody.

IIRC, Walter has made mention of const truly being 'readonly', even to the point of wishing for some way to enforce it with hardware or OS support. It should be in the archives somewhere. Like I said I hope he can be or already is convinced otherwise since hardware / OS support isn't available, at least on the most popular platforms.

The idea is that if the compiler did put all the const data into readonly memory, that it would not change the behavior of a conforming program. In other words, it would be undefined behavior to deliberately subvert the const via casting, inline assembler, or pointer manipulation. This is quite different from C++, where (as Andrei pointed out) it is legal, defined behavior to subvert const.
 I should qualify "designs".. There were a few posts that may be 
 considered full-fledged "designs", but most of them were just ideas put 
 forward on const and how to make it generally usable. The problem always 
 came to be that none of them could guarantee what was understood as a 
 prerequisite from Walter - truly read-only.

Designs and proposals are posted every day in this n.g. It's simply not possible to implement them all, or even give coherent replies to them all. But these n.g. discussions are valuable - the lazy parameter and scope guard designs were significantly improved as a direct result. The way import now works is a direct result of the n.g., for just 3 of many examples.
Jan 21 2007
next sibling parent reply "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
Walter Bright wrote:
 My objections to the C++ const are:
 
 1) there are *legal* ways to subvert it, meaning it is useless as a 
 semantic aid and useless to the optimizer/code generator
 2) it confuses a type modifier with a storage class (exhibiting muddled 
 special case behaviors)

Let me point out that D's current handling of the storage classes is broken. D defines the 'inout' and 'out' storage classes which are undetectable for templates and break any template code that needs to deal with them. (Why in the world was 'out' ever needed? Probably the "working with IDL" assumption should be revisited.) Then, to add insult to injury, D defines the 'lazy' storage class that changes the access syntax (something that a storage class is not supposed to do), does not allow assignment from the same type (although it should), and again can't work with any template code.
 3) it's ugly and litters declarations
 4) the use of overloading based on const is a workaround to another 
 design flaw in C++

But right now D is worse off than C++ because in D you can't overload on inout (or detect it in other ways) except for a much contorted way that I recall I've seen in a post around here. So where C++ is simply verbose, D is knee-deep in the mud.
 I've stated that I would put a const in if we could find a design 
 without those shortcomings. I also believe D is better off with no const 
 at all rather than a half-assed one.

Yup. We need a full-assed one :o).
 I should qualify "designs".. There were a few posts that may be 
 considered full-fledged "designs", but most of them were just ideas 
 put forward on const and how to make it generally usable. The problem 
 always came to be that none of them could guarantee what was 
 understood as a prerequisite from Walter - truly read-only.

Designs and proposals are posted every day in this n.g. It's simply not possible to implement them all, or even give coherent replies to them all. But these n.g. discussions are valuable - the lazy parameter and scope guard designs were significantly improved as a direct result. The way import now works is a direct result of the n.g., for just 3 of many examples.

I don't know much about import, am in favor of scope guard's improved syntax (compared to the one I proposed), and I'm positive that lazy is the epitome of bad language design and should go away. The article: http://www.digitalmars.com/d/lazy-evaluation.html proudly acknowledges the post: http://www.digitalmars.com/pnews/read.php?server=news.digitalmars.com&group=digitalmars.D&artnum=41633 which doesn't make any sense of anything, confuses lazy/delegates with expression templates, and fails to provide both a solid argument for 'lazy' and the shade of a valid design for it. The net result is: - lazy is a full-fledged type constructor coming in the disguise of a storage class; - lazy cannot be assigned even from same type, nor from a delegate, which reduces templates' ability to manipulate lazy and non-lazy stuff uniformly; - templates are powerless with lazy, as they are with any other storage class; they are doubly powerless with lazy because they now need to deal with the modified access syntax. I believe that populism in language design is not good. Andrei
Jan 21 2007
next sibling parent reply Frits van Bommel <fvbommel REMwOVExCAPSs.nl> writes:
Andrei Alexandrescu (See Website For Email) wrote:
 Walter Bright wrote:
 My objections to the C++ const are:

 1) there are *legal* ways to subvert it, meaning it is useless as a 
 semantic aid and useless to the optimizer/code generator
 2) it confuses a type modifier with a storage class (exhibiting 
 muddled special case behaviors)

Let me point out that D's current handling of the storage classes is broken. D defines the 'inout' and 'out' storage classes which are undetectable for templates and break any template code that needs to deal with them. (Why in the world was 'out' ever needed? Probably the "working with IDL" assumption should be revisited.) Then, to add insult to injury, D defines the 'lazy' storage class that changes the access syntax (something that a storage class is not supposed to do), does not allow assignment from the same type (although it should), and again can't work with any template code.

"in", "out", "inout" and "lazy" aren't storage classes. http://www.digitalmars.com/d/declaration.html (in the big syntax block at the top): ----- StorageClass: abstract auto const deprecated extern final override scope static synchronized ----- and later: ----- InOut: in out inout lazy ----- They're more like parameter passing conventions: * You can only use one per parameter * You can't declare a global variable to be 'lazy' ;). * You can't declare a parameter to be 'abstract', 'auto', 'const', ... They're pretty distinct concepts. They just look similar because they're used in such similar positions (typically before a type + name), but in two different contexts. I do agree that it's weird the access syntax is changed by a parameter passing convention. By the way, looking at that part of the spec I noticed something weird. I can't find the path that would allow "(int x)" to be a valid parameter list. /Parameter/ has 4 cases. In this case "Declarator" would be the obvious clause (no '=', no in/out). But /Declarator/ doesn't seem to allow 'int' at the start (or any basic type for that matter). It just allows: * type suffixes (*, [], [ /Expression/ ], delegate /Parameters/, function /Parameters), * /Identifier/ (which doesn't include basic types as they're keywords), * parenthesized /Declarator/ * /Identifier/ /DeclaratorSuffixes/ (see /Identifier/ case) * parenthesized /Declarator/ followed by /DeclaratorSuffixes/ (see case before previous) This seems to be an oversight; in /Decl/ the type (/BasicType/) is put before every instance of /Declarators/. Perhaps they were lifted out when auto declarations were added but the /Declarator/ in the parameter list syntax was overlooked?
Jan 21 2007
next sibling parent Kirk McDonald <kirklin.mcdonald gmail.com> writes:
Frits van Bommel wrote:
 By the way, looking at that part of the spec I noticed something weird. 
 I can't find the path that would allow "(int x)" to be a valid parameter 
 list. /Parameter/ has 4 cases. In this case "Declarator" would be the 
 obvious clause (no '=', no in/out). But /Declarator/ doesn't seem to 
 allow 'int' at the start (or any basic type for that matter). It just 
 allows:
 * type suffixes (*, [], [ /Expression/ ], delegate /Parameters/, 
 function /Parameters),
 * /Identifier/ (which doesn't include basic types as they're keywords),
 * parenthesized /Declarator/
 * /Identifier/ /DeclaratorSuffixes/ (see /Identifier/ case)
 * parenthesized /Declarator/ followed by /DeclaratorSuffixes/ (see case 
 before previous)
 
 This seems to be an oversight; in /Decl/ the type (/BasicType/) is put 
 before every instance of /Declarators/. Perhaps they were lifted out 
 when auto declarations were added but the /Declarator/ in the parameter 
 list syntax was overlooked?

I posted something a little while ago on d.D.bugs regarding the spec's grammar missing some things: http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D.bugs&article_id=9962 -- Kirk McDonald Pyd: Wrapping Python with D http://pyd.dsource.org
Jan 21 2007
prev sibling parent "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
Frits van Bommel wrote:
 "in", "out", "inout" and "lazy" aren't storage classes.
 http://www.digitalmars.com/d/declaration.html (in the big syntax block 
 at the top):
 -----
 StorageClass:
         abstract
         auto
         const
         deprecated
         extern
         final
         override
         scope
         static
         synchronized
 -----
 and later:
 -----
 InOut:
         in
         out
         inout
         lazy
 -----

This is more proof that it's highly unclear what they are. The article on lazy clearly says lazy is a storage class. Walter routinely refers to inout as to a storage class. The good news is that they will all be integrated: their semantics clearly defined, as will be the semantics of all legal combinations thereof (e.g. lazy inout scope). (Many combinations will be disallowed on practical grounds as being useless, e.g. "in out".) Andrei
Jan 21 2007
prev sibling parent reply Tom S <h3r3tic remove.mat.uni.torun.pl> writes:
Andrei Alexandrescu (See Website For Email) wrote:
 I'm positive that lazy is 
 the epitome of bad language design and should go away. The article:
 
 http://www.digitalmars.com/d/lazy-evaluation.html
 
 proudly acknowledges the post:
 
 http://www.digitalmars.com/pnews/read.php?server=news.digitalmars.com&group=digita
mars.D&artnum=41633 
 
 
 which doesn't make any sense of anything,

Well, thanks for the attack, but it was an attempt to suggest a fix, as the introduction of lazy evaluation broke existing code in a non-trivial way.
 confuses lazy/delegates with expression templates,

For a good reason. If I remember correctly, Walter compared lazy evaluation to expression templates in his earlier posts.
 and fails to provide both a solid argument for 'lazy'

The arguments popped up in various discussions on the NG and #d on IRC.
 and the shade of a valid design for it.

It was a suggestion for one possible way to avoid breaking existing code - not a final and complete proposal. I was hoping for a discussion to follow.
 - templates are powerless with lazy, as they are with any other storage 
 class; they are doubly powerless with lazy because they now need to deal 
 with the modified access syntax.

Actually, inout/out can be detected by templates and std.bind does it. Still, inout cannot be distinguished from out.
 I believe that populism in language design is not good.

Listening to ideas and opinions from language users is a bad idea ? Taking my post as an example, it was merely a compromise between the screams of other D users and Walter's opinion. Had Walter ignored it, the net result would be more confusion and irritation, along with a strange rule of implicitly converting lazy expressions to delegates. This said, I don't think that the way lazy expressions are currently implemented is even close to being optimal. out/inout could be changed too, as out/inout params can be detected, but one cannot realistically instantiate a function template with mixed in/out/inout params. -- Tomasz Stachowiak
Jan 21 2007
parent reply "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
Tom S wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
 I'm positive that lazy is the epitome of bad language design and 
 should go away. The article:

 http://www.digitalmars.com/d/lazy-evaluation.html

 proudly acknowledges the post:

 http://www.digitalmars.com/pnews/read.php?server=news.digitalmars.com&group=digita
mars.D&artnum=41633 


 which doesn't make any sense of anything,

Well, thanks for the attack, but it was an attempt to suggest a fix, as the introduction of lazy evaluation broke existing code in a non-trivial way.

You are not being attacked. Not even the post was attacked, but rather the haste with which a feature was implemented before a good design was being put forth. [snip]
 I believe that populism in language design is not good.

Listening to ideas and opinions from language users is a bad idea ? Taking my post as an example, it was merely a compromise between the screams of other D users and Walter's opinion. Had Walter ignored it, the net result would be more confusion and irritation, along with a strange rule of implicitly converting lazy expressions to delegates.

I think the current state of affairs is stranger than that implicit conversion rule. It will now take additional effort, and a certain amount of code breakage, to fix the design. In an ideal world, things would have gone through a more critical review before just making it into the language.
 This said, I don't think that the way lazy expressions are currently 
 implemented is even close to being optimal. out/inout could be changed 
 too, as out/inout params can be detected, but one cannot realistically 
 instantiate a function template with mixed in/out/inout params.

There is an ongoing idea of binding an expression to an alias, which probably will do a good job (better opportunities for inlining). As far as in/out/inout (and actually lazy/scope/const) go, Walter will take up to fixing the issue and has a design for distinguishing among them properly. All of these keywords will be raised to the status of type constructors, and the constructed types will have well-defined semantics. Combinations (e.g. lazy inout) will be allowed when the semantics allow it. Anyhow, speaking of a more critical review, the current intended rules for lazy are: * lazy is a type constructor (takes a type, returns a type) * sizeof(lazy T) = sizeof(T delegate()) * initialization = expression of type T or expression of type lazy T * assignment: expression of type lazy T * access: every access invokes the delegate. No trailing parens. * deduced automatically: never * can occur in: (a) function argument list; (b) function return type; (c) all lvalue contexts (namespace-level, local, struct member, class member, array, hash) One irregularity is that assignment from an expression of type T is not accepted, amid fears that this would cause confusion. Probably we could drop this irregularity. Looking forward to feedback! Andrei
Jan 21 2007
parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
Andrei Alexandrescu (See Website For Email) wrote:

 Anyhow, speaking of a more critical review, the current intended rules 
 for lazy are:

Does the lack of syntax mean that the syntax has yet to be decided upon?
 * lazy is a type constructor (takes a type, returns a type)

Cool that seems like an interesting idea for this and const.
 * sizeof(lazy T) = sizeof(T delegate())
 
 * initialization = expression of type T or expression of type lazy T
 
 * assignment: expression of type lazy T
 
 * access: every access invokes the delegate. No trailing parens.
 
 * deduced automatically: never

What does this mean, exactly? Is it speaking of a template parameter? Or for things like 'auto foo = <expr>'?
 * can occur in: (a) function argument list; (b) function return type; 
 (c) all lvalue contexts (namespace-level, local, struct member, class 
 member, array, hash)

 One irregularity is that assignment from an expression of type T is not 
 accepted, amid fears that this would cause confusion. Probably we could 
 drop this irregularity.

So this is ok: lazy int a = 3+4; And this is ok: lazy int b = 3+4; lazy int a; a = b+2; But this is not? lazy int a; a = 3+4; Don't really have any criticisms of this at the moment, just trying to figure out exactly what it's going to mean in practice. So far what I'm getting is that it's similar to the current 'lazy' just generalized to apply to more situations, and that now lazy is part of the type rather than being a storage class. Will there be any additional interop with delegates? What about assigning a delegate to a lazy variable? --bb
Jan 21 2007
parent "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
Bill Baxter wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
 
 Anyhow, speaking of a more critical review, the current intended rules 
 for lazy are:

Does the lack of syntax mean that the syntax has yet to be decided upon?

The syntax was described and is the same as using regular lvalues.
 * deduced automatically: never

What does this mean, exactly? Is it speaking of a template parameter? Or for things like 'auto foo = <expr>'?

Both. And also for future deduction of storage classes by template functions. Basically the compiler assumes eager evaluation by default.
 * can occur in: (a) function argument list; (b) function return type; 
 (c) all lvalue contexts (namespace-level, local, struct member, class 
 member, array, hash)

 One irregularity is that assignment from an expression of type T is 
 not accepted, amid fears that this would cause confusion. Probably we 
 could drop this irregularity.

So this is ok: lazy int a = 3+4; And this is ok: lazy int b = 3+4; lazy int a; a = b+2; But this is not? lazy int a; a = 3+4;

That is the plan. Although probably it's a mistake.
 Don't really have any criticisms of this at the moment, just trying to 
 figure out exactly what it's going to mean in practice.
 So far what I'm getting is that it's similar to the current 'lazy' just 
 generalized to apply to more situations, and that now lazy is part of 
 the type rather than being a storage class.
 
 Will there be any additional interop with delegates?  What about 
 assigning a delegate to a lazy variable?

That's a good idea. Frankly I am not thrilled by lazy because there is too much overlap with delegates to be worth any effort (and your idea takes that overlap to the extreme). After thinking more about it, I think lazy should go and automatic conversion from expressions to aliases should replace it as a more general and efficient alternative. Figuring out the vagaries of each storage class and all combinations thereof is just a lot of effort from the creator of the language, the compiler implementor, and the programmer who later learns the language. It's even more demotivating to see that a storage class is just white sugar for a feature that could, and probably will, be implemented in a more principled way. I honestly think lazy should go. It's a waste of time. Andrei
Jan 22 2007
prev sibling parent reply Miles <_______ _______.____> writes:
Walter Bright wrote:
 1) there are *legal* ways to subvert it,

You mean, const_cast<> and other casts? If so, this is no reason to say D shouldn't have similar implementation. You can implement it in D but without allowing any way of removing const-ness from the object reference. I never used const_cast<> or any other way of removing const-ness from an object in 8 years of C++ coding. If you mean subverting it by changing the object by its original reference, or by a different non-const reference, this is another problem, that doesn't invalidate const itself. Const applies to a specific reference to an object, not the object itself. If you mean by using mutables, I see no problem here also. Mutable is not meant to be used for any attribute, just for caches and other specific attributes that doesn't affect the object real state. If you are afraid of programmers misusing const, by subverting it... well... programmers also misuse operator overload, templates, delegates, etc... what doesn't make these features wrong.
 meaning it is useless as a semantic aid

No, it is not. const is a contract that says that the function will not change the object, at least not through *that specific reference*. This does not mean that the object won't be changed during the function call.
 and useless to the optimizer/code generator

No solution will ever be useful to the optimizer or the code generator. There is no way to guarantee that the state of any referenced object will be constant, unless you kill thread support and restrict so much what a function can do that it becomes an annoyance. Anyway, const doesn't mean optimization, it is just a form of contract. By the way, are you aware of the new 'restrict' keyword of C99? It addresses exactly the optimization facet that 'const' doesn't; for function parameters only.
 2) it confuses a type modifier with a storage class (exhibiting muddled
 special case behaviors)

Does it? I think this is an implementation issue. I think that in a good const implementation (not exactly compatible with C++) the const variant of a type should be the same as the non-const variant, just with a special "property" that restricts access, and not a completely different type. This means that class X { X func(Y a); const X func(const Y a) const; }; would generate a compile-time error, since both member functions are identical once you make const a reference property, and not a different type.
 3) it's ugly and litters declarations

Sorry, but it is your personal opinion. This doesn't make const bad.
 4) the use of overloading based on const is a workaround to another
 design flaw in C++

If you mean like the above case, yeah, sure. This does not mean that this problem have no solution. I foresee a very simple solution like the covariant return type already implemented in D. It is not that difficult. There are three cases for returning const types: (1) the member function should always return a const reference, (2) the member function should always return a non-const reference (like a copy operator), and (3) the member function should return a self-reference, respecting the const-ness of the object reference it was called from. For (1), we may declare: class X { const X getConstInstanceOfX(); // always returns a const instance, to // self or to something else } For (2): class X { X copy(); // always returns a new, non-const // instance of X } For (3), we would need another new keyword or mechanism to mean "conditional constness depending on 'this'". Some suggestions are 'volatile const', or 'scope const' (overloading some already-existing keywords), or something like 'condconst', or something else. class X { condconst X opCall() const; // returns a self-reference, function // shouldn't modify object since it may // be called as const } So, any case in which in C++ you would write a pair of identical functions overloaded by const, you would use this third case in D.
Jan 23 2007
parent reply "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
Miles wrote:
 Walter Bright wrote:

 2) it confuses a type modifier with a storage class (exhibiting muddled
 special case behaviors)

Does it? I think this is an implementation issue.

Wrong. It is a definitional issue that confuses even experts.
 I think that in a good const implementation (not exactly compatible with
 C++) the const variant of a type should be the same as the non-const
 variant, just with a special "property" that restricts access, and not a
 completely different type.
 
 This means that
 
 	class X {
 		X func(Y a);
 		const X func(const Y a) const;
 	};
 
 would generate a compile-time error, since both member functions are
 identical once you make const a reference property, and not a different
 type.

That's pretty limiting; you can't define something a la STL begin() that returns different objects depending on the object's mutability.
 3) it's ugly and litters declarations

Sorry, but it is your personal opinion. This doesn't make const bad.

Littering declarations is objective. In C++ const is never deduced, only checked. The underlying vision is that there should be cases in which constness is deduced such that programmers can benefit of it without having to babysit the compiler.
 4) the use of overloading based on const is a workaround to another
 design flaw in C++

If you mean like the above case, yeah, sure. This does not mean that this problem have no solution. I foresee a very simple solution like the covariant return type already implemented in D. It is not that difficult. There are three cases for returning const types: (1) the member function should always return a const reference, (2) the member function should always return a non-const reference (like a copy operator), and (3) the member function should return a self-reference, respecting the const-ness of the object reference it was called from.

Sorry, this is simplistic and incomplete. A member function might want to return whatever, including two unrelated types depending on constness. Andrei
Jan 23 2007
parent reply Miles <_______ _______.____> writes:
Andrei Alexandrescu (See Website For Email) wrote:
 That's pretty limiting; you can't define something a la STL begin() that
 returns different objects depending on the object's mutability.

This is almost prejudice against const. You broke my message in this point just to argue against it, without considering the explanation that follows. If you think just a little about the solution, you will see that the solution cames immediately. iterator<> and const_iterator<> will became the exact same class, with no distinction between const and non-const.
 Littering declarations is objective. In C++ const is never deduced, only
 checked. The underlying vision is that there should be cases in which
 constness is deduced such that programmers can benefit of it without
 having to babysit the compiler.

I don't share your vision. Doesn't make any sense the compiler deduce const. So I write a get() method that should only return a value from the object, but I inadvertently replace a == with a =, or something alike, and the compiler deduces this is a non-const method, instead of failing to compile it. So wrong... A const member function is not "babysitting" the compiler, it is the way the programmer have to tell the compiler that that function is not intended to modify the object, so the compiler can aid disallowing any operation that would affect the object state. Also, it is a documentation tool.
 Sorry, this is simplistic and incomplete.
 A member function might want to return whatever,

If the same named member function should return different objects, then you should seriously think renaming them to different names, since they don't do the same thing.
 including two unrelated types depending on constness.

The only reason I see for returning two "unrelated" (?) types depending on constness is in situations like STL begin(), and in this specific situation, there is simply no reason for having two different types like iterator<> and const_iterator<>. If you have read my message, this would have came immediately. The conditional const I proposed aids just this.
Jan 24 2007
parent "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
Miles wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
 That's pretty limiting; you can't define something a la STL begin() that
 returns different objects depending on the object's mutability.

This is almost prejudice against const. You broke my message in this point just to argue against it, without considering the explanation that follows. If you think just a little about the solution, you will see that the solution cames immediately. iterator<> and const_iterator<> will became the exact same class, with no distinction between const and non-const.

I broke the message because it was long, having some content that I agree with (as in "true but uninteresting"). If you get to work out on an actual design for your idea, you will just see how it does not work.
 Littering declarations is objective. In C++ const is never deduced, only
 checked. The underlying vision is that there should be cases in which
 constness is deduced such that programmers can benefit of it without
 having to babysit the compiler.

I don't share your vision. Doesn't make any sense the compiler deduce const.

Well this is simply wrong, and fortunately there's no need for an explanation because there is plenty of previous work on qualifier inference (http://www.cs.umd.edu/~jfoster/papers/pldi99.pdf and Faehndrich follow-up work). Walter and I already discussed a number of workable instances of const deduction. Andrei
Jan 24 2007
prev sibling parent janderson <askme me.com> writes:
Andrei Alexandrescu (See Website For Email) wrote:
 Dave wrote:
 
 It is then possible they were wrong, or that they didn't come with a 
 reasonable design. Speaking of which (famous last words to come), I 
 don't want to sound harsh, but if there is one thing that's worse than 
 language design by committee, that's got to be language design by 
 community. Having too many cooks in the kitchen dilutes vision, which is 
 probably the single most important thing in a language design. 

I couldn't agree more on this point. I'm glad we have someone like Walter at the helm with a clear vision and an end goal. I've been on a few design by committee projects and you can never get everyone to agree. Projects would get to a point and then someone would who didn't like one direction would lose enthusiasm and then we were back to square one. Someone needs to say, this doesn't fit into my overall picture and this is the way we should go forth. Projects work best when there is a clear direction. Having said that, I've been in groups where everyone was assigned a particular piece of the puzzle to be the lead on. That seemed to work well.
 The few
 places where Walter made populist decisions are eminently visible in the 
 language design (ah, "length", gotta love that one), and many feature 
 requests that have been aired on this newsgroup are in dire need of design.

Here I don't agree, Walter (as much as we would like to believe) doesn't have the experience of the entire community collective. He is wise to listen to others point of view. He is even more wise to be willing to change his point of view, if a good enough argument can be put forth. It's a very diplomatic way of doing things. -Joel
Jan 21 2007
prev sibling next sibling parent reply "Jarrett Billingsley" <kb3ctd2 yahoo.com> writes:
"Andrei Alexandrescu (See Website for Email)" 
<SeeWebsiteForEmail erdani.org> wrote in message 
news:eotmt2$2fru$3 digitaldaemon.com...
 A minor drawback is that Const!(Foo) must be implicitly constructible
 from a Foo, but another in-design language feature (opImplicitCast) will
 take care of that.

So... D is now becoming C++? Are there going to be globally overloadable operators next? And how did you get your hands on this juicy tidbit of info anyway?
Jan 20 2007
parent reply "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
Jarrett Billingsley wrote:
 "Andrei Alexandrescu (See Website for Email)" 
 <SeeWebsiteForEmail erdani.org> wrote in message 
 news:eotmt2$2fru$3 digitaldaemon.com...
 A minor drawback is that Const!(Foo) must be implicitly constructible
 from a Foo, but another in-design language feature (opImplicitCast) will
 take care of that.

So... D is now becoming C++? Are there going to be globally overloadable operators next?

It's all too simple to protest against various features that other languages have bastardized and throw the all too simplistic "So what, D is now becoming Xyz?" as an argument in the discussion.
 And how did you get your hands on this juicy tidbit of info anyway?

Call it collocation: Walter and I live in the same city :o). Andrei
Jan 20 2007
parent reply "Jarrett Billingsley" <kb3ctd2 yahoo.com> writes:
"Andrei Alexandrescu (See Website For Email)" 
<SeeWebsiteForEmail erdani.org> wrote in message 
news:45B2A3AE.20408 erdani.org...
 It's all too simple to protest against various features that other 
 languages have bastardized and throw the all too simplistic "So what, D is 
 now becoming Xyz?" as an argument in the discussion.

Well how long has Walter been anti-implicit casting? It just seems like he's been "no overloading opCast for more than one type" and "implicit casting and construction are bad things" and "opAssign is the devil" for so long, and then just recently he seems to have had a change of mind.. what happened?
 Call it collocation: Walter and I live in the same city :o).

Weeeell foo. Maybe you guys should tape your coffee shop rendezvous..es (or however you meet) and post them online ;)
Jan 20 2007
parent "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
Jarrett Billingsley wrote:
 "Andrei Alexandrescu (See Website For Email)" 
 <SeeWebsiteForEmail erdani.org> wrote in message 
 news:45B2A3AE.20408 erdani.org...
 It's all too simple to protest against various features that other 
 languages have bastardized and throw the all too simplistic "So what, D is 
 now becoming Xyz?" as an argument in the discussion.

Well how long has Walter been anti-implicit casting? It just seems like he's been "no overloading opCast for more than one type" and "implicit casting and construction are bad things" and "opAssign is the devil" for so long, and then just recently he seems to have had a change of mind.. what happened?

It happens to us all to associate some bad consequences with some wholesale cause without investigating further. Many of the perceptions above are based on various degrees to which C++ embarked on implementing them and partially failed because an understandable lack of clear foresight. To exemplify: * "no overloading opCast for more than one type" and "implicit casting and construction are bad things" came from C++ having made implicit conversion the default one (and the only possible one when converting to built-ins), thus making implicit conversions springing all over the place in expressions. To assuage that, C++ also added the bizarre rule that maximum one user-defined conversion can act in one expression. This, coupled with the byzantine overloading rules of C++, contributed to a general perception that implicit conversion is a bad idea. However, numerous use cases show that implicit conversions are expressive as long as they are the result of a conscious decision. Besides, the core does support implicit conversions, and I think history of programming languages has shown copiously that the dictum "Quod licet Iovis, non licet bovis" doesn't apply to language design. * "opAssign is the devil" stems from a simple confusion - that assignment and copy construction are strongly related. They aren't, and as soon as this was clarified, opAssign made its way into the language. Copy construction suffers of an infinite regression problem that C++ avoided, arguably in quite a disingenious way that has caused much grief, to be assuaged by the upcoming rvalue references (which come with the unneeded complexity specific to afterthought designs). I fear that solving inout for D will have a similar stench. Andrei
Jan 20 2007
prev sibling next sibling parent reply "Lionello Lunesu" <lionello lunesu.remove.com> writes:
"Andrei Alexandrescu (See Website for Email)" 
<SeeWebsiteForEmail erdani.org> wrote in message 
news:eotmt2$2fru$3 digitaldaemon.com...
 The reflection mechanism would have to provide the information whether
 or not a given member function changes the object.

How would that work? The difference between getters and setters is probably easy to see, but what about member functions calling non-member functions? Is the signature of that function (in,out,inout) enough to determine mutability? Sounds like a C++-style const afterall. And how about the nested classes, or array members? I very much like the idea though: generating getters with the same name as the members, but that alone is not enough. I get very confused thinking about this : S L.
Jan 21 2007
parent reply "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
Lionello Lunesu wrote:
 "Andrei Alexandrescu (See Website for Email)" 
 <SeeWebsiteForEmail erdani.org> wrote in message 
 news:eotmt2$2fru$3 digitaldaemon.com...
 The reflection mechanism would have to provide the information whether
 or not a given member function changes the object.

How would that work? The difference between getters and setters is probably easy to see, but what about member functions calling non-member functions? Is the signature of that function (in,out,inout) enough to determine mutability? Sounds like a C++-style const afterall.

Except that it's deduced. Think of it the other way: if const is an explicit Boolean flag that the compiler can check, it makes sense to have the compiler simply infer it.
 And how about the nested classes, or array members? I very much like the 
 idea though: generating getters with the same name as the members, but that 
 alone is not enough.
 
 I get very confused thinking about this : S

Me too. I actually thought lazy is a storage class. It sure has the syntax of one in definition. It changes the use syntax (requires parens). And if I look at http://www.digitalmars.com/d/lazy-evaluation.html I see that lazy *is* a storage class. My original suggestion was to simply convert expressions to delegates automatically. That setup has its own problems, but I think it's much easier to deal with them than it is with lazy. A template can't detect lazy properly. It's unclear what it means to assign to a lazy parameter. Lazy is a storage class that changes the access syntax. I believe lazy is ill-designed. Walter should have stood the pressure and refuse to implement it without a clear definition. Andrei
Jan 21 2007
next sibling parent "Lionello Lunesu" <lionello lunesu.remove.com> writes:
"Andrei Alexandrescu (See Website For Email)" 
<SeeWebsiteForEmail erdani.org> wrote in message 
news:45B3AB3F.5040707 erdani.org...
 Me too. I actually thought lazy is a storage class. It sure has the syntax 
 of one in definition. It changes the use syntax (requires parens). And if 
 I look at

 http://www.digitalmars.com/d/lazy-evaluation.html

 I see that lazy *is* a storage class.

 My original suggestion was to simply convert expressions to delegates 
 automatically. That setup has its own problems, but I think it's much 
 easier to deal with them than it is with lazy.

 A template can't detect lazy properly. It's unclear what it means to 
 assign to a lazy parameter. Lazy is a storage class that changes the 
 access syntax. I believe lazy is ill-designed. Walter should have stood 
 the pressure and refuse to implement it without a clear definition.

I'm not sure how we got from 'const' to 'lazy' :) but let me just say that I think there's a lot of potential for lazy evaluation and I was very exited when Walter added the implicit conversion from expressions to delegates. I'm not sure what the problem with the keyword "lazy" is though (other than maybe its grammar). At the moment, I think of the keyword "lazy" as a shorter way to declare a delegate. A type saver, much like "length", if you will : D Seriously though, I remember the last time I was excited about something similar. It was in the computer architecture class at the university and we were presented some NEC data flow processor. It was a CPU without an instruction pointer. Just packages of data with code attached. It was quite eliberating to see a totally different way of doing things. I think delegates/lazy-evaluation are a big step towards this kind of flow-based programming. http://en.wikipedia.org/wiki/Flow-based_programming L.
Jan 21 2007
prev sibling parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
Andrei Alexandrescu (See Website For Email) wrote:

 A template can't detect lazy properly. It's unclear what it means to 
 assign to a lazy parameter. Lazy is a storage class that changes the 
 access syntax. I believe lazy is ill-designed. Walter should have stood 
 the pressure and refuse to implement it without a clear definition.

Well, the pressure, if any, was from you, the way I recall it. There wasn't any kind of community upswell around that one. The way I remember it was that 'lazy' just suddenly appeared out of the blue in D 0.165 (http://www.digitalmars.com/d/changelog.html#new0165 -- the 'expressions to delegates' bit), with Walter saying essentially "this is going to change the game, trust me! Andrei convinced me!" But maybe I'm remembering it wrong. Either way, pressure is pressure, and perhaps Walter should have withstood it regardless of where it was coming from... I just don't think you can pin any ill results in this case on "the community". Just trying to set the record straight here. :-) --bb
Jan 21 2007
parent reply "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
Bill Baxter wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
 
 A template can't detect lazy properly. It's unclear what it means to 
 assign to a lazy parameter. Lazy is a storage class that changes the 
 access syntax. I believe lazy is ill-designed. Walter should have 
 stood the pressure and refuse to implement it without a clear definition.

Well, the pressure, if any, was from you, the way I recall it. There wasn't any kind of community upswell around that one. The way I remember it was that 'lazy' just suddenly appeared out of the blue in D 0.165 (http://www.digitalmars.com/d/changelog.html#new0165 -- the 'expressions to delegates' bit), with Walter saying essentially "this is going to change the game, trust me! Andrei convinced me!" But maybe I'm remembering it wrong. Either way, pressure is pressure, and perhaps Walter should have withstood it regardless of where it was coming from... I just don't think you can pin any ill results in this case on "the community". Just trying to set the record straight here. :-)

The feature I suggested was conversion from expression to delegate, which has a couple of problems, but nothing earth-shattering. Then somehow lazy materialized as a storage class out of the blue, but with the delegate syntax upon use, which is not a horse nor a donkey. Walter and I have worked carefully through inout, lazy, and const today and managed to figure a semantics that makes sense for all storage classes and combinations thereof. (The () use syntax of lazy will go away.) Andrei
Jan 21 2007
parent Bill Baxter <dnewsgroup billbaxter.com> writes:
Andrei Alexandrescu (See Website For Email) wrote:
 Bill Baxter wrote:
 Andrei Alexandrescu (See Website For Email) wrote:

 A template can't detect lazy properly. It's unclear what it means to 
 assign to a lazy parameter. Lazy is a storage class that changes the 
 access syntax. I believe lazy is ill-designed. Walter should have 
 stood the pressure and refuse to implement it without a clear 
 definition.

Well, the pressure, if any, was from you, the way I recall it. There wasn't any kind of community upswell around that one. The way I remember it was that 'lazy' just suddenly appeared out of the blue in D 0.165 (http://www.digitalmars.com/d/changelog.html#new0165 -- the 'expressions to delegates' bit), with Walter saying essentially "this is going to change the game, trust me! Andrei convinced me!" But maybe I'm remembering it wrong. Either way, pressure is pressure, and perhaps Walter should have withstood it regardless of where it was coming from... I just don't think you can pin any ill results in this case on "the community". Just trying to set the record straight here. :-)

The feature I suggested was conversion from expression to delegate, which has a couple of problems, but nothing earth-shattering. Then somehow lazy materialized as a storage class out of the blue, but with the delegate syntax upon use, which is not a horse nor a donkey. Walter and I have worked carefully through inout, lazy, and const today and managed to figure a semantics that makes sense for all storage classes and combinations thereof. (The () use syntax of lazy will go away.) Andrei

Great. Sounds promising. Looking forward to hearing the details. --bb
Jan 21 2007
prev sibling parent reply Frits van Bommel <fvbommel REMwOVExCAPSs.nl> writes:
Andrei Alexandrescu (See Website for Email) wrote:
 The reflection mechanism would have to provide the information whether
 or not a given member function changes the object.
 
 The only drawback that I can think right now is that the compiler can't
 exploit this kind of constness with ease to generate better code; it's a
 "user-space" implementation with semantics that are hard to figure out
 at the compiler level.
 
 A minor drawback is that Const!(Foo) must be implicitly constructible
 from a Foo, but another in-design language feature (opImplicitCast) will
 take care of that.

Correct me if I'm wrong, but I think none of this stuff will work for methods that are only declared instead of defined as well; 'const', implemented like this, wouldn't work for "header modules"... For instance: ----- struct Foo { private int data; char[] bar(); } ----- When implemented like you suggest, I don't see any way that Foo.bar() can possibly be determined to not change the struct. So will Const!(Foo).bar exist? If not (to be on the safe side) that would seem to limit the use of const for something like a closed-source library. If it *does* exist, that would easily (and perhaps accidentally) circumvent the meaning of const. I also don't see a way this can work for classes (in general). There's no way to limit a non-final method to non-mutating operations that I'm aware of, so it could easily be circumvented by subclassing. Even though that would only allow direct access to non-private fields, mutating base class methods could still be called for the rest (if available). So then the only way to get a const version of a class would be if the class doesn't allow mutations anyway (like java's String). Which leaves us right back where we started :(. And I don't think a 'const' implementation should work for structs and not for classes...
Jan 21 2007
parent reply "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
Frits van Bommel wrote:
 Andrei Alexandrescu (See Website for Email) wrote:
 The reflection mechanism would have to provide the information whether
 or not a given member function changes the object.

 The only drawback that I can think right now is that the compiler can't
 exploit this kind of constness with ease to generate better code; it's a
 "user-space" implementation with semantics that are hard to figure out
 at the compiler level.

 A minor drawback is that Const!(Foo) must be implicitly constructible
 from a Foo, but another in-design language feature (opImplicitCast) will
 take care of that.

Correct me if I'm wrong, but I think none of this stuff will work for methods that are only declared instead of defined as well; 'const', implemented like this, wouldn't work for "header modules"...

That is correct. Interface functions, nonfinal methods, and declared-only functions must be annotated manually. The same holds for things like "pure". Hopefully they are only a fraction of the entire const use, such that the burden of the programmer is reduced. Andrei
Jan 21 2007
parent reply Frits van Bommel <fvbommel REMwOVExCAPSs.nl> writes:
Andrei Alexandrescu (See Website For Email) wrote:
 Frits van Bommel wrote:
 Andrei Alexandrescu (See Website for Email) wrote:
 The reflection mechanism would have to provide the information whether
 or not a given member function changes the object.

 The only drawback that I can think right now is that the compiler can't
 exploit this kind of constness with ease to generate better code; it's a
 "user-space" implementation with semantics that are hard to figure out
 at the compiler level.

 A minor drawback is that Const!(Foo) must be implicitly constructible
 from a Foo, but another in-design language feature (opImplicitCast) will
 take care of that.

Correct me if I'm wrong, but I think none of this stuff will work for methods that are only declared instead of defined as well; 'const', implemented like this, wouldn't work for "header modules"...

That is correct. Interface functions, nonfinal methods, and declared-only functions must be annotated manually. The same holds for things like "pure". Hopefully they are only a fraction of the entire const use, such that the burden of the programmer is reduced.

If explicit annotation is possible then this could work for this case. Especially if "dmd -H' automatically added these annotations where appropriate. You didn't respond to my paragraph on classes though: Frits van Bommel wrote:
 I also don't see a way this can work for classes (in general). There's
 no way to limit a non-final method to non-mutating operations that I'm
 aware of, so it could easily be circumvented by subclassing. Even though
 that would only allow direct access to non-private fields, mutating base
 class methods could still be called for the rest (if available). So then
 the only way to get a const version of a class would be if the class
 doesn't allow mutations anyway (like java's String). Which leaves us
 right back where we started :(.

 And I don't think a 'const' implementation should work for structs and
 not for classes...

Jan 21 2007
parent reply "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
Frits van Bommel wrote:
 You didn't respond to my paragraph on classes though:
 
 Frits van Bommel wrote:
  > I also don't see a way this can work for classes (in general). There's
  > no way to limit a non-final method to non-mutating operations that I'm
  > aware of, so it could easily be circumvented by subclassing. Even though
  > that would only allow direct access to non-private fields, mutating base
  > class methods could still be called for the rest (if available). So then
  > the only way to get a const version of a class would be if the class
  > doesn't allow mutations anyway (like java's String). Which leaves us
  > right back where we started :(.
  >
  > And I don't think a 'const' implementation should work for structs and
  > not for classes...

I did when I wrote:
 That is correct. Interface functions, nonfinal methods, and
 declared-only functions must be annotated manually.


I meant - yes, you are right. Today, Walter and myself have come with a semantics for const (as in, "read-only view" that) seems to be a good starting point. The details are yet to be ironed out, but here's the basic plot: * const is a unary type constructor (takes a type, returns a type) * const is always transitive, meaning that once a value is const, the entire part of the world accessible through that value is const as well. This distances D from C++'s shallow view of const (only one level). * const is not removable legally via a cast. But the compiler must assume in the general case that a const view may alias with a modifiable view. This is a weak point of the design. The hope is that in most cases the compiler will easily prove non-aliasing and can do a good job at optimizing based on const. Example: all pure functions don't care about aliasing to start with. * Shallow const is achievable via final. Final only means a value is not supposed to be changed, but anything reachable through it can be mutated. * The syntax for manually-annotated const is: struct Widget { void Foo(const)(int i) { ... } } This is in the spirit of current D, because Foo can be seen as a specialization for a subset of Widget types. * If you do want to make Foo a template, it's: struct Widget { void Foo(const, T)(T i) { ... } } * D avoids the issue of duplicated function bodies in the following way: struct Widget { storageof(this) int* Foo(storageof(this))(int i) { ... } } This code transports whatever storage 'this' has to the result type. More involved type computations are allowed because inside Foo, typeof(this) includes the storage class. Effectively Foo above is a template. * Constructors and destructors can figure the storage class of the object being constructed. This is useful for selecting different allocation strategies for immutable vs. mutable objects: class Widget { this(int i) { ... } this(const)(int i) { ... } ~this() { ... } ~this(const)() { ... } } Const cdtors can obviously mutate the object being cdted. In a cdtor, 'const' is not enforced -- it's just an information for the programmer. * Walter sustains that given the above, there will never be a need to overload member functions based on const. I disagree, but I couldn't come up with a salient example. Besides, I don't care that much because the semantics above do allow telling const from nonconst, just in a roundabout way. Andrei
Jan 21 2007
next sibling parent Walter Bright <newshound digitalmars.com> writes:
Andrei Alexandrescu (See Website For Email) wrote:
 Today, Walter and myself have come with a semantics for const (as in, 
 "read-only view" that) seems to be a good starting point.

Five hours of coffee and scribbling <g>. Neither of us is thoroughly happy with the result, but at least we do have that good starting point.
 * const is not removable legally via a cast. But the compiler must 
 assume in the general case that a const view may alias with a modifiable 
 view. This is a weak point of the design. The hope is that in most cases 
 the compiler will easily prove non-aliasing and can do a good job at 
 optimizing based on const. Example: all pure functions don't care about 
 aliasing to start with.

An important clarification on that: the compiler *can* assume that the 'shallow const' portion (defined below) will not change. There's no way the compiler can guarantee it won't change (for various technical reasons), so it will be up to the user to ensure that no other thread or alias changes it for the duration of the scope. I'll also argue that the C++ requirement that the compiler assume it will change offers no usable advantage, it just sabotages optimizations.
 * Shallow const is achievable via final. Final only means a value is not 
 supposed to be changed, but anything reachable through it can be mutated.
 
 * The syntax for manually-annotated const is:
 
 struct Widget
 {
   void Foo(const)(int i) { ... }
 }

In C++ this is analogous to: struct Widget { void Foo(int i) const { ... } } I never liked the C++ syntax for attributing the 'this' pointer.
 
 This is in the spirit of current D, because Foo can be seen as a 
 specialization for a subset of Widget types.
 
 * If you do want to make Foo a template, it's:
 
 struct Widget
 {
   void Foo(const, T)(T i) { ... }
 }
 
 * D avoids the issue of duplicated function bodies in the following way:
 
 struct Widget
 {
   storageof(this) int* Foo(storageof(this))(int i) { ... }
 }
 
 This code transports whatever storage 'this' has to the result type. 
 More involved type computations are allowed because inside Foo, 
 typeof(this) includes the storage class. Effectively Foo above is a 
 template.
 
 * Constructors and destructors can figure the storage class of the 
 object being constructed. This is useful for selecting different 
 allocation strategies for immutable vs. mutable objects:
 
 class Widget
 {
   this(int i) { ... }
   this(const)(int i) { ... }
   ~this() { ... }
   ~this(const)() { ... }
 }
 
 Const cdtors can obviously mutate the object being cdted. In a cdtor, 
 'const' is not enforced -- it's just an information for the programmer.
 
 * Walter sustains that given the above, there will never be a need to 
 overload member functions based on const. I disagree, but I couldn't 
 come up with a salient example. Besides, I don't care that much because 
 the semantics above do allow telling const from nonconst, just in a 
 roundabout way.

Jan 21 2007
prev sibling next sibling parent reply Bruno Medeiros <brunodomedeiros+spam com.gmail> writes:
Andrei Alexandrescu (See Website For Email) wrote:
 Frits van Bommel wrote:
 You didn't respond to my paragraph on classes though:

 Frits van Bommel wrote:
  > I also don't see a way this can work for classes (in general). There's
  > no way to limit a non-final method to non-mutating operations that I'm
  > aware of, so it could easily be circumvented by subclassing. Even 
 though
  > that would only allow direct access to non-private fields, mutating 
 base
  > class methods could still be called for the rest (if available). So 
 then
  > the only way to get a const version of a class would be if the class
  > doesn't allow mutations anyway (like java's String). Which leaves us
  > right back where we started :(.
  >
  > And I don't think a 'const' implementation should work for structs and
  > not for classes...

I did when I wrote: >> That is correct. Interface functions, nonfinal methods, and >> declared-only functions must be annotated manually. I meant - yes, you are right. Today, Walter and myself have come with a semantics for const (as in, "read-only view" that) seems to be a good starting point. The details are yet to be ironed out, but here's the basic plot:

So the keyword for the "read-only view" will be 'const' too? It will share space with the 'const' as in "compile time constant" ?
 * const is a unary type constructor (takes a type, returns a type)
 
 * const is always transitive, meaning that once a value is const, the 
 entire part of the world accessible through that value is const as well. 
 This distances D from C++'s shallow view of const (only one level).
 
 * const is not removable legally via a cast. But the compiler must 
 assume in the general case that a const view may alias with a modifiable 
 view. This is a weak point of the design. The hope is that in most cases 
 the compiler will easily prove non-aliasing and can do a good job at 
 optimizing based on const. Example: all pure functions don't care about 
 aliasing to start with.
 
 * Shallow const is achievable via final. Final only means a value is not 
 supposed to be changed, but anything reachable through it can be mutated.
 

So are we going to use the 'final' keyword for that? That's good since it clears up the overloaded meaning of 'const' in current D. (used both for compile time constants and final)
 * The syntax for manually-annotated const is:
 
 struct Widget
 {
   void Foo(const)(int i) { ... }
 }
 
 This is in the spirit of current D, because Foo can be seen as a 
 specialization for a subset of Widget types.
 
 * If you do want to make Foo a template, it's:
 
 struct Widget
 {
   void Foo(const, T)(T i) { ... }
 }
 
 * D avoids the issue of duplicated function bodies in the following way:
 
 struct Widget
 {
   storageof(this) int* Foo(storageof(this))(int i) { ... }
 }
 

Hum, "storageof"? Shouldn't the name be different, since these aren't really storage classes?
 This code transports whatever storage 'this' has to the result type. 
 More involved type computations are allowed because inside Foo, 
 typeof(this) includes the storage class. Effectively Foo above is a 
 template.
 
 * Constructors and destructors can figure the storage class of the 
 object being constructed. This is useful for selecting different 
 allocation strategies for immutable vs. mutable objects:
 
 class Widget
 {
   this(int i) { ... }
   this(const)(int i) { ... }
   ~this() { ... }
   ~this(const)() { ... }
 }
 
 Const cdtors can obviously mutate the object being cdted. In a cdtor, 
 'const' is not enforced -- it's just an information for the programmer.
 

Whoa, huh? What's the meaning of a const constructor or of a const destructor? How would they even be invoked?
 * Walter sustains that given the above, there will never be a need to 
 overload member functions based on const. I disagree, but I couldn't 
 come up with a salient example. Besides, I don't care that much because 
 the semantics above do allow telling const from nonconst, just in a 
 roundabout way.
 
 
 Andrei

-- Bruno Medeiros - MSc in CS/E student http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
Jan 22 2007
next sibling parent reply "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
Bruno Medeiros wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
 Today, Walter and myself have come with a semantics for const (as in, 
 "read-only view" that) seems to be a good starting point. The details 
 are yet to be ironed out, but here's the basic plot:

So the keyword for the "read-only view" will be 'const' too? It will share space with the 'const' as in "compile time constant" ?

Yes. This is arguably a weak point of the design.
 * Shallow const is achievable via final. Final only means a value is 
 not supposed to be changed, but anything reachable through it can be 
 mutated.

So are we going to use the 'final' keyword for that? That's good since it clears up the overloaded meaning of 'const' in current D. (used both for compile time constants and final)

That is correct. An easy way to remember both is: final refers to a name (the name cannot be rebound), const refers to memory accessible through a name (the memory cannot be changed).
 * D avoids the issue of duplicated function bodies in the following way:

 struct Widget
 {
   storageof(this) int* Foo(storageof(this))(int i) { ... }
 }

Hum, "storageof"? Shouldn't the name be different, since these aren't really storage classes?

I think const can be considered a storage class even when it is "borrowed" from data that originally was not const.
 * Constructors and destructors can figure the storage class of the 
 object being constructed. This is useful for selecting different 
 allocation strategies for immutable vs. mutable objects:

 class Widget
 {
   this(int i) { ... }
   this(const)(int i) { ... }
   ~this() { ... }
   ~this(const)() { ... }
 }

 Const cdtors can obviously mutate the object being cdted. In a cdtor, 
 'const' is not enforced -- it's just an information for the programmer.

Whoa, huh? What's the meaning of a const constructor or of a const destructor? How would they even be invoked?

class Widget { ... } auto a = new Widget; // invokes this() auto b = new const Widget; // invokes this(const)() Andrei
Jan 22 2007
parent reply Bruno Medeiros <brunodomedeiros+spam com.gmail> writes:
Andrei Alexandrescu (See Website For Email) wrote:
 Bruno Medeiros wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
 Today, Walter and myself have come with a semantics for const (as in, 
 "read-only view" that) seems to be a good starting point. The details 
 are yet to be ironed out, but here's the basic plot:

So the keyword for the "read-only view" will be 'const' too? It will share space with the 'const' as in "compile time constant" ?

Yes. This is arguably a weak point of the design.

Indeed. :( Won't this cause must woe afterwards, so that it should be changed? Also will the compile-time const also be a "storage class" (i.e., part of the type)? If so how do we distinguish both? As in, how do we check a types "storage classes": is(typeof(foo) == const) // ?
 
 * Constructors and destructors can figure the storage class of the 
 object being constructed. This is useful for selecting different 
 allocation strategies for immutable vs. mutable objects:

 class Widget
 {
   this(int i) { ... }
   this(const)(int i) { ... }
   ~this() { ... }
   ~this(const)() { ... }
 }

 Const cdtors can obviously mutate the object being cdted. In a cdtor, 
 'const' is not enforced -- it's just an information for the programmer.

Whoa, huh? What's the meaning of a const constructor or of a const destructor? How would they even be invoked?

class Widget { ... } auto a = new Widget; // invokes this() auto b = new const Widget; // invokes this(const)() Andrei

Ok, but what is such a const object? Is it an object that actually does not change? (as opposed to simply a read-only view of it?) And what is the difference between a normal delete, and the deletion of a const reference? (the latter doesn't even make sense to me, delete is a mutating operation) -- Bruno Medeiros - MSc in CS/E student http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
Jan 23 2007
next sibling parent reply Frits van Bommel <fvbommel REMwOVExCAPSs.nl> writes:
Bruno Medeiros wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
 Bruno Medeiros wrote:
 Whoa, huh? What's the meaning of a const constructor or of a const 
 destructor? How would they even be invoked?

class Widget { ... } auto a = new Widget; // invokes this() auto b = new const Widget; // invokes this(const)()

Ok, but what is such a const object? Is it an object that actually does not change? (as opposed to simply a read-only view of it?)

I'd guess it would be an object with only read-only views (i.e. one that doesn't change between the end of the constructor and the beginning of the destructor).
 And what is the difference between a normal delete, and the deletion of 
 a const reference? (the latter doesn't even make sense to me, delete is 
 a mutating operation)

Deletion of a const reference will be necessary for const objects at the very least (since there are no non-const references to them). Presumably, that's what the "const destructor" is for. A case could be made for disallowing explicit deletion of const references, but that would mean const objects would only be deletable by the GC. That, in turn would mean const objects would be unusable by people who need (or just prefer) the GC to be disabled... Perhaps this should be more of a "best practice" instead of a language rule?
Jan 23 2007
parent reply "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
Frits van Bommel wrote:
 Bruno Medeiros wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
 Bruno Medeiros wrote:
 Whoa, huh? What's the meaning of a const constructor or of a const 
 destructor? How would they even be invoked?

class Widget { ... } auto a = new Widget; // invokes this() auto b = new const Widget; // invokes this(const)()

Ok, but what is such a const object? Is it an object that actually does not change? (as opposed to simply a read-only view of it?)

I'd guess it would be an object with only read-only views (i.e. one that doesn't change between the end of the constructor and the beginning of the destructor).

That is correct.
 And what is the difference between a normal delete, and the deletion 
 of a const reference? (the latter doesn't even make sense to me, 
 delete is a mutating operation)

Deletion of a const reference will be necessary for const objects at the very least (since there are no non-const references to them). Presumably, that's what the "const destructor" is for. A case could be made for disallowing explicit deletion of const references, but that would mean const objects would only be deletable by the GC. That, in turn would mean const objects would be unusable by people who need (or just prefer) the GC to be disabled... Perhaps this should be more of a "best practice" instead of a language rule?

Deletion of pointers to const data has always been a sticky issue in C++. There are good arguments either way. I think it should be disallowed, otherwise a caller passing a const class object does not have a guarantee that the callee didn't mutate the object, which is the very purpose of the entire scaffolding. Andrei
Jan 23 2007
parent reply Frits van Bommel <fvbommel REMwOVExCAPSs.nl> writes:
Andrei Alexandrescu (See Website For Email) wrote:
 Frits van Bommel wrote:
 Deletion of a const reference will be necessary for const objects at 
 the very least (since there are no non-const references to them). 
 Presumably, that's what the "const destructor" is for.

 A case could be made for disallowing explicit deletion of const 
 references, but that would mean const objects would only be deletable 
 by the GC. That, in turn would mean const objects would be unusable by 
 people who need (or just prefer) the GC to be disabled...
 Perhaps this should be more of a "best practice" instead of a language 
 rule?

Deletion of pointers to const data has always been a sticky issue in C++. There are good arguments either way. I think it should be disallowed, otherwise a caller passing a const class object does not have a guarantee that the callee didn't mutate the object, which is the very purpose of the entire scaffolding.

But as I mentioned, there will only be const references to const instances, so that would mean const objects couldn't be explicitly deleted. And there are people who prefer or need to work with GC off. This limitation would deny them the use of const objects (unless they like memory leaks ;) ). For that reason, I think it should maybe merely be very rude to delete a const reference unless you are absolutely sure nobody else has a reference to the object (that they will ever use), not an error.
Jan 23 2007
parent reply "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
Frits van Bommel wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
 Frits van Bommel wrote:
 Deletion of a const reference will be necessary for const objects at 
 the very least (since there are no non-const references to them). 
 Presumably, that's what the "const destructor" is for.

 A case could be made for disallowing explicit deletion of const 
 references, but that would mean const objects would only be deletable 
 by the GC. That, in turn would mean const objects would be unusable 
 by people who need (or just prefer) the GC to be disabled...
 Perhaps this should be more of a "best practice" instead of a 
 language rule?

Deletion of pointers to const data has always been a sticky issue in C++. There are good arguments either way. I think it should be disallowed, otherwise a caller passing a const class object does not have a guarantee that the callee didn't mutate the object, which is the very purpose of the entire scaffolding.

But as I mentioned, there will only be const references to const instances, so that would mean const objects couldn't be explicitly deleted. And there are people who prefer or need to work with GC off. This limitation would deny them the use of const objects (unless they like memory leaks ;) ).

I disagree. If you want to disable the GC, that doesn't mean you can't use const. You use a private non-const reference for ownership purposes, and you use a const reference for publishing purposes: Widget owned = new Widget; const Widget publicized = owned; ... use publicized wherever ... delete owned; This is IMHO entirely reasonable.
 For that reason, I think it should maybe merely be very rude to delete a 
 const reference unless you are absolutely sure nobody else has a 
 reference to the object (that they will ever use), not an error.

I think this might be an acceptable solution, but given the choice, I'd stay away from it. I think the C++ experience hasn't shown delete const to have any desirable virtues. Imagine a dialog like this: "You shot a diplomat with immunity!" "He's dead. A dead body can't have diplomatic immunity." "But he was alive and immune when you shot him!" "But at that point my bullet was in the gun." Andrei
Jan 23 2007
parent reply Frits van Bommel <fvbommel REMwOVExCAPSs.nl> writes:
Andrei Alexandrescu (See Website For Email) wrote:
 Frits van Bommel wrote:
 But as I mentioned, there will only be const references to const 
 instances, so that would mean const objects couldn't be explicitly 
 deleted.
 And there are people who prefer or need to work with GC off. This 
 limitation would deny them the use of const objects (unless they like 
 memory leaks ;) ).

I disagree. If you want to disable the GC, that doesn't mean you can't use const. You use a private non-const reference for ownership purposes, and you use a const reference for publishing purposes:

<pedantic> I said const *objects*, not const references. </pedantic>
 Widget owned = new Widget;
 const Widget publicized = owned;
 ... use publicized wherever ...
 delete owned;
 
 This is IMHO entirely reasonable.

Yeah, I guess it doesn't look that bad. Besides, I always keep the GC on anyway ;). But I do think this is reasonably likely to become a FAQ.
 For that reason, I think it should maybe merely be very rude to delete 
 a const reference unless you are absolutely sure nobody else has a 
 reference to the object (that they will ever use), not an error.

I think this might be an acceptable solution, but given the choice, I'd stay away from it. I think the C++ experience hasn't shown delete const to have any desirable virtues. Imagine a dialog like this: "You shot a diplomat with immunity!" "He's dead. A dead body can't have diplomatic immunity." "But he was alive and immune when you shot him!" "But at that point my bullet was in the gun."

LOL. (Not sure it applies, but LOL none the less :) )
Jan 23 2007
parent reply "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
Frits van Bommel wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
 Frits van Bommel wrote:
 But as I mentioned, there will only be const references to const 
 instances, so that would mean const objects couldn't be explicitly 
 deleted.
 And there are people who prefer or need to work with GC off. This 
 limitation would deny them the use of const objects (unless they like 
 memory leaks ;) ).

I disagree. If you want to disable the GC, that doesn't mean you can't use const. You use a private non-const reference for ownership purposes, and you use a const reference for publishing purposes:

<pedantic> I said const *objects*, not const references. </pedantic>

This is actually a great point. In the case of classes, is const a property of the value, or a property of the reference through which the value is accessed? This philosophical-sounding question leads actually to radically different implementations. If you associate constness with the value, then you have true immutable values. This was, at a point in the history of humankind, Walter's opinion of a good starting point for talking about const: true ROMable values. However, that approach is too rigid to be useful: too rarely you have truly immutable data, but most often you want to make sure that certain parts of a system *think* data is immutable. A relaxation of that concept therefore is to associate constness with the reference through which the value is accessed. Then you have much more flexibility in that you can have some mutable object, pass it to a function through a const handle (thus knowing that that function can never change it), and then continue enjoying mutable access to that object. The downside of this relaxation is that aliasing might stick its nose. My conjecture is that in the vast majority of cases it can be trivially proved (by the compiler) that no aliased access can affect const data. And even in the cases where the compiler must generates conservative code (as it does today in 100% of the cases), the benefit to the programmer still justifies this more relaxed view of immutability. So: class objects are never const. Const references only restrict access, not the nature of bits on the heap. Andrei
Jan 23 2007
next sibling parent kris <foo bar.com> writes:
Andrei Alexandrescu (See Website For Email) wrote:
 Frits van Bommel wrote:
 
 Andrei Alexandrescu (See Website For Email) wrote:

 Frits van Bommel wrote:

 But as I mentioned, there will only be const references to const 
 instances, so that would mean const objects couldn't be explicitly 
 deleted.
 And there are people who prefer or need to work with GC off. This 
 limitation would deny them the use of const objects (unless they 
 like memory leaks ;) ).

I disagree. If you want to disable the GC, that doesn't mean you can't use const. You use a private non-const reference for ownership purposes, and you use a const reference for publishing purposes:

<pedantic> I said const *objects*, not const references. </pedantic>

This is actually a great point. In the case of classes, is const a property of the value, or a property of the reference through which the value is accessed? This philosophical-sounding question leads actually to radically different implementations. If you associate constness with the value, then you have true immutable values. This was, at a point in the history of humankind, Walter's opinion of a good starting point for talking about const: true ROMable values. However, that approach is too rigid to be useful: too rarely you have truly immutable data, but most often you want to make sure that certain parts of a system *think* data is immutable. A relaxation of that concept therefore is to associate constness with the reference through which the value is accessed. Then you have much more flexibility in that you can have some mutable object, pass it to a function through a const handle (thus knowing that that function can never change it), and then continue enjoying mutable access to that object. The downside of this relaxation is that aliasing might stick its nose. My conjecture is that in the vast majority of cases it can be trivially proved (by the compiler) that no aliased access can affect const data. And even in the cases where the compiler must generates conservative code (as it does today in 100% of the cases), the benefit to the programmer still justifies this more relaxed view of immutability. So: class objects are never const. Const references only restrict access, not the nature of bits on the heap.

All sounds good to me. Bring it on :D
Jan 23 2007
prev sibling next sibling parent reply Frits van Bommel <fvbommel REMwOVExCAPSs.nl> writes:
Andrei Alexandrescu (See Website For Email) wrote:
 Frits van Bommel wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
 Frits van Bommel wrote:
 But as I mentioned, there will only be const references to const 
 instances, so that would mean const objects couldn't be explicitly 
 deleted.
 And there are people who prefer or need to work with GC off. This 
 limitation would deny them the use of const objects (unless they 
 like memory leaks ;) ).

I disagree. If you want to disable the GC, that doesn't mean you can't use const. You use a private non-const reference for ownership purposes, and you use a const reference for publishing purposes:

<pedantic> I said const *objects*, not const references. </pedantic>

This is actually a great point. In the case of classes, is const a property of the value, or a property of the reference through which the value is accessed? This philosophical-sounding question leads actually to radically different implementations. If you associate constness with the value, then you have true immutable values. This was, at a point in the history of humankind, Walter's opinion of a good starting point for talking about const: true ROMable values. However, that approach is too rigid to be useful: too rarely you have truly immutable data, but most often you want to make sure that certain parts of a system *think* data is immutable.

Let's talk about references for a bit first. You have "normal" (mutable) references and const references. When you create an object, you get a mutable reference by default, or a const reference if you ask for it ("new const C"). Normal references implicitly convert to const references, but not the other way around. I just figured that an object created by "new const" would be a "const object", since no non-const references to it are possible without invoking undefined behavior (e.g. using pointer casts). Therefore it can't be modified.
 A relaxation of that concept therefore is to associate constness with 
 the reference through which the value is accessed. Then you have much 
 more flexibility in that you can have some mutable object, pass it to a 
 function through a const handle (thus knowing that that function can 
 never change it), and then continue enjoying mutable access to that object.
 
 The downside of this relaxation is that aliasing might stick its nose. 
 My conjecture is that in the vast majority of cases it can be trivially 
 proved (by the compiler) that no aliased access can affect const data. 
 And even in the cases where the compiler must generates conservative 
 code (as it does today in 100% of the cases), the benefit to the 
 programmer still justifies this more relaxed view of immutability.

For D, you may be right because of the way modules work; the compiler will often have the source to all functions called. For C++ that conjecture will often be wrong, simply because the implementation of functions called will often be in a source file the compiler can't see (it has only seen the header). The compiler needs to either see every variable accessible from called functions (to make sure they can't contain or (transitively) reference an alias) or know for certain that no mutable references accessible to called functions exist (because it sees the allocation and the implementation of every function it may have been mutably passed to). Or maybe there's another way to be sure it won't be modified that I didn't think of, but I don't think there's any way to be sure if it's ever been a mutable parameter to an unseen function and another unseen function is then called.
 So: class objects are never const. Const references only restrict 
 access, not the nature of bits on the heap.

I didn't mean "const object" to mean anything about the nature of the bits, I just used the term to indicate an object that has been const since allocation because it was allocated with "new const".
Jan 23 2007
parent "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
Frits van Bommel wrote:
 Let's talk about references for a bit first. You have "normal" (mutable) 
 references and const references.

Yah.
 When you create an object, you get a 
 mutable reference by default, or a const reference if you ask for it 
 ("new const C"). Normal references implicitly convert to const 
 references, but not the other way around.

Yah.
 I just figured that an object created by "new const" would be a "const 
 object", since no non-const references to it are possible without 
 invoking undefined behavior (e.g. using pointer casts). Therefore it 
 can't be modified.

In D, an object created with "new const" is exactly like the proverbial tree falling in the forest: it's actually not const, but nobody can change it, so nobody can tell.
 The downside of this relaxation is that aliasing might stick its nose. 
 My conjecture is that in the vast majority of cases it can be 
 trivially proved (by the compiler) that no aliased access can affect 
 const data. And even in the cases where the compiler must generates 
 conservative code (as it does today in 100% of the cases), the benefit 
 to the programmer still justifies this more relaxed view of immutability.

For D, you may be right because of the way modules work; the compiler will often have the source to all functions called. For C++ that conjecture will often be wrong, simply because the implementation of functions called will often be in a source file the compiler can't see (it has only seen the header). The compiler needs to either see every variable accessible from called functions (to make sure they can't contain or (transitively) reference an alias) or know for certain that no mutable references accessible to called functions exist (because it sees the allocation and the implementation of every function it may have been mutably passed to). Or maybe there's another way to be sure it won't be modified that I didn't think of, but I don't think there's any way to be sure if it's ever been a mutable parameter to an unseen function and another unseen function is then called.

Something like that. Don't forget, however, that exceedingly few optimizations make assumptions across a function call. In the most frequent case, you just want to make sure that some data is immutable over a short burst of primitive operations. I think this is easily doable. Andrei
Jan 23 2007
prev sibling parent reply Chris Nicholson-Sauls <ibisbasenji gmail.com> writes:
Andrei Alexandrescu (See Website For Email) wrote:
 Frits van Bommel wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
 Frits van Bommel wrote:
 But as I mentioned, there will only be const references to const 
 instances, so that would mean const objects couldn't be explicitly 
 deleted.
 And there are people who prefer or need to work with GC off. This 
 limitation would deny them the use of const objects (unless they 
 like memory leaks ;) ).

I disagree. If you want to disable the GC, that doesn't mean you can't use const. You use a private non-const reference for ownership purposes, and you use a const reference for publishing purposes:

<pedantic> I said const *objects*, not const references. </pedantic>

This is actually a great point. In the case of classes, is const a property of the value, or a property of the reference through which the value is accessed? This philosophical-sounding question leads actually to radically different implementations. If you associate constness with the value, then you have true immutable values. This was, at a point in the history of humankind, Walter's opinion of a good starting point for talking about const: true ROMable values. However, that approach is too rigid to be useful: too rarely you have truly immutable data, but most often you want to make sure that certain parts of a system *think* data is immutable. A relaxation of that concept therefore is to associate constness with the reference through which the value is accessed. Then you have much more flexibility in that you can have some mutable object, pass it to a function through a const handle (thus knowing that that function can never change it), and then continue enjoying mutable access to that object. The downside of this relaxation is that aliasing might stick its nose. My conjecture is that in the vast majority of cases it can be trivially proved (by the compiler) that no aliased access can affect const data. And even in the cases where the compiler must generates conservative code (as it does today in 100% of the cases), the benefit to the programmer still justifies this more relaxed view of immutability. So: class objects are never const. Const references only restrict access, not the nature of bits on the heap. Andrei

So then I ask, why have const constructors/destructors? What, given an (even contrived) example, would be the likely difference in code between constant and non-constant constructors/destructors? And how is this: # Foo f = new const Foo; Different from this: # const Foo f = new Foo; Granted I like the direction D is headed with const if this is indicative, I'm just stuck on a few points. Probably won't be able to form a full opinion until I can see it in action. -- Chris Nicholson-Sauls
Jan 23 2007
parent reply "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
Chris Nicholson-Sauls wrote:
 So then I ask, why have const constructors/destructors?  What, given 
 an (even contrived) example, would be the likely difference in code 
 between constant and non-constant constructors/destructors?  And how 
 is this:

A constructor might take different decisions on caching, preallocating etc. when it prepares for mutability, as opposed to it knowing that the object won't change. For example, creating a const deque might cause a different allocation strategy than a non-const one.
 # Foo f = new const Foo;
 
 Different from this: # const Foo f = new Foo;

In the latter case you are robbing the constructor from a little piece of information. Things will still work. Andrei
Jan 23 2007
next sibling parent reply Chris Nicholson-Sauls <ibisbasenji gmail.com> writes:
Andrei Alexandrescu (See Website For Email) wrote:
 Chris Nicholson-Sauls wrote:
 So then I ask, why have const constructors/destructors?  What, given 
 an (even contrived) example, would be the likely difference in code 
 between constant and non-constant constructors/destructors?  And how 
 is this:

A constructor might take different decisions on caching, preallocating etc. when it prepares for mutability, as opposed to it knowing that the object won't change. For example, creating a const deque might cause a different allocation strategy than a non-const one.

Okay, granted. (Const instances coming from a pool rather than newly created, for example.) So, a new question, can the const constructors call the "normal" constructors? Aka: class Foo { this () { /* ... do stuff ... */ } this(const) () { // ... yadda yadda this(); // ... yadda yadda } } And what else might possible gain this odd 'func(attr)(...)' notation later? Its... interesting, to be sure. But certainly better to my eyes than 'func (...) const' and company ever were.
 # Foo f = new const Foo;

 Different from this: # const Foo f = new Foo;

In the latter case you are robbing the constructor from a little piece of information. Things will still work. Andrei

Actually this was inadvertantly answered for me when I read elsewhere in the thread of the notion of const playing a part in implicit casting, namely that mutable->const is valid while const->mutable is not. Just had a dull brain moment, I suppose. :) -- Chris Nicholson-Sauls
Jan 23 2007
parent "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
Chris Nicholson-Sauls wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
 Chris Nicholson-Sauls wrote:
 So then I ask, why have const constructors/destructors?  What, given 
 an (even contrived) example, would be the likely difference in code 
 between constant and non-constant constructors/destructors?  And how 
 is this:

A constructor might take different decisions on caching, preallocating etc. when it prepares for mutability, as opposed to it knowing that the object won't change. For example, creating a const deque might cause a different allocation strategy than a non-const one.

Okay, granted. (Const instances coming from a pool rather than newly created, for example.) So, a new question, can the const constructors call the "normal" constructors? Aka: class Foo { this () { /* ... do stuff ... */ } this(const) () { // ... yadda yadda this(); // ... yadda yadda } }

Yes. Also, it should be notice that a const constructor could escape a non-const reference to this, which makes the tree falling in the forest audible. This is in keep with the intended semantics of const, and testament to the fact that you don't want a more restrictive semantics.
 And what else might possible gain this odd 'func(attr)(...)' notation 
 later?  Its... interesting, to be sure.  But certainly better to my eyes 
 than 'func (...) const' and company ever were.

Scope is another interesting attribute that you may want to detect. Andrei
Jan 23 2007
prev sibling parent reply Bruno Medeiros <brunodomedeiros+spam com.gmail> writes:
Andrei Alexandrescu (See Website For Email) wrote:
 Chris Nicholson-Sauls wrote:
 So then I ask, why have const constructors/destructors?  What, given 
 an (even contrived) example, would be the likely difference in code 
 between constant and non-constant constructors/destructors?  And how 
 is this:

A constructor might take different decisions on caching, preallocating etc. when it prepares for mutability, as opposed to it knowing that the object won't change. For example, creating a const deque might cause a different allocation strategy than a non-const one.
 # Foo f = new const Foo;

 Different from this: # const Foo f = new Foo;

In the latter case you are robbing the constructor from a little piece of information. Things will still work. Andrei

I was about to ask the same that Chris N.S. did. So how about a destructor, what would a const destructor differ from a normal one? Perhaps to destroy the const object in cases where such object was created in such a way that the normal destructor no longer correctly knows how to destroy the const object? Seems reasonable but I can't think of an example of the top of my head. -- Bruno Medeiros - MSc in CS/E student http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
Jan 23 2007
parent "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
Bruno Medeiros wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
 Chris Nicholson-Sauls wrote:
 So then I ask, why have const constructors/destructors?  What, given 
 an (even contrived) example, would be the likely difference in code 
 between constant and non-constant constructors/destructors?  And how 
 is this:

A constructor might take different decisions on caching, preallocating etc. when it prepares for mutability, as opposed to it knowing that the object won't change. For example, creating a const deque might cause a different allocation strategy than a non-const one.
 # Foo f = new const Foo;

 Different from this: # const Foo f = new Foo;

In the latter case you are robbing the constructor from a little piece of information. Things will still work. Andrei

I was about to ask the same that Chris N.S. did. So how about a destructor, what would a const destructor differ from a normal one? Perhaps to destroy the const object in cases where such object was created in such a way that the normal destructor no longer correctly knows how to destroy the const object? Seems reasonable but I can't think of an example of the top of my head.

If a const constructor does things a different way than the non-const constructor, it follows that the const destructor might need to "undo" things differently too. E.g., a const constructor can use sharing among all const instances. Consequently, the const destructor must know this so it doesn't assume it owns the object's resources. Andrei
Jan 23 2007
prev sibling parent "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
Bruno Medeiros wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
 Bruno Medeiros wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
 Today, Walter and myself have come with a semantics for const (as 
 in, "read-only view" that) seems to be a good starting point. The 
 details are yet to be ironed out, but here's the basic plot:

So the keyword for the "read-only view" will be 'const' too? It will share space with the 'const' as in "compile time constant" ?

Yes. This is arguably a weak point of the design.

Indeed. :( Won't this cause must woe afterwards, so that it should be changed? Also will the compile-time const also be a "storage class" (i.e., part of the type)? If so how do we distinguish both? As in, how do we check a types "storage classes": is(typeof(foo) == const) // ?

Yes, the intent is that const is part of the type and that it is detectable.
 class Widget { ... }
 auto a = new Widget; // invokes this()
 auto b = new const Widget; // invokes this(const)()

Ok, but what is such a const object? Is it an object that actually does not change? (as opposed to simply a read-only view of it?)

It's an object that has no accessible mutating view.
 And what is the difference between a normal delete, and the deletion of 
 a const reference? (the latter doesn't even make sense to me, delete is 
 a mutating operation)

As I said, this is a sticky point. I think the only right semantics is to disable deletion. Andrei
Jan 23 2007
prev sibling parent reply Lionello Lunesu <lio lunesu.remove.com> writes:
Bruno Medeiros wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
 Frits van Bommel wrote:
 You didn't respond to my paragraph on classes though:

 Frits van Bommel wrote:
  > I also don't see a way this can work for classes (in general). 
 There's
  > no way to limit a non-final method to non-mutating operations that 
 I'm
  > aware of, so it could easily be circumvented by subclassing. Even 
 though
  > that would only allow direct access to non-private fields, 
 mutating base
  > class methods could still be called for the rest (if available). 
 So then
  > the only way to get a const version of a class would be if the class
  > doesn't allow mutations anyway (like java's String). Which leaves us
  > right back where we started :(.
  >
  > And I don't think a 'const' implementation should work for structs 
 and
  > not for classes...

I did when I wrote: >> That is correct. Interface functions, nonfinal methods, and >> declared-only functions must be annotated manually. I meant - yes, you are right. Today, Walter and myself have come with a semantics for const (as in, "read-only view" that) seems to be a good starting point. The details are yet to be ironed out, but here's the basic plot:

So the keyword for the "read-only view" will be 'const' too? It will share space with the 'const' as in "compile time constant" ?

'const' is not always a compile time constant. A const in function or in a class can have a different value for each call/instance. Don't ask me why. L.
Jan 22 2007
next sibling parent kris <foo bar.com> writes:
Lionello Lunesu wrote:
 Bruno Medeiros wrote:
 
 Andrei Alexandrescu (See Website For Email) wrote:

 Frits van Bommel wrote:

 You didn't respond to my paragraph on classes though:

 Frits van Bommel wrote:
  > I also don't see a way this can work for classes (in general). 
 There's
  > no way to limit a non-final method to non-mutating operations 
 that I'm
  > aware of, so it could easily be circumvented by subclassing. Even 
 though
  > that would only allow direct access to non-private fields, 
 mutating base
  > class methods could still be called for the rest (if available). 
 So then
  > the only way to get a const version of a class would be if the class
  > doesn't allow mutations anyway (like java's String). Which leaves us
  > right back where we started :(.
  >
  > And I don't think a 'const' implementation should work for 
 structs and
  > not for classes...

I did when I wrote: >> That is correct. Interface functions, nonfinal methods, and >> declared-only functions must be annotated manually. I meant - yes, you are right. Today, Walter and myself have come with a semantics for const (as in, "read-only view" that) seems to be a good starting point. The details are yet to be ironed out, but here's the basic plot:

So the keyword for the "read-only view" will be 'const' too? It will share space with the 'const' as in "compile time constant" ?

'const' is not always a compile time constant. A const in function or in a class can have a different value for each call/instance. Don't ask me why. L.

const int also takes space in an executable. It apparenltly becomes a true 4-byte instance, not a compile-time constant. Though, why anyone would *expect* to take the address of a constant is completely beyond my comprehension. No big deal, huh? Then consider the avalanche of constants in a full Win32 API
Jan 23 2007
prev sibling next sibling parent reply Sean Kelly <sean f4.ca> writes:
Lionello Lunesu wrote:
 Bruno Medeiros wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
 Frits van Bommel wrote:
 You didn't respond to my paragraph on classes though:

 Frits van Bommel wrote:
  > I also don't see a way this can work for classes (in general). 
 There's
  > no way to limit a non-final method to non-mutating operations 
 that I'm
  > aware of, so it could easily be circumvented by subclassing. Even 
 though
  > that would only allow direct access to non-private fields, 
 mutating base
  > class methods could still be called for the rest (if available). 
 So then
  > the only way to get a const version of a class would be if the class
  > doesn't allow mutations anyway (like java's String). Which leaves us
  > right back where we started :(.
  >
  > And I don't think a 'const' implementation should work for 
 structs and
  > not for classes...

I did when I wrote: >> That is correct. Interface functions, nonfinal methods, and >> declared-only functions must be annotated manually. I meant - yes, you are right. Today, Walter and myself have come with a semantics for const (as in, "read-only view" that) seems to be a good starting point. The details are yet to be ironed out, but here's the basic plot:

So the keyword for the "read-only view" will be 'const' too? It will share space with the 'const' as in "compile time constant" ?

'const' is not always a compile time constant. A const in function or in a class can have a different value for each call/instance. Don't ask me why.

I ran into this problem recently: module a; class C { const int val; static this() { val = 1; } } C:\code\src\d\test>dmd test test.d(7): Error: can only initialize const val inside constructor I'm sure this is a bug but haven't reported it yet. Trying to use a static module ctor to init the class const doesn't work either. But if the const is global then all is well. Sean
Jan 23 2007
parent reply Frits van Bommel <fvbommel REMwOVExCAPSs.nl> writes:
Sean Kelly wrote:
 Lionello Lunesu wrote:
 'const' is not always a compile time constant. A const in function or 
 in a class can have a different value for each call/instance. Don't 
 ask me why.

I ran into this problem recently: module a; class C { const int val; static this() { val = 1; } } C:\code\src\d\test>dmd test test.d(7): Error: can only initialize const val inside constructor I'm sure this is a bug but haven't reported it yet. Trying to use a static module ctor to init the class const doesn't work either. But if the const is global then all is well.

I don't see how that is a bug in anything but your code. The error message may be a bit unclear in this instance, but I believe it is still correct. Note that it says you need to do it in a constructor, not in a *static* constructor (or *module* constructor). You need a plain old constructor to initialize that field. That, or the field should have been static. So you may have meant one of these: ----- class C { static const int val; static this() { val = 1; } } class D { const int val; this() { val = 1; } } ----- The first is for if it's the same for all instances of the class, the second if it doesn't change for the lifetime of an instance of the class. Neither of these needs to be a compile-time constant; both can be determined at runtime.
Jan 23 2007
parent reply Sean Kelly <sean f4.ca> writes:
Frits van Bommel wrote:
 Sean Kelly wrote:
 Lionello Lunesu wrote:
 'const' is not always a compile time constant. A const in function or 
 in a class can have a different value for each call/instance. Don't 
 ask me why.

I ran into this problem recently: module a; class C { const int val; static this() { val = 1; } } C:\code\src\d\test>dmd test test.d(7): Error: can only initialize const val inside constructor I'm sure this is a bug but haven't reported it yet. Trying to use a static module ctor to init the class const doesn't work either. But if the const is global then all is well.

I don't see how that is a bug in anything but your code. The error message may be a bit unclear in this instance, but I believe it is still correct. Note that it says you need to do it in a constructor, not in a *static* constructor (or *module* constructor). You need a plain old constructor to initialize that field. That, or the field should have been static.

Aren't const values implicitly static? I assumed that they were.
 So you may have meant one of these:
 -----
     class C
     {
         static const int val;
 
         static this() { val = 1; }
     }
 
     class D
     {
         const int val;
 
         this() { val = 1; }
     }
 -----
 The first is for if it's the same for all instances of the class, the 
 second if it doesn't change for the lifetime of an instance of the 
 class. Neither of these needs to be a compile-time constant; both can be 
 determined at runtime.

Very weird. I had no idea that constants could be defined on a per-instance basis. Thanks! Sean
Jan 23 2007
parent Frits van Bommel <fvbommel REMwOVExCAPSs.nl> writes:
Sean Kelly wrote:
 Aren't const values implicitly static?  I assumed that they were.

You assumed wrong :).
 Very weird.  I had no idea that constants could be defined on a 
 per-instance basis.  Thanks!

Per-instance constants isn't something most people use regularly, I guess (me included).
Jan 23 2007
prev sibling parent Bruno Medeiros <brunodomedeiros+spam com.gmail> writes:
Lionello Lunesu wrote:
 Bruno Medeiros wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
 Frits van Bommel wrote:
 You didn't respond to my paragraph on classes though:

 Frits van Bommel wrote:
  > I also don't see a way this can work for classes (in general). 
 There's
  > no way to limit a non-final method to non-mutating operations 
 that I'm
  > aware of, so it could easily be circumvented by subclassing. Even 
 though
  > that would only allow direct access to non-private fields, 
 mutating base
  > class methods could still be called for the rest (if available). 
 So then
  > the only way to get a const version of a class would be if the class
  > doesn't allow mutations anyway (like java's String). Which leaves us
  > right back where we started :(.
  >
  > And I don't think a 'const' implementation should work for 
 structs and
  > not for classes...

I did when I wrote: >> That is correct. Interface functions, nonfinal methods, and >> declared-only functions must be annotated manually. I meant - yes, you are right. Today, Walter and myself have come with a semantics for const (as in, "read-only view" that) seems to be a good starting point. The details are yet to be ironed out, but here's the basic plot:

So the keyword for the "read-only view" will be 'const' too? It will share space with the 'const' as in "compile time constant" ?

'const' is not always a compile time constant. A const in function or in a class can have a different value for each call/instance. Don't ask me why. L.

I know that very well, I mentioned it ahead in the: "So are we going to use the 'final' keyword for that? That's good since it clears up the overloaded meaning of 'const' in current D. (used both for compile time constants and final)" Another post from some time ago about this: http://www.digitalmars.com/pnews/read.php?server=news.digitalmars.com&group=digitalmars.D&artnum=44168 -- Bruno Medeiros - MSc in CS/E student http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
Jan 23 2007
prev sibling parent reply Bruno Medeiros <brunodomedeiros+spam com.gmail> writes:
Andrei Alexandrescu (See Website For Email) wrote:
 
 * D avoids the issue of duplicated function bodies in the following way:
 
 struct Widget
 {
   storageof(this) int* Foo(storageof(this))(int i) { ... }
 }
 

Hum... what if I want to override such functions? Will D even allow it? -- Bruno Medeiros - MSc in CS/E student http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
Jan 23 2007
parent "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
Bruno Medeiros wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
 * D avoids the issue of duplicated function bodies in the following way:

 struct Widget
 {
   storageof(this) int* Foo(storageof(this))(int i) { ... }
 }

Hum... what if I want to override such functions? Will D even allow it?

Technically that's a template function, so you can't override it. But it's a template with only a few specializations, so in theory overriding could be made to work. Andrei
Jan 23 2007
prev sibling parent reply Walter Bright <newshound digitalmars.com> writes:
Bill Baxter wrote:
 Walter Bright wrote:
 Bill Baxter wrote:
 The question is how can we write functions in D that take big structs 
 as parameters in such a way that it is:
 A) efficient and
 B) guaranteed not to change the caller's value

pointer are: 1) inout 2) take the address and use a pointer 3) make it a class instead

And none of those accomplishes a or b.

All of them accomplish A.
Jan 20 2007
parent Bill Baxter <dnewsgroup billbaxter.com> writes:
Walter Bright wrote:
 Bill Baxter wrote:
 Walter Bright wrote:
 Bill Baxter wrote:
 The question is how can we write functions in D that take big 
 structs as parameters in such a way that it is:
 A) efficient and
 B) guaranteed not to change the caller's value

pointer are: 1) inout 2) take the address and use a pointer 3) make it a class instead

And none of those accomplishes a or b.

All of them accomplish A.

Ha ha. You almost had me for a minute there. You cut and pasted bits and pieces of two different messages of mine. The a/b in question was actually:
 Very true.  What I'm after is really a concise way to both
 a) document to readers of the code that it won't change and
 b) get the compiler to zap me if I accidentally try to modify it.

That's the a/b I said none of your pointer-based solutions achieve. --bb
Jan 20 2007
prev sibling parent reply James Dennett <jdennett acm.org> writes:
Walter Bright wrote:
 Bill Baxter wrote:
 The question is how can we write functions in D that take big structs
 as parameters in such a way that it is:
 A) efficient and
 B) guaranteed not to change the caller's value

 In C++ the answer is const LargeStruct&.

C++ doesn't *guarantee* it won't change. There are at least two legal ways to do it.

This is an oft-repeated point, but it also misses the users' perspective. It's important from an implementors perspective that this use of const as a type modifier doesn't provide the compiler with a guarantee of immutability. Given sensible users, that doesn't generally matter to programmers writing code. const documents an interface; it doesn't enforce it, though
 In D the answer is...

There aren't const references in D. But the 3 ways to pass it by pointer are: 1) inout 2) take the address and use a pointer 3) make it a class instead

So, in D the answer is that you have to provide a wrapper which provides no update operations? Very heavyweight for a simple, useful thing that C++ can express pretty well inside the language. Users like it; they mostly find that it does what they like. Java, C#, D and Felix have dropped this valuable feature from C++, and users miss it. const in C++ is far from perfect, but it's much, much more useful than having no answer other than "make a copy and maybe the compiler will be able to optimize it away". Sometimes we really do want reference-to-const semantics. Granted, this adds some complexity to the language, and other languages have flourished without anything like the C++ notion of const as a qualifier on pointers/references. But it's important to recognize that this shouldn't be dismissed by noting that const (apart from top-level const) doesn't provide much useful guarantee to a compiler write; it *does* provide something very useful to programmers using a language. -- James
Jan 20 2007
parent Bill Baxter <dnewsgroup billbaxter.com> writes:
James Dennett wrote:
 Walter Bright wrote:
 Bill Baxter wrote:
 The question is how can we write functions in D that take big structs
 as parameters in such a way that it is:
 A) efficient and
 B) guaranteed not to change the caller's value

 In C++ the answer is const LargeStruct&.

ways to do it.

This is an oft-repeated point, but it also misses the users' perspective. It's important from an implementors perspective that this use of const as a type modifier doesn't provide the compiler with a guarantee of immutability. Given sensible users, that doesn't generally matter to programmers writing code. const documents an interface; it doesn't enforce it, though

Exactly. The main reason I want to be able to have something like a const inout is because I want the compiler to let _me_ know when I'm writing some function if I accidentally modify some value that I promised in my interface that I wouldn't modify. It doesn't bother me that the constness can be subverted by a cast. In fact I'd *like* to be able to choose to modify some private bit of state in an object if necessary (e.g. private mutable members used for caching or somesuch). D is a down-to-the-metal systems programming language, after all, not a nanny language. In fact, I really don't see much practical use for a totally watertight, totally irrevocable const. (Unless it maybe opens the door for some sweet optimizations. But I'd argue that should be a separate thing from this const that's more of a promise about the interface.) That said, I'm also certainly not in love with how const works in C++. At the end of the day, all I really want is an efficient way to pass big structs without suggesting in the interface that they'll be modified. But that leads naturally to const references, and then that basically leads to const everywhere, just like C++, since you can't pass any const parameter to another function that isn't also const. :-( --bb
Jan 21 2007
prev sibling next sibling parent Bill Baxter <dnewsgroup billbaxter.com> writes:
Bill Baxter wrote:
 Walter Bright wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
 No, and for a good reason:

 foo(LargeStruct s1, LargeStruct s2)
 {
   ...
 }
 ...
 LargeStruct s;
 foo(s, s);

 Nobody would enjoy hidden aliasing of s1 and s2.

There's another aliasing case: LargeStruct s; foo(s); ... here some other thread modifies s ...


This exists in C++ with const reference parameters. But I've not heard much gnashing of teeth over this. Maybe people are getting worried about it more these days, but since I started learning C++ I've seen lots of code that uses const references all over the place. But I can see that it is important to make the distinction in the header for those who are writing multithreaded code. Which leaves us with two alternatives: 1) add some sort of const-reference construct to D 2) leave it as is. I have to say that 2) is probably going to send me packing. I've always assumed that D would get some sort of const reference someday but it sounds like you're saying you don't see a need. But I think this raytracer example shows very clearly that using even moderately sized structs (i.e. 3-6 floats, not even that big) as parameters in critical loops can degrade performance significantly. Switching to a class is not really an option. That's going to be even slower given that intermediate vector calculations are needed all over the place. So the other option is scope classes which someone suggested on the other thread. Is this really an option? I haven't played with it that much, so I don't really know what the issues are or what the syntax looks like in practice. But I believe at least that it means every place you use LargeStruct you're going to have to say 'scope LargeStruct', right? That's fairly ugly to have to say everywhere for something that's going to get heavy use. Especially when in the end it really *is* a value type. So it should be a struct. Period. --bb
Jan 20 2007
prev sibling parent reply "Christian Kamm" <kamm nospam.de> writes:
  There's another aliasing case:
  LargeStruct s;
 foo(s);
 ... here some other thread modifies s ...


Okay, I can see that this could result in unexpected behaviour. The idea of determining whether to copy or not during compile time was not a good one at all. What sparked this suggestion was exactly the situation Bill Baxter described:
 The question is how can we write functions in D that take big structs as  
 parameters in such a way that it is:
 A) efficient and
 B) guaranteed not to change the caller's value

If inout is used, it doesn't guarantee B) and even worse: I'm lying to anyone reading the function signature. A pointer will also require additional documentation to keep users from wondering whether their data is changed or not. Making the struct a class is not an option because of A). Maybe it's just time to accept that D just doesn't do const the C++ way and B) can't be expressed. Christian
Jan 20 2007
parent reply Dave <Dave_member pathlink.com> writes:
Christian Kamm wrote:
 
  There's another aliasing case:
  LargeStruct s;
 foo(s);
 ... here some other thread modifies s ...


Okay, I can see that this could result in unexpected behaviour. The idea of determining whether to copy or not during compile time was not a good one at all. What sparked this suggestion was exactly the situation Bill Baxter described:
 The question is how can we write functions in D that take big structs 
 as parameters in such a way that it is:
 A) efficient and
 B) guaranteed not to change the caller's value

If inout is used, it doesn't guarantee B) and even worse: I'm lying to anyone reading the function signature. A pointer will also require

I agree, I don't like the thought of us having to use inout as an optimization. That's not what it's meant for. Leaving aside the notion of 'const' right now, I think 'byref' or some such would work fine for this purpose. It tells a function user "this is passed by reference" and is easily grep-able (unlike '&'). And a pointer will require the use of '&' at every call site instead of 'byref' just once in the function definition.
 additional documentation to keep users from wondering whether their data 
 is changed or not. Making the struct a class is not an option because of 
 A).
 
 Maybe it's just time to accept that D just doesn't do const the C++ way 
 and B) can't be expressed.
 
 Christian

Jan 20 2007
parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
Dave wrote:
 Christian Kamm wrote:
 If inout is used, it doesn't guarantee B) and even worse: I'm lying to 
 anyone reading the function signature. A pointer will also require 

I agree, I don't like the thought of us having to use inout as an optimization. That's not what it's meant for. Leaving aside the notion of 'const' right now, I think 'byref' or some such would work fine for this purpose. It tells a function user "this is passed by reference" and is easily grep-able (unlike '&'). And a pointer will require the use of '&' at every call site instead of 'byref' just once in the function definition.

So you're suggesting byref would be a synonym for inout? And the compiler would treat it exactly like the current inout? If the compiler's not going to be of any help in making sure that I don't change things that I don't mean to be changing, then I don't see much point in introducing a new keyword. --bb
Jan 20 2007
parent Dave <Dave_member pathlink.com> writes:
Bill Baxter wrote:
 Dave wrote:
 Christian Kamm wrote:
 If inout is used, it doesn't guarantee B) and even worse: I'm lying 
 to anyone reading the function signature. A pointer will also require 

I agree, I don't like the thought of us having to use inout as an optimization. That's not what it's meant for. Leaving aside the notion of 'const' right now, I think 'byref' or some such would work fine for this purpose. It tells a function user "this is passed by reference" and is easily grep-able (unlike '&'). And a pointer will require the use of '&' at every call site instead of 'byref' just once in the function definition.

So you're suggesting byref would be a synonym for inout? And the compiler would treat it exactly like the current inout? If the compiler's not going to be of any help in making sure that I don't change things that I don't mean to be changing, then I don't see much point in introducing a new keyword.

'inout' doesn't necessarily specify 'byref'. But, in C++ if you specify '&' then the param. must be passed by reference. 'byref' would act like that. 'byref' doesn't have to satisfy the IDL requirement like inout does either. I agree that if it could be made to work, 'byref' acting like a semantically meaningful C++ 'const&' would be great (then maybe 'cref' for the keyword?). D is terrific - truly a better C++ while avoiding almost all of C++'s corner cases. But IMHO D takes this a little too far without reference params. and being able to return references (and ctors for structs while I'm at it). They are used so often in C++ I find it hard to justify a language design for 'a better C++' that doesn't match those capabilities.
 --bb

Jan 20 2007
prev sibling parent reply janderson <askme me.com> writes:
Walter Bright wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
 No, and for a good reason:

 foo(LargeStruct s1, LargeStruct s2)
 {
   ...
 }
 ...
 LargeStruct s;
 foo(s, s);

 Nobody would enjoy hidden aliasing of s1 and s2.


I think you mean more like: foo(LargeStruct s1, LargeStruct inout s2) { ... } ... LargeStruct s; foo(s, s); In cases where there is an inout, I think the compiler would have to fallback on the old one. The one below that Walter pointed out is more troublesome.
 
 There's another aliasing case:
 
 LargeStruct s;
 foo(s);
 ... here some other thread modifies s ...
 
 And foo() suddenly has its argument values change unexpectedly.
 
 Value and reference type usages mostly overlap, but they are 
 fundamentally different, and having the compiler switch between the two 
 in some implementation-defined manner (as suggested by others more than 
 once) is not going to work.
 
 If one is needing reference behavior from a struct, seriously consider 
 making it a class instead. Or even just take the address of it and pass 
 the pointer.

I never thought about that one. Of course your right about this like normal. Humm... what about copy on read/write... struct A { byte[100] b; } //My contrived example void foo(in A x, in A y) { if (g) { if (x) { //... } else { if (y) { //... } } } } //Would convert too. void foo(inout A x, inout A y) //Both treated as in/out { if (g) { A x2 = x; //First time x is used if (x2) { //... } else { A y2 = y; //First time y is used if (y2) { //... } } } } if (g) was false you would avoid the expensive copy of x2 to the stack. Of course the tricky part is working out when this optimisation is worth it. The function could actually run slower because there are 2 more values on the stack. Ok, I spotted a floor in that design also. A thread event could occur between the copy of x2 and y2 (or at g). I guess this would mean the user would have to somehow indicate that that is ok. Ok here's yet another idea. void bar(in A a) { //.... } void foo(in A a) { bar(a); bar(a); } In this example we already have an in copy of A, so why copy it again? We do not have a reference and foo isn't doing any threading operations. If this was inlined it would be optimized away, however if it can't be inlined it should still be possible to optimize it away. //Here's another example of the some thing void foo() { A a; bar(a); //End of function A goes out of scope and A is destroyed. } -Joel
Jan 20 2007
parent reply "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
janderson wrote:
 Walter Bright wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
 No, and for a good reason:

 foo(LargeStruct s1, LargeStruct s2)
 {
   ...
 }
 ...
 LargeStruct s;
 foo(s, s);

 Nobody would enjoy hidden aliasing of s1 and s2.


I think you mean more like: foo(LargeStruct s1, LargeStruct inout s2) { ... } ... LargeStruct s; foo(s, s);

I meant what I wrote, although your example is just as relevant. If the compiler takes initiative in passing things by reference, there is risk of unintended aliasing. Andrei
Jan 20 2007
parent reply janderson <askme me.com> writes:
Andrei Alexandrescu (See Website For Email) wrote:
 janderson wrote:
 Walter Bright wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
 No, and for a good reason:

 foo(LargeStruct s1, LargeStruct s2)
 {
   ...
 }
 ...
 LargeStruct s;
 foo(s, s);

 Nobody would enjoy hidden aliasing of s1 and s2.


I think you mean more like: foo(LargeStruct s1, LargeStruct inout s2) { ... } ... LargeStruct s; foo(s, s);

I meant what I wrote, although your example is just as relevant. If the compiler takes initiative in passing things by reference, there is risk of unintended aliasing. Andrei

If all the function does is read the values. How can aliasing be a problem? -Joel
Jan 20 2007
parent "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> writes:
janderson wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
 janderson wrote:
 Walter Bright wrote:
 Andrei Alexandrescu (See Website For Email) wrote:
 No, and for a good reason:

 foo(LargeStruct s1, LargeStruct s2)
 {
   ...
 }
 ...
 LargeStruct s;
 foo(s, s);

 Nobody would enjoy hidden aliasing of s1 and s2.


I think you mean more like: foo(LargeStruct s1, LargeStruct inout s2) { ... } ... LargeStruct s; foo(s, s);

I meant what I wrote, although your example is just as relevant. If the compiler takes initiative in passing things by reference, there is risk of unintended aliasing. Andrei

If all the function does is read the values. How can aliasing be a problem?

Probably I misunderstood some of what was said earlier in the discussion, sorry. My current understanding is that we're on the same page: there is an aliasing issue that needs proper addressing. :o) Andrei
Jan 20 2007