digitalmars.D - Performance improvements for D / DMD compiler.
- Dave (7/7) Jan 19 2007 What's on your wish-list (let's leave the GC and other library functiona...
- janderson (6/17) Jan 19 2007 The above and:
- janderson (4/25) Jan 19 2007 Also:
- janderson (11/20) Jan 21 2007 I should add, compiler identifying cases where:
- BCS (13/24) Jan 19 2007 Better const folding/handling, local const values are put into the stack...
- Frits van Bommel (8/11) Jan 19 2007 On a related note: put global variables (especially constants) into
- Christian Kamm (8/10) Jan 19 2007 I'd like to see unneccessary copies being optimized away, especially of ...
- Andrei Alexandrescu (See Website For Email) (11/24) Jan 19 2007 No, and for a good reason:
- Bill Baxter (28/57) Jan 19 2007 I'm not really sure why that would be a problem, as long as the compiler...
- janderson (7/45) Jan 19 2007 I believe this is possible. In particular since D generates its own
- Walter Bright (13/24) Jan 19 2007 There's another aliasing case:
- Bill Baxter (9/38) Jan 20 2007 If we're going to pass a pointer to it, then we might as well just make
- Walter Bright (8/15) Jan 20 2007 C++ doesn't *guarantee* it won't change. There are at least two legal
- Bill Baxter (6/24) Jan 20 2007 Very true. What I'm after is really a concise way to both
- Kyle Furlong (3/31) Jan 20 2007 Ah the great const debate. I have a feeling that this discussion will be...
- Rioshin an'Harthen (6/8) Jan 20 2007 I wish we'd gone the const by default way before releasing 1.0, no matte...
- Lars Ivar Igesund (11/20) Jan 24 2007 This is true, by offputting this particular discussion for a resolution,...
- Lars Ivar Igesund (9/27) Jan 24 2007 I suppose I should read the _whole_ thread prior to posting such opinion...
- Chris Nicholson-Sauls (30/58) Jan 20 2007 Just a thought: B) can be accomplished with an Interface that only expos...
- Bill Baxter (9/80) Jan 20 2007 That's pretty clever, but it's an awful lot superfluous cruft for
- Andrei Alexandrescu (See Website for Email) (36/64) Jan 20 2007 I think this is very interesting because it opens the door to a new
- Chris Nicholson-Sauls (22/97) Jan 20 2007 Its not a bad idea, although with a little more flavor added to Tuples, ...
- Andrei Alexandrescu (See Website for Email) (35/97) Jan 20 2007 Let's see:
- Bruno Medeiros (7/23) Jan 21 2007 What's wrong with 'lazy'? 'lazy' is not a storage class. Its behavior
- Dave (12/87) Jan 20 2007 But you could still trivially and legally subvert this 'const'-ness:
- Andrei Alexandrescu (See Website For Email) (29/78) Jan 20 2007 Casts can break any design, so it's not phasing anyone off that they can...
- Dave (14/103) Jan 20 2007 I wish Walter would have agreed with many of us that it (casts breaking ...
- Andrei Alexandrescu (See Website For Email) (30/62) Jan 20 2007 My statement about design-breaking casts needs qualifying. Walter (and I...
- Dave (11/82) Jan 20 2007 I think the general understanding was that D const would not be C++'s co...
- Andrei Alexandrescu (See Website For Email) (18/28) Jan 20 2007 OS support is often available, but in too coarse a manner (page-level
- Walter Bright (24/62) Jan 21 2007 My objections to the C++ const are:
- Andrei Alexandrescu (See Website For Email) (34/58) Jan 21 2007 Let me point out that D's current handling of the storage classes is
- Frits van Bommel (51/68) Jan 21 2007 "in", "out", "inout" and "lazy" aren't storage classes.
- Kirk McDonald (8/26) Jan 21 2007 I posted something a little while ago on d.D.bugs regarding the spec's
- Andrei Alexandrescu (See Website For Email) (9/33) Jan 21 2007 This is more proof that it's highly unclear what they are. The article
- Tom S (23/41) Jan 21 2007 Well, thanks for the attack, but it was an attempt to suggest a fix, as
- Andrei Alexandrescu (See Website For Email) (34/61) Jan 21 2007 You are not being attacked. Not even the post was attacked, but rather
- Bill Baxter (22/40) Jan 21 2007 Cool that seems like an interesting idea for this and const.
- Andrei Alexandrescu (See Website For Email) (17/53) Jan 22 2007 Both. And also for future deduction of storage classes by template
- Miles (73/81) Jan 23 2007 You mean, const_cast<> and other casts? If so, this is no reason to say
- Andrei Alexandrescu (See Website For Email) (12/48) Jan 23 2007 Wrong. It is a definitional issue that confuses even experts.
- Miles (25/34) Jan 24 2007 This is almost prejudice against const. You broke my message in this
- Andrei Alexandrescu (See Website For Email) (10/27) Jan 24 2007 I broke the message because it was long, having some content that I
- janderson (18/30) Jan 21 2007 I couldn't agree more on this point. I'm glad we have someone like
- Jarrett Billingsley (6/9) Jan 20 2007 "Andrei Alexandrescu (See Website for Email)"
- Andrei Alexandrescu (See Website For Email) (6/16) Jan 20 2007 It's all too simple to protest against various features that other
- Jarrett Billingsley (10/14) Jan 20 2007 "Andrei Alexandrescu (See Website For Email)"
- Andrei Alexandrescu (See Website For Email) (28/40) Jan 20 2007 It happens to us all to associate some bad consequences with some
- Lionello Lunesu (12/14) Jan 21 2007 "Andrei Alexandrescu (See Website for Email)"
- Andrei Alexandrescu (See Website For Email) (17/32) Jan 21 2007 Except that it's deduced. Think of it the other way: if const is an
- Lionello Lunesu (19/31) Jan 21 2007 "Andrei Alexandrescu (See Website For Email)"
- Bill Baxter (13/17) Jan 21 2007 Well, the pressure, if any, was from you, the way I recall it. There
- Andrei Alexandrescu (See Website For Email) (9/30) Jan 21 2007 The feature I suggested was conversion from expression to delegate,
- Bill Baxter (3/38) Jan 21 2007 Great. Sounds promising. Looking forward to hearing the details.
- Frits van Bommel (28/39) Jan 21 2007 Correct me if I'm wrong, but I think none of this stuff will work for
- Andrei Alexandrescu (See Website For Email) (6/22) Jan 21 2007 That is correct. Interface functions, nonfinal methods, and
- Frits van Bommel (6/38) Jan 21 2007 If explicit annotation is possible then this could work for this case.
- Andrei Alexandrescu (See Website For Email) (57/73) Jan 21 2007 I meant - yes, you are right.
-
Walter Bright
(16/76)
Jan 21 2007
Five hours of coffee and scribbling
. Neither of us is thoroughly - Bruno Medeiros (13/111) Jan 22 2007 So the keyword for the "read-only view" will be 'const' too? It will
- Andrei Alexandrescu (See Website For Email) (11/55) Jan 22 2007 That is correct. An easy way to remember both is: final refers to a name...
- Bruno Medeiros (15/53) Jan 23 2007 Indeed. :( Won't this cause must woe afterwards, so that it should be
- Frits van Bommel (12/26) Jan 23 2007 I'd guess it would be an object with only read-only views (i.e. one that...
- Andrei Alexandrescu (See Website For Email) (8/38) Jan 23 2007 Deletion of pointers to const data has always been a sticky issue in
- Frits van Bommel (9/26) Jan 23 2007 But as I mentioned, there will only be const references to const
- Andrei Alexandrescu (See Website For Email) (18/45) Jan 23 2007 I disagree. If you want to disable the GC, that doesn't mean you can't
- Frits van Bommel (7/38) Jan 23 2007 Yeah, I guess it doesn't look that bad. Besides, I always keep the GC on...
- Andrei Alexandrescu (See Website For Email) (25/39) Jan 23 2007 This is actually a great point. In the case of classes, is const a
- kris (2/51) Jan 23 2007 All sounds good to me. Bring it on :D
- Frits van Bommel (27/68) Jan 23 2007 Let's talk about references for a bit first. You have "normal" (mutable)...
- Andrei Alexandrescu (See Website For Email) (11/43) Jan 23 2007 Yah.
- Chris Nicholson-Sauls (10/56) Jan 23 2007 So then I ask, why have const constructors/destructors? What, given an ...
- Andrei Alexandrescu (See Website For Email) (8/15) Jan 23 2007 A constructor might take different decisions on caching, preallocating
- Chris Nicholson-Sauls (19/38) Jan 23 2007 Okay, granted. (Const instances coming from a pool rather than newly cr...
- Andrei Alexandrescu (See Website For Email) (7/35) Jan 23 2007 Yes. Also, it should be notice that a const constructor could escape a
- Bruno Medeiros (10/30) Jan 23 2007 I was about to ask the same that Chris N.S. did. So how about a
- Andrei Alexandrescu (See Website For Email) (7/35) Jan 23 2007 If a const constructor does things a different way than the non-const
- Andrei Alexandrescu (See Website For Email) (6/35) Jan 23 2007 It's an object that has no accessible mutating view.
- Lionello Lunesu (4/41) Jan 22 2007 'const' is not always a compile time constant. A const in function or in...
- kris (7/57) Jan 23 2007 const int also takes space in an executable. It apparenltly becomes a
- Sean Kelly (14/56) Jan 23 2007 I ran into this problem recently:
- Frits van Bommel (24/46) Jan 23 2007 I don't see how that is a bug in anything but your code. The error
- Sean Kelly (5/55) Jan 23 2007 Very weird. I had no idea that constants could be defined on a
- Frits van Bommel (4/7) Jan 23 2007 Per-instance constants isn't something most people use regularly, I
- Bruno Medeiros (10/55) Jan 23 2007 I know that very well, I mentioned it ahead in the:
- Bruno Medeiros (5/13) Jan 23 2007 Hum... what if I want to override such functions? Will D even allow it?
- Andrei Alexandrescu (See Website For Email) (5/16) Jan 23 2007 Technically that's a template function, so you can't override it. But
- Walter Bright (2/16) Jan 20 2007 All of them accomplish A.
- Bill Baxter (6/26) Jan 20 2007 Ha ha. You almost had me for a minute there. You cut and pasted bits
- James Dennett (27/45) Jan 20 2007 This is an oft-repeated point, but it also misses the users'
- Bill Baxter (21/39) Jan 21 2007 Exactly. The main reason I want to be able to have something like a
- Bill Baxter (28/47) Jan 20 2007 This exists in C++ with const reference parameters. But I've not heard
- Christian Kamm (12/20) Jan 20 2007 Okay, I can see that this could result in unexpected behaviour. The idea...
- Dave (7/33) Jan 20 2007 I agree, I don't like the thought of us having to use inout as an optimi...
- Bill Baxter (7/19) Jan 20 2007 So you're suggesting byref would be a synonym for inout? And the
- Dave (10/31) Jan 20 2007 'inout' doesn't necessarily specify 'byref'. But, in C++ if you specify ...
- janderson (85/114) Jan 20 2007 I think you mean more like:
- Andrei Alexandrescu (See Website For Email) (5/29) Jan 20 2007 I meant what I wrote, although your example is just as relevant. If the
- janderson (3/35) Jan 20 2007 If all the function does is read the values. How can aliasing be a prob...
- Andrei Alexandrescu (See Website For Email) (5/41) Jan 20 2007 Probably I misunderstood some of what was said earlier in the
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
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
janderson wrote:Dave wrote:Also: Something like ned-malloc in the standard lib. -JoelWhat'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
janderson wrote:janderson wrote: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. -JoelDave wrote:Also: Something like ned-malloc in the standard lib. -Joel
Jan 21 2007
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
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
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
Christian Kamm 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. AndreiWhat'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?
Jan 19 2007
Andrei Alexandrescu (See Website For Email) wrote:Christian Kamm 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". --bbNo, and for a good reason: foo(LargeStruct s1, LargeStruct s2) { ... } ... LargeStruct s; foo(s, s); Nobody would enjoy hidden aliasing of s1 and s2. AndreiWhat'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?
Jan 19 2007
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
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
Walter Bright wrote:Andrei Alexandrescu (See Website For Email) wrote: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... --bbNo, 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 20 2007
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
Walter Bright wrote:Bill Baxter wrote: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.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.And none of those accomplishes a or b. --bbIn 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
Bill Baxter wrote:Walter Bright wrote:Ah the great const debate. I have a feeling that this discussion will be a major part of D 2.0.Bill Baxter wrote: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.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.And none of those accomplishes a or b. --bbIn 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
"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
Rioshin an'Harthen wrote:"Kyle Furlong" <kylefurlong gmail.com> wrote: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 TangoAh 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 24 2007
Lars Ivar Igesund wrote:Rioshin an'Harthen wrote: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"Kyle Furlong" <kylefurlong gmail.com> wrote: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.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 24 2007
Bill Baxter wrote:Walter Bright wrote: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-SaulsBill Baxter wrote: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.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.And none of those accomplishes a or b. --bbIn 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
Chris Nicholson-Sauls wrote:Bill Baxter wrote: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. --bbWalter Bright wrote: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-SaulsBill Baxter wrote: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.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.And none of those accomplishes a or b. --bbIn 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
Chris Nicholson-Sauls wrote:Just a thought: B) can be accomplished with an Interface that only exposes gettors.[snip]<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
Andrei Alexandrescu (See Website for Email) wrote:Chris Nicholson-Sauls wrote: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-SaulsJust a thought: B) can be accomplished with an Interface that only exposes gettors.[snip]<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
Chris Nicholson-Sauls wrote:Andrei Alexandrescu (See Website for Email) wrote: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).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. AndreiIts 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; } } }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
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). AndreiWhat's wrong with 'lazy'? 'lazy' is not a storage class. Its behavior 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
Andrei Alexandrescu (See Website for Email) wrote:Chris Nicholson-Sauls wrote: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).Just a thought: B) can be accomplished with an Interface that only exposes gettors.[snip]<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.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
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.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).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.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. AndreiThe 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.
Jan 20 2007
Andrei Alexandrescu (See Website For Email) wrote:Dave wrote: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!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.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);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."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 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.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.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.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
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. 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.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!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. AndreiI 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.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.
Jan 20 2007
Andrei Alexandrescu (See Website For Email) wrote:Dave wrote: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".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.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!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.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.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 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.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.Andrei
Jan 20 2007
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
Dave wrote:Andrei Alexandrescu (See Website For Email) 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) 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.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".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.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.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
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 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. AndreiI 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
Andrei Alexandrescu (See Website For Email) wrote:Walter Bright 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 ----- 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?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.
Jan 21 2007
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
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
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
Tom S wrote:Andrei Alexandrescu (See Website For Email) wrote: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'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.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.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.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
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: neverWhat 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
Bill Baxter wrote:Andrei Alexandrescu (See Website For Email) wrote:The syntax was described and is the same as using regular lvalues.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?Both. And also for future deduction of storage classes by template functions. Basically the compiler assumes eager evaluation by default.* deduced automatically: neverWhat does this mean, exactly? Is it speaking of a template parameter? Or for things like 'auto foo = <expr>'?That is the plan. Although probably it's a mistake.* 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?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
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 aidNo, 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 generatorNo 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 declarationsSorry, 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
Miles wrote:Walter Bright wrote:[snip]Wrong. It is a definitional issue that confuses even experts.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.That's pretty limiting; you can't define something a la STL begin() that returns different objects depending on the object's mutability.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.3) it's ugly and litters declarationsSorry, but it is your personal opinion. This doesn't make const bad.Sorry, this is simplistic and incomplete. A member function might want to return whatever, including two unrelated types depending on constness. Andrei4) 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.
Jan 23 2007
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
Miles wrote:Andrei Alexandrescu (See Website For Email) wrote: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.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.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. AndreiLittering 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.
Jan 24 2007
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
"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
Jarrett Billingsley wrote:"Andrei Alexandrescu (See Website for Email)" <SeeWebsiteForEmail erdani.org> wrote in message news:eotmt2$2fru$3 digitaldaemon.com...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.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?Call it collocation: Walter and I live in the same city :o). Andrei
Jan 20 2007
"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
Jarrett Billingsley wrote:"Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail erdani.org> wrote in message news:45B2A3AE.20408 erdani.org...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. AndreiIt'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?
Jan 20 2007
"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
Lionello Lunesu wrote:"Andrei Alexandrescu (See Website for Email)" <SeeWebsiteForEmail erdani.org> wrote in message news:eotmt2$2fru$3 digitaldaemon.com...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.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 : SMe 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
"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
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
Bill Baxter wrote:Andrei Alexandrescu (See Website For Email) wrote: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.) AndreiA 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. :-)
Jan 21 2007
Andrei Alexandrescu (See Website For Email) wrote:Bill Baxter wrote:Great. Sounds promising. Looking forward to hearing the details. --bbAndrei Alexandrescu (See Website For Email) wrote: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.) AndreiA 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. :-)
Jan 21 2007
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
Frits van Bommel wrote:Andrei Alexandrescu (See Website for Email) wrote: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. AndreiThe 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"...
Jan 21 2007
Andrei Alexandrescu (See Website For Email) wrote:Frits van Bommel wrote: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:Andrei Alexandrescu (See Website for Email) wrote: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.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"...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
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: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. AndreiThat is correct. Interface functions, nonfinal methods, and declared-only functions must be annotated manually.
Jan 21 2007
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
Andrei Alexandrescu (See Website For Email) wrote:Frits van Bommel wrote:So the keyword for the "read-only view" will be 'const' too? It will share space with the 'const' as in "compile time constant" ?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.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
Bruno Medeiros wrote:Andrei Alexandrescu (See Website For Email) wrote:Yes. This is arguably a weak point of the design.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" ?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).* 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)I think const can be considered a storage class even when it is "borrowed" from data that originally was not const.* 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?class Widget { ... } auto a = new Widget; // invokes this() auto b = new const Widget; // invokes this(const)() Andrei* 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?
Jan 22 2007
Andrei Alexandrescu (See Website For Email) wrote:Bruno Medeiros wrote: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) // ?Andrei Alexandrescu (See Website For Email) wrote:Yes. This is arguably a weak point of the design.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" ?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#Dclass Widget { ... } auto a = new Widget; // invokes this() auto b = new const Widget; // invokes this(const)() Andrei* 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?
Jan 23 2007
Bruno Medeiros wrote:Andrei Alexandrescu (See Website For Email) wrote: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).Bruno Medeiros wrote: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?)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)()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
Frits van Bommel wrote:Bruno Medeiros wrote:That is correct.Andrei Alexandrescu (See Website For Email) wrote: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).Bruno Medeiros wrote: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?)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)()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. AndreiAnd 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
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 ;) ). 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.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.
Jan 23 2007
Frits van Bommel wrote:Andrei Alexandrescu (See Website For Email) wrote: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.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 ;) ).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.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
Andrei Alexandrescu (See Website For Email) wrote:Frits van Bommel wrote:<pedantic> I said const *objects*, not const references. </pedantic>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.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.LOL. (Not sure it applies, but LOL none the less :) )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."
Jan 23 2007
Frits van Bommel wrote:Andrei Alexandrescu (See Website For Email) wrote: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. AndreiFrits van Bommel wrote:<pedantic> I said const *objects*, not const references. </pedantic>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:
Jan 23 2007
Andrei Alexandrescu (See Website For Email) wrote:Frits van Bommel wrote:All sounds good to me. Bring it on :DAndrei Alexandrescu (See Website For Email) wrote: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.Frits van Bommel wrote:<pedantic> I said const *objects*, not const references. </pedantic>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:
Jan 23 2007
Andrei Alexandrescu (See Website For Email) wrote:Frits van Bommel wrote: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.Andrei Alexandrescu (See Website For Email) wrote: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.Frits van Bommel wrote:<pedantic> I said const *objects*, not const references. </pedantic>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: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
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.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. AndreiThe 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.
Jan 23 2007
Andrei Alexandrescu (See Website For Email) wrote:Frits van Bommel 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: Different from this: 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-SaulsAndrei Alexandrescu (See Website For Email) wrote: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. AndreiFrits van Bommel wrote:<pedantic> I said const *objects*, not const references. </pedantic>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:
Jan 23 2007
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.In the latter case you are robbing the constructor from a little piece of information. Things will still work. Andrei
Jan 23 2007
Andrei Alexandrescu (See Website For Email) wrote:Chris Nicholson-Sauls wrote: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.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.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-SaulsIn the latter case you are robbing the constructor from a little piece of information. Things will still work. Andrei
Jan 23 2007
Chris Nicholson-Sauls wrote:Andrei Alexandrescu (See Website For Email) wrote: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.Chris Nicholson-Sauls wrote: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 } }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.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
Andrei Alexandrescu (See Website For Email) wrote:Chris Nicholson-Sauls wrote: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#DSo 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.In the latter case you are robbing the constructor from a little piece of information. Things will still work. Andrei
Jan 23 2007
Bruno Medeiros wrote:Andrei Alexandrescu (See Website For Email) wrote: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. AndreiChris Nicholson-Sauls wrote: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.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.In the latter case you are robbing the constructor from a little piece of information. Things will still work. Andrei
Jan 23 2007
Bruno Medeiros wrote:Andrei Alexandrescu (See Website For Email) wrote:Yes, the intent is that const is part of the type and that it is detectable.Bruno Medeiros wrote: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) // ?Andrei Alexandrescu (See Website For Email) wrote:Yes. This is arguably a weak point of the design.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" ?It's an object that has no accessible mutating view.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?)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
Bruno Medeiros wrote:Andrei Alexandrescu (See Website For Email) 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. L.Frits van Bommel wrote:So the keyword for the "read-only view" will be 'const' too? It will share space with the 'const' as in "compile time constant" ?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:
Jan 22 2007
Lionello Lunesu wrote:Bruno Medeiros wrote: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 APIAndrei Alexandrescu (See Website For Email) 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. L.Frits van Bommel wrote:So the keyword for the "read-only view" will be 'const' too? It will share space with the 'const' as in "compile time constant" ?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:
Jan 23 2007
Lionello Lunesu wrote:Bruno Medeiros wrote: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. SeanAndrei Alexandrescu (See Website For Email) 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.Frits van Bommel wrote:So the keyword for the "read-only view" will be 'const' too? It will share space with the 'const' as in "compile time constant" ?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:
Jan 23 2007
Sean Kelly wrote:Lionello Lunesu wrote: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.'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.
Jan 23 2007
Frits van Bommel wrote:Sean Kelly wrote:Aren't const values implicitly static? I assumed that they were.Lionello Lunesu wrote: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.'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.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
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
Lionello Lunesu wrote:Bruno Medeiros wrote: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#DAndrei Alexandrescu (See Website For Email) 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. L.Frits van Bommel wrote:So the keyword for the "read-only view" will be 'const' too? It will share space with the 'const' as in "compile time constant" ?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:
Jan 23 2007
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
Bruno Medeiros wrote:Andrei Alexandrescu (See Website For Email) wrote: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* 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?
Jan 23 2007
Bill Baxter wrote:Walter Bright wrote:All of them accomplish A.Bill Baxter wrote:And none of those accomplishes a or b.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 valueThere 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
Walter Bright wrote:Bill Baxter wrote: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:Walter Bright wrote:All of them accomplish A.Bill Baxter wrote:And none of those accomplishes a or b.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 valueThere 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 insteadVery 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
Walter Bright wrote:Bill Baxter wrote: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, thoughThe 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.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 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. -- JamesIn 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
James Dennett wrote:Walter Bright wrote: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. :-( --bbBill Baxter wrote: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, thoughThe 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.
Jan 21 2007
Bill Baxter wrote:Walter Bright wrote: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. --bbAndrei 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 ...
Jan 20 2007
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:There's another aliasing case: LargeStruct s; foo(s); ... here some other thread modifies s ...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 valueIf 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
Christian Kamm wrote: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.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:There's another aliasing case: LargeStruct s; foo(s); ... here some other thread modifies s ...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 valueIf inout is used, it doesn't guarantee B) and even worse: I'm lying to anyone reading the function signature. A pointer will also requireadditional 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
Dave wrote:Christian Kamm wrote: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. --bbIf inout is used, it doesn't guarantee B) and even worse: I'm lying to anyone reading the function signature. A pointer will also requireI 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.
Jan 20 2007
Bill Baxter wrote:Dave wrote:'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.Christian Kamm wrote: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.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 requireI 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.--bb
Jan 20 2007
Walter Bright wrote:Andrei Alexandrescu (See Website For Email) wrote: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.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.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
janderson wrote:Walter Bright wrote: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. AndreiAndrei Alexandrescu (See Website For Email) wrote:I think you mean more like: foo(LargeStruct s1, LargeStruct inout s2) { ... } ... LargeStruct s; foo(s, s);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.
Jan 20 2007
Andrei Alexandrescu (See Website For Email) wrote:janderson wrote:If all the function does is read the values. How can aliasing be a problem? -JoelWalter Bright wrote: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. AndreiAndrei Alexandrescu (See Website For Email) wrote:I think you mean more like: foo(LargeStruct s1, LargeStruct inout s2) { ... } ... LargeStruct s; foo(s, s);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.
Jan 20 2007
janderson wrote:Andrei Alexandrescu (See Website For Email) wrote: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) Andreijanderson wrote:If all the function does is read the values. How can aliasing be a problem?Walter Bright wrote: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. AndreiAndrei Alexandrescu (See Website For Email) wrote:I think you mean more like: foo(LargeStruct s1, LargeStruct inout s2) { ... } ... LargeStruct s; foo(s, s);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.
Jan 20 2007