digitalmars.D - Stepping back and looking at constness from another angle.
- Jarrett Billingsley (33/33) Jun 04 2007 I won't lie, I'm not fond of the changes that are being developed. Now ...
- Bill Baxter (61/68) Jun 04 2007 Great question. A little brainstorming look-aside never hurt.
- Charlie (25/105) Jun 04 2007 As far as I can tell 'const' is just a guarantee by the person that
- Jason House (17/21) Jun 04 2007 Honestly, when I first saw in, out, and inout, I thought that was sort
- Bill Baxter (13/35) Jun 04 2007 That's why you'll only have to type 'in' in Walter's latest vision
- Derek Parnell (76/79) Jun 04 2007 I also think that the idea of 'stepping back' is a good idea at this poi...
- Jarrett Billingsley (19/48) Jun 04 2007 I really like that idea. It also implicitly reduces maintenance. If to...
- Bill Baxter (9/19) Jun 04 2007 I suspect the main problem with it is just that it makes a prototype
- Leandro Lucarella (28/69) Jun 05 2007 +1 here too
- Ameer Armaly (12/46) Jun 04 2007 Here's an idea that just occured to me:
- Lionello Lunesu (22/22) Jun 04 2007 Great post, Jarrett!
- David B. Held (130/130) Jun 04 2007 Let me point out a few guiding principles that are worth considering:
- Johan Granberg (17/168) Jun 05 2007 You write a loot about Haskell and how awkward it is to use pure functio...
- Charlie (4/24) Jun 05 2007 I think you've hit it on the head here, the problem is the lack of const...
- Bill Baxter (19/28) Jun 05 2007 Current D doesn't have this property.
- Derek Parnell (104/239) Jun 05 2007 Well, function parameters are not 'variables', they are data you entrust...
- Regan Heath (13/162) Jun 06 2007 I am looking forward to const, and invariant. I am in agreement with yo...
- David B. Held (14/22) Jun 06 2007 Ok, Bruno probably won't like this, but he makes a great point about
- Derek Parnell (15/16) Jun 06 2007 No they're not. Sure, currently they look /like/ a tuple of types, but
- Walter Bright (5/19) Jun 06 2007 Function argument lists are expression tuples, and function parameter
- Bruno Medeiros (19/44) Jun 11 2007 Just for the record, I'm not an unconditional fan of orthogonality
- Don Clugston (5/48) Jun 11 2007 Agreed, but I wonder if the second aspect is realistic. There are severa...
- Reiner Pope (14/14) Jun 06 2007 Another problem with const by default came to mind. Consider the
- Denton Cockburn (5/22) Jun 06 2007 Again, I'm also trying to understand this, knowing that things change a
- Don Clugston (3/21) Jun 06 2007 I would expect 'ref' be implicitly non-const. Especially since 'ref' is ...
- Reiner Pope (7/15) Jun 04 2007 I may be off track, but I think that scope could also bring some easy
- Frits van Bommel (5/8) Jun 05 2007 A good point: Delegates to 'scope' entities[1] should be 'scope'
- Walter Bright (27/30) Jun 04 2007 I answered a similar post from Charlie, but since the discussion seems
- James Dennett (30/34) Jun 05 2007 But there are things in C++ that don't work if you
- Sean Kelly (33/52) Jun 05 2007 'const' is essentially a contract that should guarantee certain behavior...
- Frits van Bommel (4/6) Jun 05 2007 IIRC Tango is actually where I got the idea to code these sort of
- Sean Kelly (3/5) Jun 05 2007 Sorry about that.
- Don Clugston (10/29) Jun 05 2007 That's my experience too. Among curly-brace languages, only C++ has cons...
- Bill Baxter (5/9) Jun 05 2007 What do you mean? T[new] would be an array which you can both change
- Don Clugston (2/13) Jun 05 2007 What is a const T[new] ?
- Carlos Santander (27/35) Jun 05 2007 Not only for those new to the language, but for less experienced program...
- Ary Manzana (20/64) Jun 05 2007 I also get very confused about const, final, invariant... and I think
- Carlos Santander (34/40) Jun 05 2007 structures are needed in order to "execute" them. I've never seen an alg...
- Ary Manzana (11/21) Jun 06 2007 Yes, you're right. Your analogy is great. I think I was too tired
- eao197 (48/51) Jun 07 2007 re =
- Ary Manzana (15/75) Jun 07 2007 Thanks for the examples, I liked them. Now I see where is the problem,
- eao197 (15/19) Jun 07 2007 =
- Leandro Lucarella (10/11) Jun 06 2007 Syntax is getting so obscure... I'm scared.
- Walter Bright (18/23) Jun 07 2007 Think of invariant(type) this way: imagine a template that creates a
- Carlos Santander (4/38) Jun 07 2007 I guess that's clear, but what does it mean when there're no parentheses...
- Walter Bright (4/5) Jun 07 2007 When it has no parentheses, it behaves as a "storage class" rather than
- Carlos Santander (5/11) Jun 08 2007 And here's where it gets confusing. However, like I said before, I'll un...
- Leandro Lucarella (19/25) Jun 08 2007 I _understand_ the syntax, but it's getting really hard to read the code
- Regan Heath (3/17) Jun 08 2007 Yet another reason for const by default with implicit 'in'. The source ...
- Jeff Nowakowski (28/31) Jun 05 2007 People have:
- Bill Baxter (6/25) Jun 05 2007 Wow. That's too funny. Good find!
- Bruno Medeiros (8/12) Jun 09 2007 Well, I guess Java's lack of const is somewhat mitigated by having
- Robert Fraser (8/48) Jun 05 2007 I agree, though for a slightly different reason (now that I'm 99% sure o...
- Craig Black (15/15) Jun 05 2007 Guess I should put in my 2 cents also, since there are so many people wh...
- Thomas Kuehne (25/34) Jun 06 2007 -----BEGIN PGP SIGNED MESSAGE-----
- BCS (5/15) Jun 06 2007 Actually, I think that this should go the other way, the language should...
- Regan Heath (4/28) Jun 07 2007 I thought so too. As Don says; 'ref' is an alias of 'inout'. It's ver
- Lionello Lunesu (6/14) Jun 08 2007 Actually, it might be a nice distinction to keep both 'inout' and 'ref',...
- Deewiant (7/19) Jun 08 2007 'ref' meaning what C++'s 'const foo&' is how I originally understood it,...
- Regan Heath (13/32) Jun 08 2007 I liked 'inout' better than 'ref' too. I was against the initial propos...
- Xinok (7/10) Jun 08 2007 Main reason I can think of is function overloading. This is an
- Deewiant (6/48) Jun 08 2007 We'd lose the ability to overload a function with both foo(A) and foo(A*...
- Deewiant (10/21) Jun 08 2007 Didn't take long to come up with a case:
- Bill Baxter (16/29) Jun 08 2007 Good idea. I'd be perfectly happy to do that with structs. Now that
- Regan Heath (3/35) Jun 08 2007 True, but calling with an int* should be allowed? I'm wondering if that...
- Derek Parnell (10/40) Jun 08 2007 I have to disagree Regan. 'ref' might mean that the passing mechanism is
- Regan Heath (3/40) Jun 08 2007 You're dead right. In fact 'ref' (a passing mechanic) is/has replaced '...
I won't lie, I'm not fond of the changes that are being developed. Now that even my dear, sweet dynamic arrays are having their syntax changed to something like int[new], I figured I'd voice my concerns over it. I feel (and this coming from someone who has never truly learned const-correctness, this may not mean much) that all the new const stuff is doing is the same thing that C and C++ do. Yes, there's more granularity by having const, scope, final, and invariant, and even with a new type of unresizeable array. But it doesn't feel like a D solution for it. As an example, I'll give the C preprocessor. D didn't inherit it; instead, it stepped back, looked at what it was used for, and designed the most common features into the language, giving them first-class benefits that the CPP can never give. We have version statements, debug statements, constants and enums, a proper module system, templates, mixins (templates and string), and soon even AST-based macros, all of which blow the CPP out of the water in terms of parsing and features. What I'm asking you more seasoned programmers, and those more experienced with const-correctness to do, is to do something similar here. Step back, and have a look at what constness is for. What problems does it solve? Is it necessarily the best way to solve them? More importantly, what problems does it introduce? Remember, how the language _looks_ is just as important as the features it has. After all, what do you want to look at for hours on end each day if you use it at your job? I realize that this is late in the game. I feel like Walter and company have probably already ironed out most of the issues and semantics of the new syntaxes, if not begun to work on them. I don't want to discredit all they've done at all. I'm just wondering if it's the best thing for the language. (And one final concern is -- what if this is implemented and it's a mess? A total disaster? Will Walter just say "sorry guys, we messed up" and take it out? Or will he, having put in so much time and effort, just say "well you're stuck with it"? This is a huge feature. This isn't regex match expressions. If it gets put into the language, it might not be taken back out. Especially since this is pretty much _the_ D 2.0 feature.)
Jun 04 2007
Jarrett Billingsley wrote:What I'm asking you more seasoned programmers, and those more experienced with const-correctness to do, is to do something similar here. Step back, and have a look at what constness is for. What problems does it solve? Is it necessarily the best way to solve them? More importantly, what problems does it introduce? Remember, how the language _looks_ is just as important as the features it has. After all, what do you want to look at for hours on end each day if you use it at your job?Great question. A little brainstorming look-aside never hurt. The problem in a nutshell is to make it possible to prevent variables from changing when you don't want them too. Knowing they won't change makes large systems easier to understand. want const for is to make it possible to pass a big struct efficiently to a function and know for certain that the caller's value won't be modified. Taking a cue from the removal of 'virtual' from D, one thing D could do would be to automatically choose to pass variables by reference based on performance criteria. Have the compiler let me write: Vec4f add(Vec4f rbga1, Vec4f rgba2) { } and not have to worry about whether it would be more efficient to pass a const ref there. People pass const refs all over the place in C++ not because they want reference semantics, but because they want reference performace. So let the compiler worry about the optimization, not me. I'll specify the semantics, and you, Mr. Compiler, figure out how to make it efficient. That's basically what Walter's done with virtual, which is great. One less low-level thing for me to worry about. The crux there is that I really don't care how it gets passed under the hood as long as it's efficient and behaves as if it were passed by value. Of course that doesn't help object parameters which are references to begin with. There I don't see much way around specifying 'const' in the signature. I'm honestly not too excited by the invariant flavor of const. I guess it's useful for parallel code. But I don't write a lot of parallel code. Of course we're probably all going to be writing a lot more parallel code in the upcoming years, so I'm willing to go along with the "eat your peas" message believing it will be good for me in the long run. :-) In terms of a real, comprehensive answer to your question (as opposed to just a trick for struct parameters), I think the two main alternatives you see are 1) functional programing languages 2) the ostrich approach. In pure functional languages, everything is immutable, so problem solved. It's the compiler's job to try to make it efficient. But approach 2), put your head in the sand and ignore it, is also quite popular. Especially for dynamic scripting langauges. Python for instance has no const at all. I can't remember seeing const in any of the scripting languages I've used. I don't think any of Perl or PHP or Ruby or Tcl or Javascript has it. C obviously got along fine for a long time without C++-like const semantics (not sure if that's changed in C99 though). I wonder what Pascal/Delphi do? I guess all I can say is that once you get used to using const like in C++, you get an uneasy feeling any time you see a function signature like: foo(MyClass a) You think "whoa -- maybe that's going to modify the object I pass in". Whereas with a single 'const' there you can know that it's not *supposed* to modify 'a', and if it does (via casts or other subversions) then it's probably a bug in the library. You can do that with comments, too, but the compiler won't tell you when your comment is out of sync with what the code actually does, so the comment has less chance of reflecting reality than a 'const' in the declaration does. The question is whether the end result is really worth the effort. I don't really know. I had thought so, but it really has to be weighed against the inconveniences introduced by the new design for const, which we can't be sure of right now. But I think it's a very valid question. If the safety line preventing you from falling off the cliff gets you all tangled up and in the end ties one of your hands behind your back, then clearly things have gone too far. --bb
Jun 04 2007
As far as I can tell 'const' is just a guarantee by the person that wrote the function that said function won't modify your variable , and if it tries the compiler won't let it and tell you so. In C++ I think most people always pass by const reference, and like Bill said once you see a function signature that doesn't include const you get a little panicked, maybe because not having const for parameters doesn't tell you wether or not it _will_ modify you're variable, it only says it _might_. So maybe we need something that guarantees that it will be modified instead, and have const the default ( has this already been discussed ? ). That way there wont be 'will it or wont it' , it will be clear that this function intends to modify the variable . I think its more often the case that a function doesn't intend to modify your variable. Or maybe , instead of having the function guarantee the const'ness, have a keyword for parameters when calling a function, that tells the compiler , 'if this function tries to modify this variable, then error out and tell me about it' , something like: foo( const myInstance ); Or the opposite is probably better, make it always error out when the function tries to modify it, and only when you expect it to do the modifying you pass it : foo( mod myInstance ); ( Ignore the syntax, just the idea ). Just brainstorming, Charlie Bill Baxter wrote:Jarrett Billingsley wrote:What I'm asking you more seasoned programmers, and those more experienced with const-correctness to do, is to do something similar here. Step back, and have a look at what constness is for. What problems does it solve? Is it necessarily the best way to solve them? More importantly, what problems does it introduce? Remember, how the language _looks_ is just as important as the features it has. After all, what do you want to look at for hours on end each day if you use it at your job?Great question. A little brainstorming look-aside never hurt. The problem in a nutshell is to make it possible to prevent variables from changing when you don't want them too. Knowing they won't change makes large systems easier to understand. want const for is to make it possible to pass a big struct efficiently to a function and know for certain that the caller's value won't be modified. Taking a cue from the removal of 'virtual' from D, one thing D could do would be to automatically choose to pass variables by reference based on performance criteria. Have the compiler let me write: Vec4f add(Vec4f rbga1, Vec4f rgba2) { } and not have to worry about whether it would be more efficient to pass a const ref there. People pass const refs all over the place in C++ not because they want reference semantics, but because they want reference performace. So let the compiler worry about the optimization, not me. I'll specify the semantics, and you, Mr. Compiler, figure out how to make it efficient. That's basically what Walter's done with virtual, which is great. One less low-level thing for me to worry about. The crux there is that I really don't care how it gets passed under the hood as long as it's efficient and behaves as if it were passed by value. Of course that doesn't help object parameters which are references to begin with. There I don't see much way around specifying 'const' in the signature. I'm honestly not too excited by the invariant flavor of const. I guess it's useful for parallel code. But I don't write a lot of parallel code. Of course we're probably all going to be writing a lot more parallel code in the upcoming years, so I'm willing to go along with the "eat your peas" message believing it will be good for me in the long run. :-) In terms of a real, comprehensive answer to your question (as opposed to just a trick for struct parameters), I think the two main alternatives you see are 1) functional programing languages 2) the ostrich approach. In pure functional languages, everything is immutable, so problem solved. It's the compiler's job to try to make it efficient. But approach 2), put your head in the sand and ignore it, is also quite popular. Especially for dynamic scripting langauges. Python for instance has no const at all. I can't remember seeing const in any of the scripting languages I've used. I don't think any of Perl or PHP or Ruby or Tcl or Javascript has it. C obviously got along fine for a long time without C++-like const semantics (not sure if that's changed in C99 though). I wonder what Pascal/Delphi do? I guess all I can say is that once you get used to using const like in C++, you get an uneasy feeling any time you see a function signature like: foo(MyClass a) You think "whoa -- maybe that's going to modify the object I pass in". Whereas with a single 'const' there you can know that it's not *supposed* to modify 'a', and if it does (via casts or other subversions) then it's probably a bug in the library. You can do that with comments, too, but the compiler won't tell you when your comment is out of sync with what the code actually does, so the comment has less chance of reflecting reality than a 'const' in the declaration does. The question is whether the end result is really worth the effort. I don't really know. I had thought so, but it really has to be weighed against the inconveniences introduced by the new design for const, which we can't be sure of right now. But I think it's a very valid question. If the safety line preventing you from falling off the cliff gets you all tangled up and in the end ties one of your hands behind your back, then clearly things have gone too far. --bb
Jun 04 2007
Charlie wrote:Or the opposite is probably better, make it always error out when the function tries to modify it, and only when you expect it to do the modifying you pass it : foo( mod myInstance ); ( Ignore the syntax, just the idea ).Honestly, when I first saw in, out, and inout, I thought that was sort of the idea... with in being the default. I considered in to be "const&" or "const" depending on efficiency and inout to be "&". (Excuse the mixing of C++-like terms). I liked the simplicity of it. Then I discovered that objects are really pointers and the description really only applies to the pointer for them. That's when I had my "whoa, this is really wrong" moment. The lack of const was one of my big gripes with the D language. I'm glad to see that they're working on it. I'm sad that I don't see any documentation on what the new design will (or won't) be. Personally, I think "const final scope" seems like a mouthful. I'll never want to type it. Even typing two keywords seems like a lot to me... kind of why I liked the simplicity of in, out, and inout. If in meant what I thought it meant, I probably would have few gripes. So far, I don't yet have an appreciation for all the complexity of the new const system.
Jun 04 2007
Jason House wrote:Charlie wrote:That's why you'll only have to type 'in' in Walter's latest vision posted here. 'in' on a parameter will be synonymous with 'const scope final' so you won't have to think about it. But you will have to use 'in' explicitly if that's what you want. I suspect if you want things to const, you'll make objects in and structs in ref. But as I said before it would be really cool if structs could just be 'in' too without me having to guess whether Vec2f is big enough that it should be passed by reference or not.Or the opposite is probably better, make it always error out when the function tries to modify it, and only when you expect it to do the modifying you pass it : foo( mod myInstance ); ( Ignore the syntax, just the idea ).Honestly, when I first saw in, out, and inout, I thought that was sort of the idea... with in being the default. I considered in to be "const&" or "const" depending on efficiency and inout to be "&". (Excuse the mixing of C++-like terms). I liked the simplicity of it. Then I discovered that objects are really pointers and the description really only applies to the pointer for them. That's when I had my "whoa, this is really wrong" moment. The lack of const was one of my big gripes with the D language. I'm glad to see that they're working on it. I'm sad that I don't see any documentation on what the new design will (or won't) be. Personally, I think "const final scope" seems like a mouthful. I'll never want to type it.Even typing two keywords seems like a lot to me... kind of why I liked the simplicity of in, out, and inout. If in meant what I thought it meant, I probably would have few gripes.That's kind of the feeling I had too originally. I expected that (like the simplicity of no virtual in D) that the compiler was going to figure out the best way to pass parameters to match the stated intent (in/out/inout). But I think that's basically what we'll have.
Jun 04 2007
On Mon, 04 Jun 2007 20:23:41 -0500, Charlie wrote:As far as I can tell 'const' is just a guarantee by the person that wrote the function that said function won't modify your variable , and if it tries the compiler won't let it and tell you so.I also think that the idea of 'stepping back' is a good idea at this point. And the concept of defining the problem before its solution is also a good one. In this case, the problems seem to be ... (a) How can the source code reader be sure that the data passed to a function, represented by a parameter, will not have been modified once that function returns to the caller, and without having access to the function's source code? (b) How can a function writer ensure that the compiler will detect or prevent inadvertent attempts to modify data that was not intended to be changed by the algorithm designer? If we assume that the majority of the time, functions are not attempting to modify passed data then the default, and presumably simpler, syntax should reflect that passed data is designed to be immutable. Thus it follows that in order to allow function writers to modify passed data, the parameter signature explicitly should say that it is permitted. This would make the code reader's task easier to as the absence of a 'mutable' modifier in the parameter signature signals a 'safe' function for that parameter. Of course, this assumes that the Type of data being passed is potentially mutable anyway. Given that some data types are by their very nature immutable, I would suggest that calling a function, which allows changes to a parameter, with an immutable data type should be a compiler-detected error. Immutable data types include literals, Walter's new 'slice' type, invariant types and variables declared as 'final'. A complication arises with parameters that are references to data rather than data themselves. In this case we need to be able to independently specify whether its the reference and/or the data that is allowed to be changed. Also, I believe that the type of access permitted to a function needs to be divorced from the mechanism used to pass the parameter data, as they are independent concepts. So what would happen if there was a key word that could be used to specify that a function has permission to modify a parameter(data)? This key word would be neither a storage class nor a type, but used solely to grant permission to a function to change passed data. For the sake of this discussion, I'll use 'upd' to represent the key word above but it could be anything better. // For reference types (such as classes and variable-sized arrays) ... foo(RT c); // Can't change 'c' or its members foo(RT upd c); // Can change 'c' but not its members foo(upd RT c); // Can't change 'c' but can change its members. foo(upd RT upd c);// Can change both 'c' and its members. // For value types (which include structs and fixed-size arrays) ... foo(VT d); // Can't change d's contents foo(VT upd d); // Can change d's contents foo(upd VT d); // Can't change d's contents (upd is ignored) foo(upd VT upd d);// Can change d's contents (first upd is ignored) We probably could also cater for the situation in which changes made by a function are not to be returned to the caller. That is, the function is given permission to play with the data passed, but any changes it does make are never returned to the caller. I think this is similar to Walter's 'scope' idea for parameters. However I'd prefer a more specific looking keyword, such as 'local'. foo(local char[] pText); This example above permits foo() to modify the data referred to by 'pText' but not pText itself /and/ ensure that any modifications are not returned to the caller. This is really just a convenience syntax and is not strictly required as the function can always just take a local copy of the parameter data. With respect to the parameter passing mechanism, I would expect that the compiler would select the most efficient method given the access, storage and type information. However, maybe we can let the function writer override the compiler's selection by using the 'ref' and 'val' key words to force 'pass by reference' and 'pass by value' respectively. 'ref' would mean that the function receives the address of the parameter and the compiler does all the de-referencing as required. In the case of reference types, the function actually receives the address of a reference and not the reference itself. Well then, how far away from the ideal have I strayed? -- Derek (skype: derek.j.parnell) Melbourne, Australia "Justice for David Hicks!" 5/06/2007 11:40:15 AM
Jun 04 2007
"Bill Baxter" <dnewsgroup billbaxter.com> wrote in message news:f42c51$1ap8$1 digitalmars.com...want const for is to make it possible to pass a big struct efficiently to a function and know for certain that the caller's value won't be modified. Taking a cue from the removal of 'virtual' from D, one thing D could do would be to automatically choose to pass variables by reference based on performance criteria. Have the compiler let me write: Vec4f add(Vec4f rbga1, Vec4f rgba2) { } and not have to worry about whether it would be more efficient to pass a const ref there. People pass const refs all over the place in C++ not because they want reference semantics, but because they want reference performace. So let the compiler worry about the optimization, not me. I'll specify the semantics, and you, Mr. Compiler, figure out how to make it efficient. That's basically what Walter's done with virtual, which is great. One less low-level thing for me to worry about. The crux there is that I really don't care how it gets passed under the hood as long as it's efficient and behaves as if it were passed by value.I really like that idea. It also implicitly reduces maintenance. If today, passing a 16-byte struct on the stack is more efficient to pass by reference, or if a struct doesn't fit in a register, but five years from now that's no longer the case, we don't have to go around removing all the refs to improve performance. The compiler knows what's better.In terms of a real, comprehensive answer to your question (as opposed to just a trick for struct parameters), I think the two main alternatives you see are 1) functional programing languages 2) the ostrich approach. In pure functional languages, everything is immutable, so problem solved. It's the compiler's job to try to make it efficient. But approach 2), put your head in the sand and ignore it, is also quite popular.Aww, but I like the ostrich approach :) I'm actually more interested in the const-by-default idea, which is a lot like functional languages. I'd be interested to see how it would pan out. It's also a _new idea_ -- nothing like this has ever been done in a non-functional language AFAIK, and we'd be breaking new ground. Who knows, it could be what we've been looking for. And if it isn't -- hey, we've got the current const ideas to fall back on.The question is whether the end result is really worth the effort. I don't really know. I had thought so, but it really has to be weighed against the inconveniences introduced by the new design for const, which we can't be sure of right now. But I think it's a very valid question. If the safety line preventing you from falling off the cliff gets you all tangled up and in the end ties one of your hands behind your back, then clearly things have gone too far.Which is why I think const-by-default would be interesting to try. If you really think about it, there are a lot of things that usually don't change, and a lot of methods that shouldn't have any side effects. Explicitly marking code which _does_ have side effects seems to make more sense than explicitly marking that which _doesn't_.
Jun 04 2007
Jarrett Billingsley wrote:"Bill Baxter" <dnewsgroup billbaxter.com> wrote in message news:f42c51$1ap8$1 digitalmars.com...I suspect the main problem with it is just that it makes a prototype alone insufficient for determining how to call a function. Probably not an insurmountable problem, but probably difficult enough to make it not worth it. For instance if you could have meta-info about functions in obj files and modify the linker to use it, then probably it's no prob. But that's a big if. And it still doesn't give you reference object constness. --bbThe crux there is that I really don't care how it gets passed under the hood as long as it's efficient and behaves as if it were passed by value.I really like that idea. It also implicitly reduces maintenance. If today, passing a 16-byte struct on the stack is more efficient to pass by reference, or if a struct doesn't fit in a register, but five years from now that's no longer the case, we don't have to go around removing all the refs to improve performance. The compiler knows what's better.
Jun 04 2007
Jarrett Billingsley, el 4 de junio a las 23:01 me escribiste:"Bill Baxter" <dnewsgroup billbaxter.com> wrote in message news:f42c51$1ap8$1 digitalmars.com...+1want const for is to make it possible to pass a big struct efficiently to a function and know for certain that the caller's value won't be modified. Taking a cue from the removal of 'virtual' from D, one thing D could do would be to automatically choose to pass variables by reference based on performance criteria. Have the compiler let me write: Vec4f add(Vec4f rbga1, Vec4f rgba2) { } and not have to worry about whether it would be more efficient to pass a const ref there. People pass const refs all over the place in C++ not because they want reference semantics, but because they want reference performace. So let the compiler worry about the optimization, not me. I'll specify the semantics, and you, Mr. Compiler, figure out how to make it efficient. That's basically what Walter's done with virtual, which is great. One less low-level thing for me to worry about. The crux there is that I really don't care how it gets passed under the hood as long as it's efficient and behaves as if it were passed by value.I really like that idea. It also implicitly reduces maintenance. If today, passing a 16-byte struct on the stack is more efficient to pass by reference, or if a struct doesn't fit in a register, but five years from now that's no longer the case, we don't have to go around removing all the refs to improve performance. The compiler knows what's better.+1 here too passed, like: inplace_sort(ref some_container); // call This could be some more work to write, but much more clearer to read (you know explicitly that some_container could be modified). If you omit the keyword when calling, you have to be take a look at inplace_sort() definition to see if it can modify some_container. Maybe 'ref' is not a great keyword, because it could be used for performance too (which is not the idea). 'mutable' maybe it's a better keyword, but a little long. I think this could improve auto-documentating code (and prevent bugs). -- LUCA - Leandro Lucarella - Usando Debian GNU/Linux Sid - GNU Generation ------------------------------------------------------------------------ E-Mail / JID: luca lugmen.org.ar GPG Fingerprint: D9E1 4545 0F4B 7928 E82C 375D 4B02 0FE0 B08B 4FB2 GPG Key: gpg --keyserver pks.lugmen.org.ar --recv-keys B08B4FB2 ------------------------------------------------------------------------ Americans, let's face it: We've been a spoiled country for a long time. Do you know what the number one health risk in America is? Obesity, obesity! They say we're in the middle of an obesity epidemic. An epidemic, like it's polio. Like we'll be telling our grand kids about it one day: The Great Obesity Epidemic of 2004. "How'd you get through it grandpa?" "Oh, it was horrible Johnny, but there was cheesecake and pork chops everywhere."In terms of a real, comprehensive answer to your question (as opposed to just a trick for struct parameters), I think the two main alternatives you see are 1) functional programing languages 2) the ostrich approach. In pure functional languages, everything is immutable, so problem solved. It's the compiler's job to try to make it efficient. But approach 2), put your head in the sand and ignore it, is also quite popular.Aww, but I like the ostrich approach :) I'm actually more interested in the const-by-default idea, which is a lot like functional languages. I'd be interested to see how it would pan out. It's also a _new idea_ -- nothing like this has ever been done in a non-functional language AFAIK, and we'd be breaking new ground. Who knows, it could be what we've been looking for. And if it isn't -- hey, we've got the current const ideas to fall back on.
Jun 05 2007
"Jarrett Billingsley" <kb3ctd2 yahoo.com> wrote in message news:f428hd$15qa$1 digitalmars.com...I won't lie, I'm not fond of the changes that are being developed. Now that even my dear, sweet dynamic arrays are having their syntax changed to something like int[new], I figured I'd voice my concerns over it. I feel (and this coming from someone who has never truly learned const-correctness, this may not mean much) that all the new const stuff is doing is the same thing that C and C++ do. Yes, there's more granularity by having const, scope, final, and invariant, and even with a new type of unresizeable array. But it doesn't feel like a D solution for it. As an example, I'll give the C preprocessor. D didn't inherit it; instead, it stepped back, looked at what it was used for, and designed the most common features into the language, giving them first-class benefits that the CPP can never give. We have version statements, debug statements, constants and enums, a proper module system, templates, mixins (templates and string), and soon even AST-based macros, all of which blow the CPP out of the water in terms of parsing and features. What I'm asking you more seasoned programmers, and those more experienced with const-correctness to do, is to do something similar here. Step back, and have a look at what constness is for. What problems does it solve? Is it necessarily the best way to solve them? More importantly, what problems does it introduce? Remember, how the language _looks_ is just as important as the features it has. After all, what do you want to look at for hours on end each day if you use it at your job? I realize that this is late in the game. I feel like Walter and company have probably already ironed out most of the issues and semantics of the new syntaxes, if not begun to work on them. I don't want to discredit all they've done at all. I'm just wondering if it's the best thing for the language. (And one final concern is -- what if this is implemented and it's a mess? A total disaster? Will Walter just say "sorry guys, we messed up" and take it out? Or will he, having put in so much time and effort, just say "well you're stuck with it"? This is a huge feature. This isn't regex match expressions. If it gets put into the language, it might not be taken back out. Especially since this is pretty much _the_ D 2.0 feature.)Here's an idea that just occured to me: How about making standalone const mean that whatever the type is it and whatever it possibly points to are const and add qualifiers like const(ref) and const(data) for use with arrays and classes to point out exactly we want to be constant? Here are examples: 1. const char[] c; // pointer and data are const 2. const(ref) char[] c; // the pointer is const but the data is not 3. const(data) char[] c; // the pointer can change, but whatever it points to can't be changed Ideas?
Jun 04 2007
Great post, Jarrett! I'm having similar feelings towards these new changes. D will get so much more complicated, that we really should ask ourselves "is it worth it?". As for "the problem that const is trying to solve", this reminds me of a video I saw from Scott Meyers on Google Video: http://video.google.com/videoplay?docid=-4728145737208991310&q=red+code Here's the abstract: "C++ compilers allow non-const code to call const code, but going the other way requires a cast. In this talk, Scott describes an approach he's been pursuing to generalize this notion to arbitrary criteria. For example, thread-safe code should only call other thread-safe code (unless you explicitly permit it on a per-call basis). Ditto for exception-safe code, code not "contaminated" by some open source license, or any other constraint you choose. The approach is based on template metaprogramming (TMP), and the implementation uses the Boost metaprogramming library (Boost.MPL), so constraint violations are, wherever possible, detected during compilation." The idea is, I think, that you'd basically want many compile-time contracts, like "const" of "reentrant" or whatever. (The other use of const is what Bill described: the omnipresent "const type&", kind-of like a "inref" or something, but it would be even better if the compiler would take care of it.) L.
Jun 04 2007
Let me point out a few guiding principles that are worth considering: 1) The more code you break, the more benefit you must offer 2) Orthogonality is good; exceptions and corner cases bad While the idea of making const the default sounds appealing, why limit it to function parameters? Why not treat variables as const-by-default also? Very functional indeed. Unfortunately, not very D-like. D could have builtin lists, just like functional languages. Instead, it has arrays. Guess why? Arrays are smaller and faster. D chooses performance. Those arrays could be immutable by default, but they aren't. Why not? Try to time the performance of quicksort in Haskell, and compare it to C's qsort(), and you will soon understand the power of mutable arrays. Haskell's version certainly looks better, but C's burns the pants off all takers, because it's about as fast as hand-coded, fully optimized, no-holds-barred assembly. D is a multi-paradigm language, like C++, but let's not forget that "imperative" is a paradigm, and that machine language itself is imperative. And since D aims to let you get as close to the machine as possible, it makes sense that the default for *variables* is mutability. A corollary to principle 2) above is: 3) Function parameters should have the same syntax as local declarations Why? Well, why not? Why should users have to think hard about whether a particular syntax is only legal for parameters or not? While certain qualifiers like 'in' and 'out' must necessarily be exceptions, they idea is that they provide sufficient convenience to justify their existence. Beyond that, you should be able to look at a function parameter list and say: "Each of those parameter declarations could be pulled out into a local declaration". If we agree that is a powerful principle that makes the language easier to understand, then we see why it is not acceptable for function arguments to default to const while locals do not. And if we agree that D is an imperative language before all else, because it is a *systems* language, then we see why it is not acceptable for locals to default to const. This leads us to conclude that mutability should indeed be the default, and that constness should be indicated explicitly. However, I think 'in' as a synonym for constness is a very nice compromise that gives just about the shortest possible abbreviation for 'const' that you could imagine (short of something silly like type sigils). One person mentioned that Perl doesn't have 'const'. In fact, that is only half-true. There is a Perl pragma called 'constant' which is used like thus: use constant PI => 3.14159; Unfortunately, being a pragma, the symbol PI does not interpolate like a scalar, making certain uses of it very awkward. Larry recognized both that this was unacceptable and that constants are indeed very useful, and decided to give Perl 6 first-class constants. Also, Andrei likes to make much of the fact that Perl, like Java, has immutable strings. Who would have guessed? Perl is pretty much the poster child of string manipulation and obscure do-anything code semantics, so unless you went looking for it, very few people would guess that strings in Perl are actually immutable, but they are. Why is that? Well, it's because making strings CONST makes it very easy to guarantee that there is no improper aliasing mixed with string mutation. Java programmers that need mutable strings use StringBuilder, and then convert to a String when they're done. It's such a powerful idiom that the xxxBuilder pattern is quite popular in the Java standard library now. How could D get immutable strings? Well, we could try it with const, but if you look closely, 'const' doesn't mean "this array can't change". It means: "You can't change this array through this const reference, but somebody else can." That is, 'const' isn't strong enough to get you immutable strings. For that, you need something stronger. You need 'invariant'. Only invariant guarantees that nobody holds a mutable reference to your object, and thus, you are free to alias the object all you like, quite safely. D, being a *systems language*, concerned with *performance*, will take any performance-enhancing substance available, and 'invariant' is one of the best drugs on the market (eat your heart out, Floyd). So could we live with 'invariant' and dump 'const'? Well, we could, but it wouldn't result in a very useful language. Because then we wouldn't be able to say: "I want the implementor of this function to have a read-only view on my precious mutable data which I need to be able to update in place." The only way you could do that is to make a copy, which defeats the whole point of 'invariant'. Are things starting to become a little more clear? So if we agree that the Functional Paradigm is A Good Thing, and part of why it's a good thing is Purity, then it follows that D can gain some of the benefits of FP by implementing limited purity, which is what const/invariant are. Why is purity a good thing? It's very simple. Functions, in the mathematical sense of a map from A to B, are fairly easy to understand. State machines, on the other hand, are much much harder to understand. Adding variables to a state machine typically increases the number of states geometrically, and the number of state transitions exponentially. Adding constants, on the other hand, does not increase the number of states, nor the number of transitions, which means that you literally get an exponential increase in simplicity by making as many things immutable as possible. And that's why 'const' in general is A Good Thing(TM). Of course, the cost of all this is some additional syntactic burden. But consider the opposite case: pure functional languages trying to implement mutable state. In our case, we only need to sprinkle a keyword here and there. For Haskell, you need to *define a Monad*. In case you didn't know, a Monad is a structure with three distinct operations which are fairly non-trivial for a novice to define. On the other hand, most novices can add 'const' to a declaration without any assistance whatsoever. This is most likely why even most functional languages are not pure. If you think that you are willing to take the plunge and make all your programs const by default, consider all the libraries that D can and does link to, and consider the effect of const-by-default on that codebase. Even if you are willing to use a Haskell-like discipline yourself, most D code is not, and would take a huge effort to fix up. One final observation is this: the benefit of const is something of an emergent property that is sensitive to scale. That is, for small programs, the cost outweighs the benefit. But as a program grows from a few modules to several libraries and tens or hundreds of modules with many thousands of lines of code written by teams of programmers, the benefit of const pays back its cost through every possible interaction of programmers. Why is that? It's because 'const' is both documentation and a promise. It tells another programmer what to expect, and it promises to follow through on that created expectation. Since the number of possible interactions among programmers is factorial (super-exponential!) in the number of coders, the benefit of const soon becomes apparent with even a modestly-sized team. If you have reservations about const on your personal project, try to imagine writing a very large project with a group and ask yourself how much you trust the other programmers (imagine the group stepped out of Dilbert, and not your favorite startup). While inferring 'const' is certainly possible (and several papers have been written about how to do it), it neglects the documentation aspect of 'const'--the contractual aspect of it. This is something that only becomes apparent at large scales, but once you reach those scales, it becomes very visible very quickly. If there is some magic const-bullet that would make everyone happy, I'm sure Walter is the first person in the D community that wants to know about it. Unfortunately, this is a very hard problem, and solutions to hard problems always involve some kind of compromise. Of course, the problem with compromises is that they are guaranteed to piss off everyone at least a little bit (look at immigration reform for a good example). That doesn't mean it isn't a good compromise; on the contrary, it probably means it is. Dave
Jun 04 2007
David B. Held wrote:Let me point out a few guiding principles that are worth considering: 1) The more code you break, the more benefit you must offer 2) Orthogonality is good; exceptions and corner cases bad While the idea of making const the default sounds appealing, why limit it to function parameters? Why not treat variables as const-by-default also? Very functional indeed. Unfortunately, not very D-like. D could have builtin lists, just like functional languages. Instead, it has arrays. Guess why? Arrays are smaller and faster. D chooses performance. Those arrays could be immutable by default, but they aren't. Why not? Try to time the performance of quicksort in Haskell, and compare it to C's qsort(), and you will soon understand the power of mutable arrays. Haskell's version certainly looks better, but C's burns the pants off all takers, because it's about as fast as hand-coded, fully optimized, no-holds-barred assembly. D is a multi-paradigm language, like C++, but let's not forget that "imperative" is a paradigm, and that machine language itself is imperative. And since D aims to let you get as close to the machine as possible, it makes sense that the default for *variables* is mutability. A corollary to principle 2) above is: 3) Function parameters should have the same syntax as local declarations Why? Well, why not? Why should users have to think hard about whether a particular syntax is only legal for parameters or not? While certain qualifiers like 'in' and 'out' must necessarily be exceptions, they idea is that they provide sufficient convenience to justify their existence. Beyond that, you should be able to look at a function parameter list and say: "Each of those parameter declarations could be pulled out into a local declaration". If we agree that is a powerful principle that makes the language easier to understand, then we see why it is not acceptable for function arguments to default to const while locals do not. And if we agree that D is an imperative language before all else, because it is a *systems* language, then we see why it is not acceptable for locals to default to const. This leads us to conclude that mutability should indeed be the default, and that constness should be indicated explicitly. However, I think 'in' as a synonym for constness is a very nice compromise that gives just about the shortest possible abbreviation for 'const' that you could imagine (short of something silly like type sigils). One person mentioned that Perl doesn't have 'const'. In fact, that is only half-true. There is a Perl pragma called 'constant' which is used like thus: use constant PI => 3.14159; Unfortunately, being a pragma, the symbol PI does not interpolate like a scalar, making certain uses of it very awkward. Larry recognized both that this was unacceptable and that constants are indeed very useful, and decided to give Perl 6 first-class constants. Also, Andrei likes to make much of the fact that Perl, like Java, has immutable strings. Who would have guessed? Perl is pretty much the poster child of string manipulation and obscure do-anything code semantics, so unless you went looking for it, very few people would guess that strings in Perl are actually immutable, but they are. Why is that? Well, it's because making strings CONST makes it very easy to guarantee that there is no improper aliasing mixed with string mutation. Java programmers that need mutable strings use StringBuilder, and then convert to a String when they're done. It's such a powerful idiom that the xxxBuilder pattern is quite popular in the Java standard library now. How could D get immutable strings? Well, we could try it with const, but if you look closely, 'const' doesn't mean "this array can't change". It means: "You can't change this array through this const reference, but somebody else can." That is, 'const' isn't strong enough to get you immutable strings. For that, you need something stronger. You need 'invariant'. Only invariant guarantees that nobody holds a mutable reference to your object, and thus, you are free to alias the object all you like, quite safely. D, being a *systems language*, concerned with *performance*, will take any performance-enhancing substance available, and 'invariant' is one of the best drugs on the market (eat your heart out, Floyd). So could we live with 'invariant' and dump 'const'? Well, we could, but it wouldn't result in a very useful language. Because then we wouldn't be able to say: "I want the implementor of this function to have a read-only view on my precious mutable data which I need to be able to update in place." The only way you could do that is to make a copy, which defeats the whole point of 'invariant'. Are things starting to become a little more clear? So if we agree that the Functional Paradigm is A Good Thing, and part of why it's a good thing is Purity, then it follows that D can gain some of the benefits of FP by implementing limited purity, which is what const/invariant are. Why is purity a good thing? It's very simple. Functions, in the mathematical sense of a map from A to B, are fairly easy to understand. State machines, on the other hand, are much much harder to understand. Adding variables to a state machine typically increases the number of states geometrically, and the number of state transitions exponentially. Adding constants, on the other hand, does not increase the number of states, nor the number of transitions, which means that you literally get an exponential increase in simplicity by making as many things immutable as possible. And that's why 'const' in general is A Good Thing(TM). Of course, the cost of all this is some additional syntactic burden. But consider the opposite case: pure functional languages trying to implement mutable state. In our case, we only need to sprinkle a keyword here and there. For Haskell, you need to *define a Monad*. In case you didn't know, a Monad is a structure with three distinct operations which are fairly non-trivial for a novice to define. On the other hand, most novices can add 'const' to a declaration without any assistance whatsoever. This is most likely why even most functional languages are not pure. If you think that you are willing to take the plunge and make all your programs const by default, consider all the libraries that D can and does link to, and consider the effect of const-by-default on that codebase. Even if you are willing to use a Haskell-like discipline yourself, most D code is not, and would take a huge effort to fix up. One final observation is this: the benefit of const is something of an emergent property that is sensitive to scale. That is, for small programs, the cost outweighs the benefit. But as a program grows from a few modules to several libraries and tens or hundreds of modules with many thousands of lines of code written by teams of programmers, the benefit of const pays back its cost through every possible interaction of programmers. Why is that? It's because 'const' is both documentation and a promise. It tells another programmer what to expect, and it promises to follow through on that created expectation. Since the number of possible interactions among programmers is factorial (super-exponential!) in the number of coders, the benefit of const soon becomes apparent with even a modestly-sized team. If you have reservations about const on your personal project, try to imagine writing a very large project with a group and ask yourself how much you trust the other programmers (imagine the group stepped out of Dilbert, and not your favorite startup). While inferring 'const' is certainly possible (and several papers have been written about how to do it), it neglects the documentation aspect of 'const'--the contractual aspect of it. This is something that only becomes apparent at large scales, but once you reach those scales, it becomes very visible very quickly. If there is some magic const-bullet that would make everyone happy, I'm sure Walter is the first person in the D community that wants to know about it. Unfortunately, this is a very hard problem, and solutions to hard problems always involve some kind of compromise. Of course, the problem with compromises is that they are guaranteed to piss off everyone at least a little bit (look at immigration reform for a good example). That doesn't mean it isn't a good compromise; on the contrary, it probably means it is. DaveYou write a loot about Haskell and how awkward it is to use pure functional constructs in a systems language like D. But I have not heard anyone say that they want to make D pure in any way. Even functional languages like Standard ML have reference parameters (mutable) and arrays (also mutable) and what was suggested for D was even less extream, if you are OK whit writing const all over the place how can writing mutable all over the place be any worse. The idea is that if you forget to declare something const the compiler cant catch you but if you forget to declare mutable the compiler will complain if you try to change it. I don't know about your programs but when I code in imperative languages like C D or Java most variables are never changed they are defined locally as the result of some computation or function call and read one ore more time during the rest of the function, granted globals and class members are a little different but I don't think they are more common than function parameters and if you are OK whit writing const next to each function parameter writing mutable next to some member variables should not be a huge burden.
Jun 05 2007
Johan Granberg Wrote:David B. Held wrote:<snip>Let me point out a few guiding principles that are worth considering:You write a loot about Haskell and how awkward it is to use pure functional constructs in a systems language like D. But I have not heard anyone say that they want to make D pure in any way. Even functional languages like Standard ML have reference parameters (mutable) and arrays (also mutable) and what was suggested for D was even less extream, if you are OK whit writing const all over the place how can writing mutable all over the place be any worse. The idea is that if you forget to declare something const the compiler cant catch you but if you forget to declare mutable the compiler will complain if you try to change it. I don't know about your programs but when I code in imperative languages like C D or Java most variables are never changed they are defined locally as the result of some computation or function call and read one ore more time during the rest of the function, granted globals and class members are a little different but I don't think they are more common than function parameters and if you are OK whit writing const next to each function parameter writing mutable next to some member variables should not be a huge burden.I think you've hit it on the head here, the problem is the lack of const on a parameter tells you nothing, where as if const was the default ( can we agree that most parameters are usually passed const ? ) ,then thet lack of a keyword means its safe, and the presence of a keyword means the function plans to modify it. Its 100% clear what the function plans to do, you can't get that with const . Charlie
Jun 05 2007
David B. Held wrote: Most of your post was great, but this bit seems to be a bit off to me:3) Function parameters should have the same syntax as local declarations Why? Well, why not? Why should users have to think hard about whether a particular syntax is only legal for parameters or not? While certain qualifiers like 'in' and 'out' must necessarily be exceptions, they idea is that they provide sufficient convenience to justify their existence. Beyond that, you should be able to look at a function parameter list and say: "Each of those parameter declarations could be pulled out into a local declaration".Current D doesn't have this property. void foo(out int bar) { out int baz; // oops 'out' syntax only works on parameters! } nor does C++: void foo(int x[5]) { // sort of works but likely not what user expected int y[5]; } Maybe you were arguing in the abstract, though, that it would be nice if it were true? I don't really see why it should be the case though. I don't have to think hard now about whether or not I can use 'out' on a local variable. As long as the rules make sense users won't have to think that hard. It's pretty easy to remember something like "parameters aren't usually modified so they're const default, locals are so they're mutable default". Probably even easier to remember and more intuitive than the differences between structs and classes. --bb
Jun 05 2007
On Mon, 04 Jun 2007 22:05:13 -0700, David B. Held wrote:Let me point out a few guiding principles that are worth considering: 1) The more code you break, the more benefit you must offer 2) Orthogonality is good; exceptions and corner cases badYep and yep.While the idea of making const the default sounds appealing, why limit it to function parameters? Why not treat variables as const-by-default also?Well, function parameters are not 'variables', they are data you entrust to functions. And 'variables' that are not mutable are not /variables/, they are constants. In brief, function-parameters and identifiers-in-scope are not the same beasties. However, I get your point. Why not have local data immutable by default? I would say its because nearly all local data is designed to be modified and the immutable local stuff is not as common. Whereas most data passed to functions is designed to be immutable. According to Walter's experience (as I read it) he is finding that to be true.Very functional indeed. Unfortunately, not very D-like.A what exactly is "D-like"?D could have builtin lists, just like functional languages. Instead, it has arrays. Guess why? Arrays are smaller and faster. D chooses performance. Those arrays could be immutable by default, but they aren't. Why not? Try to time the performance of quicksort in Haskell, and compare it to C's qsort(), and you will soon understand the power of mutable arrays. Haskell's version certainly looks better, but C's burns the pants off all takers, because it's about as fast as hand-coded, fully optimized, no-holds-barred assembly.I think the key phrase here is "immutable by default", meaning that a quicksort in D could be coded with mutable arrays even if the default array usage is immutable. Haskell doesn't give you that option, no?D is a multi-paradigm language, like C++, but let's not forget that "imperative" is a paradigm, and that machine language itself is imperative. And since D aims to let you get as close to the machine as possible, it makes sense that the default for *variables* is mutability.As "D is multi-paradigm" and "imperative is a paradigm" and "machine language is imperative" and "D aims to [be close] to the machine" therefore "it makes sense that the default for *variables* is mutability" Huh? This doesn't seem to be a logical conclusion at all. Can you expand or elaborate? Doesn't 'default' mean something like 'unless otherwise specified'? In other words, if the default is "X" it does not follow that "not X" is therefore prohibited, which seems to be behind some of your arguments.A corollary to principle 2) above is: 3) Function parameters should have the same syntax as local declarationsWhy? They are not the same things. For example ... void Foo(int x) { int y; . . . } Most people reading this would assume that 'x' is not going to be modified by Foo and that 'y' is going to be modified by Foo. In general, data passed to functions is not modified by the function - but there are exceptions, and in general local declarations are modified by the function - but there are exceptions.Why? Well, why not? Why should users have to think hard about whether a particular syntax is only legal for parameters or not?Why 'legal'? Are we talking about 'default' access modifiers rather than what syntax is allowed? void Foo(upd int x) { const int y = 42; . . . } Above, the default modifiers have been replaced by explicit modifiers. Now the reader knows that 'x' could be modified by Foo and 'y' would be modified by Foo.While certain qualifiers like 'in' and 'out' must necessarily be exceptions, they idea is that they provide sufficient convenience to justify their existence. Beyond that, you should be able to look at a function parameter list and say: "Each of those parameter declarations could be pulled out into a local declaration".One still can. But I think you mean "pulled out" without changing the declaration source text. If one is doing such refactoring then one needs to re-examine the code anyway so text changes are to be expected, I believe.If we agree that is a powerful principle that makes the language easier to understand, then we see why it is not acceptable for function arguments to default to const while locals do not.Actually I don't. I do agree that orthogonality is a good principle for programming languages to follow, but not if it makes languages harder to understand. I also work with the Euphoria programming language. It is strict with respect to parameters - any change to parameter data made by a function is never returned to the caller. In other words, the caller can always know that whatever data they pass to a function will not be changed by that function. In Euphoria, parameters are immutable and local declarations are mutable. This is never been a concept that has been hard to grasp by Euphoria practitioners.And if we agree that D is an imperative language before all else, because it is a *systems* language, then we see why it is not acceptable for locals to default to const.I still don't follow this line of 'logic'. We are talking about a compiler that can make intelligent choices and we are talking about 'defaults'. But I agree for other reasons that local declarations should be mutable by default.This leads us to conclude that mutability should indeed be the default, and that constness should be indicated explicitly.... for local declarations.However, I think 'in' as a synonym for constness is a very nice compromise that gives just about the shortest possible abbreviation for 'const' that you could imagine (short of something silly like type sigils).An even shorter abbreviation for the concept of immutable function parameters is to omit the access modifier. And if that is more common than mutable function parameters it must mean less typing and less for code readers to process.One person mentioned that Perl doesn't have 'const'. In fact, that is only half-true. There is a Perl pragma called 'constant' which is used like thus: use constant PI => 3.14159; Unfortunately, being a pragma, the symbol PI does not interpolate like a scalar, making certain uses of it very awkward. Larry recognized both that this was unacceptable and that constants are indeed very useful, and decided to give Perl 6 first-class constants. Also, Andrei likes to make much of the fact that Perl, like Java, has immutable strings. Who would have guessed? Perl is pretty much the poster child of string manipulation and obscure do-anything code semantics, so unless you went looking for it, very few people would guess that strings in Perl are actually immutable, but they are. Why is that? Well, it's because making strings CONST makes it very easy to guarantee that there is no improper aliasing mixed with string mutation. Java programmers that need mutable strings use StringBuilder, and then convert to a String when they're done. It's such a powerful idiom that the xxxBuilder pattern is quite popular in the Java standard library now. How could D get immutable strings? Well, we could try it with const, but if you look closely, 'const' doesn't mean "this array can't change". It means: "You can't change this array through this const reference, but somebody else can." That is, 'const' isn't strong enough to get you immutable strings. For that, you need something stronger. You need 'invariant'. Only invariant guarantees that nobody holds a mutable reference to your object, and thus, you are free to alias the object all you like, quite safely. D, being a *systems language*, concerned with *performance*, will take any performance-enhancing substance available, and 'invariant' is one of the best drugs on the market (eat your heart out, Floyd). So could we live with 'invariant' and dump 'const'? Well, we could, but it wouldn't result in a very useful language. Because then we wouldn't be able to say: "I want the implementor of this function to have a read-only view on my precious mutable data which I need to be able to update in place." The only way you could do that is to make a copy, which defeats the whole point of 'invariant'. Are things starting to become a little more clear?Not at all <G> Now you seem to be contradicting your earlier arguments. It now seems that you are saying that sometimes default immutable local declarations are a good thing. (N.B. I'm trying to avoid using the terms 'const' and 'invariant' because these have specific meanings depending on which language one is talking about.) With respect to strings above, it seems you are saying that we need a mechanism to identifier those strings (arrays?) that are always immutable during the life of a program, and those accesses to strings (arrays?) that are not given permission to change them. One is talking about any and every access to the data (invariant) and the other is talking about specific accesses to data (const).So if we agree that the Functional Paradigm is A Good Thing, and part of why it's a good thing is Purity, then it follows that D can gain some of the benefits of FP by implementing limited purity, which is what const/invariant are. Why is purity a good thing? It's very simple. Functions, in the mathematical sense of a map from A to B, are fairly easy to understand. State machines, on the other hand, are much much harder to understand. Adding variables to a state machine typically increases the number of states geometrically, and the number of state transitions exponentially. Adding constants, on the other hand, does not increase the number of states, nor the number of transitions, which means that you literally get an exponential increase in simplicity by making as many things immutable as possible. And that's why 'const' in general is A Good Thing(TM).I don't think that anyone here has a problem with the concept of limiting the type of access to data that functions can have. The discussion is more about should the default access permission be 'immutable' or 'mutable'?Of course, the cost of all this is some additional syntactic burden. But consider the opposite case: pure functional languages trying to implement mutable state. In our case, we only need to sprinkle a keyword here and there. For Haskell, you need to *define a Monad*. In case you didn't know, a Monad is a structure with three distinct operations which are fairly non-trivial for a novice to define. On the other hand, most novices can add 'const' to a declaration without any assistance whatsoever. This is most likely why even most functional languages are not pure.I think you've gotten off the track a bit. No one is saying that D can only support immutable function parameters, but just that the default access permission should be immutable. If one needed to have mutable function parameters then one would explicitly say so. The talk about Haskell is a red-herring - a straw man argument, if you like.If you think that you are willing to take the plunge and make all your programs const by default, consider all the libraries that D can and does link to, and consider the effect of const-by-default on that codebase. Even if you are willing to use a Haskell-like discipline yourself, most D code is not, and would take a huge effort to fix up.Got any figures to back this assertion up? I don't mind being proven wrong.One final observation is this: the benefit of const is something of an emergent property that is sensitive to scale. That is, for small programs, the cost outweighs the benefit. But as a program grows from a few modules to several libraries and tens or hundreds of modules with many thousands of lines of code written by teams of programmers, the benefit of const pays back its cost through every possible interaction of programmers. Why is that? It's because 'const' is both documentation and a promise. It tells another programmer what to expect, and it promises to follow through on that created expectation. Since the number of possible interactions among programmers is factorial (super-exponential!) in the number of coders, the benefit of const soon becomes apparent with even a modestly-sized team.Are you talking about the key word 'const' or the concept of immutability? If immutability was the default the function contracts still exist. The 'documentation' is still in the source code as one can still determine which function arguments are mutable and which are not.If you have reservations about const on your personal project, try to imagine writing a very large project with a group and ask yourself how much you trust the other programmers (imagine the group stepped out of Dilbert, and not your favorite startup). While inferring 'const' is certainly possible (and several papers have been written about how to do it), it neglects the documentation aspect of 'const'--the contractual aspect of it. This is something that only becomes apparent at large scales, but once you reach those scales, it becomes very visible very quickly.Why is it necessary, in the philosophical sense, to have the key word 'const' explicitly coded to implement the concept of immutable function parameters? -- Derek (skype: derek.j.parnell) Melbourne, Australia "Justice for David Hicks!" 5/06/2007 11:04:11 PM
Jun 05 2007
David B. Held Wrote:Let me point out a few guiding principles that are worth considering: 1) The more code you break, the more benefit you must offer 2) Orthogonality is good; exceptions and corner cases bad While the idea of making const the default sounds appealing, why limit it to function parameters? Why not treat variables as const-by-default also? Very functional indeed. Unfortunately, not very D-like. D could have builtin lists, just like functional languages. Instead, it has arrays. Guess why? Arrays are smaller and faster. D chooses performance. Those arrays could be immutable by default, but they aren't. Why not? Try to time the performance of quicksort in Haskell, and compare it to C's qsort(), and you will soon understand the power of mutable arrays. Haskell's version certainly looks better, but C's burns the pants off all takers, because it's about as fast as hand-coded, fully optimized, no-holds-barred assembly. D is a multi-paradigm language, like C++, but let's not forget that "imperative" is a paradigm, and that machine language itself is imperative. And since D aims to let you get as close to the machine as possible, it makes sense that the default for *variables* is mutability. A corollary to principle 2) above is: 3) Function parameters should have the same syntax as local declarations Why? Well, why not? Why should users have to think hard about whether a particular syntax is only legal for parameters or not? While certain qualifiers like 'in' and 'out' must necessarily be exceptions, they idea is that they provide sufficient convenience to justify their existence. Beyond that, you should be able to look at a function parameter list and say: "Each of those parameter declarations could be pulled out into a local declaration". If we agree that is a powerful principle that makes the language easier to understand, then we see why it is not acceptable for function arguments to default to const while locals do not. And if we agree that D is an imperative language before all else, because it is a *systems* language, then we see why it is not acceptable for locals to default to const. This leads us to conclude that mutability should indeed be the default, and that constness should be indicated explicitly. However, I think 'in' as a synonym for constness is a very nice compromise that gives just about the shortest possible abbreviation for 'const' that you could imagine (short of something silly like type sigils). One person mentioned that Perl doesn't have 'const'. In fact, that is only half-true. There is a Perl pragma called 'constant' which is used like thus: use constant PI => 3.14159; Unfortunately, being a pragma, the symbol PI does not interpolate like a scalar, making certain uses of it very awkward. Larry recognized both that this was unacceptable and that constants are indeed very useful, and decided to give Perl 6 first-class constants. Also, Andrei likes to make much of the fact that Perl, like Java, has immutable strings. Who would have guessed? Perl is pretty much the poster child of string manipulation and obscure do-anything code semantics, so unless you went looking for it, very few people would guess that strings in Perl are actually immutable, but they are. Why is that? Well, it's because making strings CONST makes it very easy to guarantee that there is no improper aliasing mixed with string mutation. Java programmers that need mutable strings use StringBuilder, and then convert to a String when they're done. It's such a powerful idiom that the xxxBuilder pattern is quite popular in the Java standard library now. How could D get immutable strings? Well, we could try it with const, but if you look closely, 'const' doesn't mean "this array can't change". It means: "You can't change this array through this const reference, but somebody else can." That is, 'const' isn't strong enough to get you immutable strings. For that, you need something stronger. You need 'invariant'. Only invariant guarantees that nobody holds a mutable reference to your object, and thus, you are free to alias the object all you like, quite safely. D, being a *systems language*, concerned with *performance*, will take any performance-enhancing substance available, and 'invariant' is one of the best drugs on the market (eat your heart out, Floyd). So could we live with 'invariant' and dump 'const'? Well, we could, but it wouldn't result in a very useful language. Because then we wouldn't be able to say: "I want the implementor of this function to have a read-only view on my precious mutable data which I need to be able to update in place." The only way you could do that is to make a copy, which defeats the whole point of 'invariant'. Are things starting to become a little more clear? So if we agree that the Functional Paradigm is A Good Thing, and part of why it's a good thing is Purity, then it follows that D can gain some of the benefits of FP by implementing limited purity, which is what const/invariant are. Why is purity a good thing? It's very simple. Functions, in the mathematical sense of a map from A to B, are fairly easy to understand. State machines, on the other hand, are much much harder to understand. Adding variables to a state machine typically increases the number of states geometrically, and the number of state transitions exponentially. Adding constants, on the other hand, does not increase the number of states, nor the number of transitions, which means that you literally get an exponential increase in simplicity by making as many things immutable as possible. And that's why 'const' in general is A Good Thing(TM). Of course, the cost of all this is some additional syntactic burden. But consider the opposite case: pure functional languages trying to implement mutable state. In our case, we only need to sprinkle a keyword here and there. For Haskell, you need to *define a Monad*. In case you didn't know, a Monad is a structure with three distinct operations which are fairly non-trivial for a novice to define. On the other hand, most novices can add 'const' to a declaration without any assistance whatsoever. This is most likely why even most functional languages are not pure. If you think that you are willing to take the plunge and make all your programs const by default, consider all the libraries that D can and does link to, and consider the effect of const-by-default on that codebase. Even if you are willing to use a Haskell-like discipline yourself, most D code is not, and would take a huge effort to fix up. One final observation is this: the benefit of const is something of an emergent property that is sensitive to scale. That is, for small programs, the cost outweighs the benefit. But as a program grows from a few modules to several libraries and tens or hundreds of modules with many thousands of lines of code written by teams of programmers, the benefit of const pays back its cost through every possible interaction of programmers. Why is that? It's because 'const' is both documentation and a promise. It tells another programmer what to expect, and it promises to follow through on that created expectation. Since the number of possible interactions among programmers is factorial (super-exponential!) in the number of coders, the benefit of const soon becomes apparent with even a modestly-sized team. If you have reservations about const on your personal project, try to imagine writing a very large project with a group and ask yourself how much you trust the other programmers (imagine the group stepped out of Dilbert, and not your favorite startup). While inferring 'const' is certainly possible (and several papers have been written about how to do it), it neglects the documentation aspect of 'const'--the contractual aspect of it. This is something that only becomes apparent at large scales, but once you reach those scales, it becomes very visible very quickly. If there is some magic const-bullet that would make everyone happy, I'm sure Walter is the first person in the D community that wants to know about it. Unfortunately, this is a very hard problem, and solutions to hard problems always involve some kind of compromise. Of course, the problem with compromises is that they are guaranteed to piss off everyone at least a little bit (look at immigration reform for a good example). That doesn't mean it isn't a good compromise; on the contrary, it probably means it is.I am looking forward to const, and invariant. I am in agreement with you over the merits of having both. However, I would prefer const by default because... 1. apples are apples and oranges are oranges and treating apples like oranges and calling it orthogonality isn't correct. To explain; - local variables are (until manually/explicitly assigned to another such) the 1st and only copy/reference of/to the data. - function parameters are implicitly a copy/alias of data. Once you have aliases you have complication and once you have complication you greatly increase the chances of unexpected changes and therefore bugs. In other words local variables are apples and function parameters are oranges and I don't think we should treat them the same (they're not the same thing and the idea of orthogonality does not apply). Sure, we've been doing so up until now, and it may be easier to learn (because we're used to it) but does that make it right? I don't think so because... 2. The majority of function parameters are meant only to be 'read' from, they are the common case and const by default reflects that. 3. It gives programmers protection immediately without them having to even understand/know that they need it. 4. It will catch bugs in existing code (which explicit 'in' will not do) and it will prevent the sometimes dangerous behaviour of re-using function parameters (accidently even) as if they are local temporaries. 5. Combined with a mutable modifier it gives a rock solid guarantee when viewing the function signature (compared to a missing 'const' in C++ which basically says 'maybe' I'll modify this). Regan
Jun 06 2007
Regan Heath wrote:[...] - local variables are (until manually/explicitly assigned to another such) the 1st and only copy/reference of/to the data. - function parameters are implicitly a copy/alias of data. Once you have aliases you have complication and once you have complication you greatly increase the chances of unexpected changes and therefore bugs. [...]Ok, Bruno probably won't like this, but he makes a great point about orthogonality. The killer argument for orthogonality, IMO, is generics. Remember that function argument lists are type tuples. If we say that function arguments are const by default, that means that a given type tuple has different meanings depending on whether it is used in a function argument context or a non-function argument context. In fact, it could have exactly the *opposite* meaning, which would be the worst kind of non-orthogonality. This may seem like a faraway problem, because I don't believe you can construct functions by assembling type tuples and pasting them onto bits of code. But if we cement in this bit of non-orthogonality, we guarantee that such a feature is either awkward or impossible in the future. Dave
Jun 06 2007
On Wed, 06 Jun 2007 20:45:18 -0700, David B. Held wrote:Remember that function argument lists are type tuples.No they're not. Sure, currently they look /like/ a tuple of types, but that's not what they actually are. This visual similarity is a side-effect of the current syntax. A function argument list is a tuple of course, but each argument in the signature also identifies the passing mechanism and the access mechanism and not only the type. Even in current D one cannot say that a function argument list such as "(inout int, out int, in int)" is a type tuple. -- Derek (skype: derek.j.parnell) Melbourne, Australia "Justice for David Hicks!" 7/06/2007 2:52:04 PM
Jun 06 2007
Derek Parnell wrote:On Wed, 06 Jun 2007 20:45:18 -0700, David B. Held wrote:Function argument lists are expression tuples, and function parameter lists are type tuples.Remember that function argument lists are type tuples.No they're not. Sure, currently they look /like/ a tuple of types, but that's not what they actually are. This visual similarity is a side-effect of the current syntax.A function argument list is a tuple of course, but each argument in the signature also identifies the passing mechanism and the access mechanism and not only the type. Even in current D one cannot say that a function argument list such as "(inout int, out int, in int)" is a type tuple.That's true, and it's one of the big motivators to support 'ref', as then it becomes possible to make the parameter list a type tuple.
Jun 06 2007
David B. Held wrote:Regan Heath wrote:Just for the record, I'm not an unconditional fan of orthogonality (otherwise I'd be using LISP-family languages). There are various syntactic sugars, and also "semantic sugars", that breach orthogonality but are useful and practical enough to justify them. What I am unconditionally against are orthogonality breaches that serve little or no useful purpose, like some of the examples mentioned in the other thread (like static arrays). As for this issue, I guess we all agree that there are two beneficial, but apparently mutually exclusive aspects: * The added safety of const-by-default (or syntactic sugar, depending on how you see it). * The kept orthogonality of declaring function parameters with tuples. Which aspect do I prefer? I'm not sure yet. Frankly, I'm hoping we could come up with a solution that didn't require sacrificing one over the other (or at least minimized that). -- Bruno Medeiros - MSc in CS/E student http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D[...] - local variables are (until manually/explicitly assigned to another such) the 1st and only copy/reference of/to the data. - function parameters are implicitly a copy/alias of data. Once you have aliases you have complication and once you have complication you greatly increase the chances of unexpected changes and therefore bugs. [...]Ok, Bruno probably won't like this, but he makes a great point about orthogonality. The killer argument for orthogonality, IMO, is generics. Remember that function argument lists are type tuples. If we say that function arguments are const by default, that means that a given type tuple has different meanings depending on whether it is used in a function argument context or a non-function argument context. In fact, it could have exactly the *opposite* meaning, which would be the worst kind of non-orthogonality. This may seem like a faraway problem, because I don't believe you can construct functions by assembling type tuples and pasting them onto bits of code. But if we cement in this bit of non-orthogonality, we guarantee that such a feature is either awkward or impossible in the future. Dave
Jun 11 2007
Bruno Medeiros wrote:David B. Held wrote:Agreed, but I wonder if the second aspect is realistic. There are several other issues besides constness. It sounds as though 'ref' variables will be added. But there are others. For example, lazy parameters and non-tuple varags are two function parameters that would need to become valid variable declarations.Regan Heath wrote:Just for the record, I'm not an unconditional fan of orthogonality (otherwise I'd be using LISP-family languages). There are various syntactic sugars, and also "semantic sugars", that breach orthogonality but are useful and practical enough to justify them. What I am unconditionally against are orthogonality breaches that serve little or no useful purpose, like some of the examples mentioned in the other thread (like static arrays). As for this issue, I guess we all agree that there are two beneficial, but apparently mutually exclusive aspects: * The added safety of const-by-default (or syntactic sugar, depending on how you see it). * The kept orthogonality of declaring function parameters with tuples. Which aspect do I prefer? I'm not sure yet. Frankly, I'm hoping we could come up with a solution that didn't require sacrificing one over the other (or at least minimized that).[...] - local variables are (until manually/explicitly assigned to another such) the 1st and only copy/reference of/to the data. - function parameters are implicitly a copy/alias of data. Once you have aliases you have complication and once you have complication you greatly increase the chances of unexpected changes and therefore bugs. [...]Ok, Bruno probably won't like this, but he makes a great point about orthogonality. The killer argument for orthogonality, IMO, is generics. Remember that function argument lists are type tuples. If we say that function arguments are const by default, that means that a given type tuple has different meanings depending on whether it is used in a function argument context or a non-function argument context. In fact, it could have exactly the *opposite* meaning, which would be the worst kind of non-orthogonality. This may seem like a faraway problem, because I don't believe you can construct functions by assembling type tuples and pasting them onto bits of code. But if we cement in this bit of non-orthogonality, we guarantee that such a feature is either awkward or impossible in the future. Dave
Jun 11 2007
Another problem with const by default came to mind. Consider the following code, with const-not-by-default: void foo( ref const(char)[] c) { c = c[1..$]; } I would hate to see this in const-by-default, which would try to wrap a const( ) around the whole type. To counter that, you would probably have to write some mess like void foo ( mutable( ref const(char)[] ) c ) { c = c[1..$]; } -- Reiner
Jun 06 2007
On Thu, 07 Jun 2007 16:25:21 +1000, Reiner Pope wrote:Another problem with const by default came to mind. Consider the following code, with const-not-by-default: void foo( ref const(char)[] c) { c = c[1..$]; } I would hate to see this in const-by-default, which would try to wrap a const( ) around the whole type. To counter that, you would probably have to write some mess like void foo ( mutable( ref const(char)[] ) c ) { c = c[1..$]; } -- ReinerAgain, I'm also trying to understand this, knowing that things change a lot over the course of the discussion. That being said, I thought the current position is that by adding any other qualifier, such as ref, const/scope/final is no longer automatically applied.
Jun 06 2007
Reiner Pope wrote:Another problem with const by default came to mind. Consider the following code, with const-not-by-default: void foo( ref const(char)[] c) { c = c[1..$]; } I would hate to see this in const-by-default, which would try to wrap a const( ) around the whole type. To counter that, you would probably have to write some mess like void foo ( mutable( ref const(char)[] ) c ) { c = c[1..$]; } -- ReinerI would expect 'ref' be implicitly non-const. Especially since 'ref' is a synonym for 'inout' at present.
Jun 06 2007
Jarrett Billingsley wrote:What I'm asking you more seasoned programmers, and those more experienced with const-correctness to do, is to do something similar here. Step back, and have a look at what constness is for. What problems does it solve? Is it necessarily the best way to solve them? More importantly, what problems does it introduce? Remember, how the language _looks_ is just as important as the features it has. After all, what do you want to look at for hours on end each day if you use it at your job?I may be off track, but I think that scope could also bring some easy efficiency benefits, especially for small programs. I can believe that most function parameters are 'scope.' Being aware of this could potentially allow many more variables to be stack-allocated. It may also solve the problem of lexical closures: the escape analysis they require could perhaps be done by scope.
Jun 04 2007
Reiner Pope wrote:It may also solve the problem of lexical closures: the escape analysis they require could perhaps be done by scope.A good point: Delegates to 'scope' entities[1] should be 'scope' themselves. After all, they contain a reference to a 'scope' entity. [1] including anonymous delegates, which can be considered "delegates to stack frames".
Jun 05 2007
Jarrett Billingsley wrote:I won't lie, I'm not fond of the changes that are being developed. Now that even my dear, sweet dynamic arrays are having their syntax changed to something like int[new], I figured I'd voice my concerns over it.I answered a similar post from Charlie, but since the discussion seems to be here instead, I'll repost here: ----------------------- Actually, I quite empathize with your viewpoint. I worry that the final, const, invariant thing is too complicated. But there are some mitigating factors: 1) Just as in C++, you can pretty much ignore final, const, and invariant if they don't appeal to you. I don't bother using const in my C++ code. 2) The transitive nature of const means that there are a lot fewer const's you have to write. 3) Using D's type inference capability, a lot fewer types (and their attendant const's) need to be written. 4) It provides information that is actually useful to the compiler. 5) Scope has the promise of enabling reference counting to be far more efficient than is possible in C++. 6) Working together, these features move us towards supporting the functional programming paradigm better. FP is very important for the future, as it is very adaptable to parallel programming. 7) They make interfaces much more self-documenting. 8) They can make automated code analysis tools more effective. Automated code analysis is big business and is getting to be far more important as security consultants are brought in to analyze code for correctness, security, etc. Wall Street in particular is very interested in this stuff. 9) So far, in my work to make Phobos const-correct, it hasn't been the annoyance I thought it would be.
Jun 04 2007
Walter Bright wrote:1) Just as in C++, you can pretty much ignore final, const, and invariant if they don't appeal to you. I don't bother using const in my C++ code.But there are things in C++ that don't work if you ignore const. You can't bind a reference (to non-const) to a temporary, for example. You can't use your types in standard containers if their copy operations don't use const-qualified references to their source objects. You can't use symbolic constants for array sizes, or otherwise at compile time, if you don't make them const (or abuse enums for this purpose). You'll be unable to use all manner of interfaces if you ignore const. If you write within a very limited subset of C++, you can *then* ignore const -- but in that case you've already let it have a huge influence of your style. If you're writing libraries in C++ and you don't use const, you make it impractical for users of your library to use const, hence reducing its likely user base to those who don't use C++ in what is widely considered to be an idiomatic way. This is relevant here because in C++ the choice of avoiding const comes with significant costs (that most find prohibitive). It's woven into the fabric of the language and its standard library. D might be able to do better in allowing users to choose whether to use a const-aware style or not. If it does, the community will then have to choose what style(s) prevail. (My personal preference would be for an enriched type system that supports const-ish things, but that's not what this post is really about.) -- James
Jun 05 2007
Jarrett Billingsley wrote:What I'm asking you more seasoned programmers, and those more experienced with const-correctness to do, is to do something similar here. Step back, and have a look at what constness is for. What problems does it solve? Is it necessarily the best way to solve them? More importantly, what problems does it introduce? Remember, how the language _looks_ is just as important as the features it has. After all, what do you want to look at for hours on end each day if you use it at your job?'const' is essentially a contract that should guarantee certain behavior of a function. It can make multi-threaded code more efficient because it obviates the need for locks in some cases, and can help verify at compile-time that code does not violate the guarantees it attempts to maintain. That said, although I'm coming from a C++ background and though I have used 'const' as a matter of course, it has never actually improved my code. I've never accidentally violated const behavior and had the compiler catch the mistake for me, and dealing with const qualifiers and overloads is an absolute nightmare at times. I've got to admit that I've become somewhat of a D convert in this respect. I don't miss 'const' and will probably be annoyed if these changes complicate the code I write.I realize that this is late in the game. I feel like Walter and company have probably already ironed out most of the issues and semantics of the new syntaxes, if not begun to work on them. I don't want to discredit all they've done at all. I'm just wondering if it's the best thing for the language.The proposed changes are actually pretty decent from a conceptual standpoint but I don't like the syntax. Using three synonyms to represent different facets of const behavior kind of stinks. I'm sure I'll get used to it with practice, but I worry that the const features will hurt readability for those new to the language, and will complicate code in the general case for little actual gain.(And one final concern is -- what if this is implemented and it's a mess? A total disaster? Will Walter just say "sorry guys, we messed up" and take it out? Or will he, having put in so much time and effort, just say "well you're stuck with it"? This is a huge feature. This isn't regex match expressions. If it gets put into the language, it might not be taken back out. Especially since this is pretty much _the_ D 2.0 feature.)That's why it's in a branch. Frankly, I'm surprised to say that 'const' is probably my least anticipated 2.0 feature. I suppose I've simply been using D for long enough now that I don't really miss it from C++. That said, I suspect it may be important for D to gain acceptance in corporate environments, and wonder why no one has ever complained about Java's lack of const (to my knowledge). What worries me most is that these new features will change things about the language that I like--the .length/T[new] issue being the most recent example. Fritz's example is a core concept in much of Tango, and if it breaks we're pretty much sunk. If the syntax changes but it still works... I suppose we'll see. For the rest, I'm really just waiting to see how the new features pan out. Sean
Jun 05 2007
Sean Kelly wrote:Fritz's example is a core concept in much of Tango, and if it breaks we're pretty much sunk.IIRC Tango is actually where I got the idea to code these sort of functions like that :). Also, you misspelled my name.
Jun 05 2007
Frits van Bommel wrote:Also, you misspelled my name.Sorry about that. Sean
Jun 05 2007
Sean Kelly wrote:That said, although I'm coming from a C++ background and though I have used 'const' as a matter of course, it has never actually improved my code. I've never accidentally violated const behavior and had the compiler catch the mistake for me, and dealing with const qualifiers and overloads is an absolute nightmare at times. I've got to admit that I've become somewhat of a D convert in this respect. I don't miss 'const' and will probably be annoyed if these changes complicate the code I write.That's my experience too. Among curly-brace languages, only C++ has const, and C++ const is broken. No existing language does it properly, so it's hard to argue that it's a mandatory feature.The proposed changes are actually pretty decent from a conceptual standpoint but I don't like the syntax. Using three synonyms to represent different facets of const behavior kind of stinks. I'm sure I'll get used to it with practice, but I worry that the const features will hurt readability for those new to the language, and will complicate code in the general case for little actual gain.My thoughts exactly.What worries me most is that these new features will change things about the language that I like--the .length/T[new] issue being the most recent example. Fritz's example is a core concept in much of Tango, and if it breaks we're pretty much sunk. If the syntax changes but it still works... I suppose we'll see.I'm also a bit concerned that T[new] doesn't seem to be orthogonal to other const issues. It doesn't make sense to have an array which you can change the size of, but can't write to. I hope it's all worth it. Don.
Jun 05 2007
Don Clugston wrote:I'm also a bit concerned that T[new] doesn't seem to be orthogonal to other const issues. It doesn't make sense to have an array which you can change the size of, but can't write to.What do you mean? T[new] would be an array which you can both change the size of and write to. T[] would be an array which you can write to but can't change the size of. --bb
Jun 05 2007
Bill Baxter wrote:Don Clugston wrote:What is a const T[new] ?I'm also a bit concerned that T[new] doesn't seem to be orthogonal to other const issues. It doesn't make sense to have an array which you can change the size of, but can't write to.What do you mean? T[new] would be an array which you can both change the size of and write to. T[] would be an array which you can write to but can't change the size of. --bb
Jun 05 2007
Sean Kelly escribió:The proposed changes are actually pretty decent from a conceptual standpoint but I don't like the syntax. Using three synonyms to represent different facets of const behavior kind of stinks. I'm sure I'll get used to it with practice, but I worry that the const features will hurt readability for those new to the language, and will complicate code in the general case for little actual gain.Not only for those new to the language, but for less experienced programmers too. I know some C++, but I've never done anything serious with it. Thus, since I don't use it, I don't know what all those "const" do (once I read about it in some manual or something, but I no longer remember.) So, while I understand the problem at hand here, I still get confused over the syntax, even if I'm not new to the language, there have been so many discussions about that problem here, and the syntax has been explained so many times. For example, yesterday Walter posted a message that included this line: invariant(char)[new] ret = "prefix"; I understand it as "ret can't change." However, later on, on the same snippet, he appended to "ret." Not to mention that when I first read it, I had no idea what it was. I still don't know why those () are there and not somewhere else. Please don't try to explain to me; I guess it'll be clear once I start using it, but right now, I'm just trusting those who have walked this road before. Finally, I wouldn't know how to explain it to someone who's just starting in programming. Again, maybe it's because I myself don't understand it, but bear with me for a moment. I don't think it'll be immediately crystal clear for everyone that in an array you can or can't change the length, the pointer, the contents, all of them, none of them, or some of them, and that the keyword you use is the one that defines that, that the keywords available are very close in meaning, and that using or not using parentheses also changes the meaning. I'm sorry if it sounded over-dramatical; it certainly wasn't my intention. Bottom line is, I'm really hoping that it turns out well, and that I can understand it when it's out. -- Carlos Santander Bernal
Jun 05 2007
I also get very confused about const, final, invariant... and I think I'm beggining to understand why (I get confused). Algorithms describe what should be done, and also which variables and structures are needed in order to "execute" them. I've never seen an algorithm's pseudocode saying something like "and this var's content can't change" "this reference can't change", or "now do foo(bar), where bar dosen't change it's contents". The only point I see in marking variables as const or such, is for allowing compiler optimizations. So adding keywords to a language just to allow compiler optimizations seems strange to me. If you can't mark a function parameter as const in the language, you can always put a big warning comment saying "Hey, don't modify this array's content" and that's it. Documentation exists for this reason. So what if you write an interface and somebody implements it in the wrong way? It's like trying to document what a function in a interface should do, and enforcing the compiler to check this. Something llike int double(int x) does(returnsDouble) { return 3*x; // error, 3*x is not allowed because of returnsDouble } Carlos Santander escribió:Sean Kelly escribió:The proposed changes are actually pretty decent from a conceptual standpoint but I don't like the syntax. Using three synonyms to represent different facets of const behavior kind of stinks. I'm sure I'll get used to it with practice, but I worry that the const features will hurt readability for those new to the language, and will complicate code in the general case for little actual gain.Not only for those new to the language, but for less experienced programmers too. I know some C++, but I've never done anything serious with it. Thus, since I don't use it, I don't know what all those "const" do (once I read about it in some manual or something, but I no longer remember.) So, while I understand the problem at hand here, I still get confused over the syntax, even if I'm not new to the language, there have been so many discussions about that problem here, and the syntax has been explained so many times. For example, yesterday Walter posted a message that included this line: invariant(char)[new] ret = "prefix"; I understand it as "ret can't change." However, later on, on the same snippet, he appended to "ret." Not to mention that when I first read it, I had no idea what it was. I still don't know why those () are there and not somewhere else. Please don't try to explain to me; I guess it'll be clear once I start using it, but right now, I'm just trusting those who have walked this road before. Finally, I wouldn't know how to explain it to someone who's just starting in programming. Again, maybe it's because I myself don't understand it, but bear with me for a moment. I don't think it'll be immediately crystal clear for everyone that in an array you can or can't change the length, the pointer, the contents, all of them, none of them, or some of them, and that the keyword you use is the one that defines that, that the keywords available are very close in meaning, and that using or not using parentheses also changes the meaning. I'm sorry if it sounded over-dramatical; it certainly wasn't my intention. Bottom line is, I'm really hoping that it turns out well, and that I can understand it when it's out.
Jun 05 2007
Ary Manzana escribió:I also get very confused about const, final, invariant... and I think I'mbeggining to understand why (I get confused).Algorithms describe what should be done, and also which variables andstructures are needed in order to "execute" them. I've never seen an algorithm's pseudocode saying something like "and this var's content can't change" "this reference can't change", or "now do foo(bar), where bar dosen't change it's contents". The only point I see in marking variables as const or such, is for allowing compiler optimizations. So adding keywords to a language just to allow compiler optimizations seems strange to me.I agree with this...If you can't mark a function parameter as const in the language, you canalways put a big warning comment saying "Hey, don't modify this array's content" and that's it. Documentation exists for this reason. So what if you write an interface and somebody implements it in the wrong way? It's like trying to document what a function in a interface should do, and enforcing the compiler to check this. Something llikeint double(int x) does(returnsDouble) { return 3*x; // error, 3*x is not allowed because of returnsDouble }... but not with this. While it's still over my head, I see the const-correctness thing similar to type-safety. There're languages where you say: // returns a string function foo () { return "foo"; } And it works, and you don't want the compiler/interpreter to be bugging you. But at some point you want type-safety, even if only to make the comment and the implementation match. My point is, I don't think that's the purpose of documentation. There is one thing, however, and it is when everything gets carried away. Where do you draw the line between just documenting and actually implementing it as a language feature? For example, "throws" in Java, which is non-existent in D. Here, (almost) everyone agreed that it was best to leave it as documentation only. Nonetheless, the same argument I just applied for type-safety and const-correctness could be applied to "throws," so I guess it's a matter of preference or background, maybe? -- Carlos Santander Bernal
Jun 05 2007
Carlos Santander escribió:Ary Manzana escribió: My point is, I don't think that's the purpose of documentation. There is one thing, however, and it is when everything gets carried away. Where do you draw the line between just documenting and actually implementing it as a language feature? For example, "throws" in Java, which is non-existent in D. Here, (almost) everyone agreed that it was best to leave it as documentation only. Nonetheless, the same argument I just applied for type-safety and const-correctness could be applied to "throws," so I guess it's a matter of preference or background, maybe?Yes, you're right. Your analogy is great. I think I was too tired yesterday and some of my neurons weren't working well. Anyway, I'd like to see some real-world examples where const avoids bugs. Because if those examples don't exist, then the keywords are there just for compiler optimization. (BTW, I would have liked to have throws specified in functions... when programming in .Net, I just don't know what exceptions to expect while calling a function like File.open, for example. Of course, this is a trivial example, it's sure IOException, but with other classes it's not that obvious...)
Jun 06 2007
On Wed, 06 Jun 2007 14:11:18 +0400, Ary Manzana <ary esperanto.org.ar> = wrote:Anyway, I'd like to see some real-world examples where const avoids =bugs. Because if those examples don't exist, then the keywords are the=re =just for compiler optimization.First example: class A { public : A() {} A( const A & o ) {} A & operator=3D( const A & o ) {} bool operator!() const { return false; } bool operator=3D=3D( const A & o ) /*const*/ { return this =3D=3D &o;= } bool operator!=3D( const A & o ) /*const*/ { return !( *this =3D o );= } }; Try find error there without const-modifiers and with them. Second example: #include <string> class OutgoingCall { public : const std::string & msisdn() const { return msisdn_; } void setMsisdn( const std::string & value ) { msisdn_ =3D value; } private : std::string msisdn_; }; std::string makePlainNumber( std::string & msisdn ) { if( '+' =3D=3D msisdn[ 0 ] ) msisdn.erase( 0, 1 ); return msisdn; } void routeOutgoingCall( const OutgoingCall & call ) { std::string plainNumber =3D makePlainNumber( call.msisdn() ); } Because of const compiler doesn't allow me to call makePlainNumber from = = routeOutgoingCall. Without const this is allowed and makePlainNumber = modifies routeOutgoingCall's argument 'call'. -- = Regards, Yauheni Akhotnikau
Jun 07 2007
Thanks for the examples, I liked them. Now I see where is the problem, compared to other languages that doesn't have const, but have created hacks to avoid it. For example in Java, if a class has a List of something, and you want to expose it in a way that doesn't allow the caller to add elements, then you return an array of the elements. Of course, here the caller can still modify each of the elements in the array, but at least one problem is avoided. I guess creating an array is fast in Java, but it's sure faster not creating it and enforcing the caller not to modify the elements. And still the problem of being able to modify the elements exists. Which makes me think... Will const, final, invariant be used in method declarations to say "Calling this method this class won't be modified"? (the const after the function declaration, right?) I've been sort of following the threads and I haven't seen anything mentioning this. eao197 escribio':On Wed, 06 Jun 2007 14:11:18 +0400, Ary Manzana <ary esperanto.org.ar> wrote:Anyway, I'd like to see some real-world examples where const avoids bugs. Because if those examples don't exist, then the keywords are there just for compiler optimization.First example: class A { public : A() {} A( const A & o ) {} A & operator=( const A & o ) {} bool operator!() const { return false; } bool operator==( const A & o ) /*const*/ { return this == &o; } bool operator!=( const A & o ) /*const*/ { return !( *this = o ); } }; Try find error there without const-modifiers and with them. Second example: #include <string> class OutgoingCall { public : const std::string & msisdn() const { return msisdn_; } void setMsisdn( const std::string & value ) { msisdn_ = value; } private : std::string msisdn_; }; std::string makePlainNumber( std::string & msisdn ) { if( '+' == msisdn[ 0 ] ) msisdn.erase( 0, 1 ); return msisdn; } void routeOutgoingCall( const OutgoingCall & call ) { std::string plainNumber = makePlainNumber( call.msisdn() ); } Because of const compiler doesn't allow me to call makePlainNumber from routeOutgoingCall. Without const this is allowed and makePlainNumber modifies routeOutgoingCall's argument 'call'. --Regards, Yauheni Akhotnikau
Jun 07 2007
On Thu, 07 Jun 2007 15:49:54 +0400, Ary Manzana <ary esperanto.org.ar> = wrote:Which makes me think... Will const, final, invariant be used in method==declarations to say "Calling this method this class won't be modified"=? =(the const after the function declaration, right?) I've been sort of =following the threads and I haven't seen anything mentioning this.I don't see discussion about that in this thread (but I may miss = something). But a few month ago in one of the first discussion about = const/final/invariant (somewhere near = http://www.digitalmars.com/webnews/newsgroups.php?art_group=3Ddigitalmar= s.D&article_id=3D50233) = Andrei Alexandresky said that it is planned too = (http://www.digitalmars.com/webnews/newsgroups.php?art_group=3Ddigitalma= rs.D&article_id=3D50256) -- = Regards, Yauheni Akhotnikau
Jun 07 2007
Carlos Santander, el 5 de junio a las 21:15 me escribiste:invariant(char)[new] ret = "prefix";Syntax is getting so obscure... I'm scared. -- Leandro Lucarella (luca) | Blog colectivo: http://www.mazziblog.com.ar/blog/ .------------------------------------------------------------------------, \ GPG: 5F5A8D05 // F8CD F9A7 BF00 5431 4145 104C 949E BFB6 5F5A 8D05 / '--------------------------------------------------------------------' Y ya no tengo colores, sólo gris y negro Aquà donde el amor es de hierro Los dÃas pasan y moriremos contando el tiempo
Jun 06 2007
Leandro Lucarella wrote:Carlos Santander, el 5 de junio a las 21:15 me escribiste:Think of invariant(type) this way: imagine a template that creates a pointer to its type argument: template Pointer(T) { alias T* Pointer; } and used like: int x; Pointer!(int) p = &x; This is the notion of the "type constructor". So, let's think about doing it for const/invariant: template Const(T) and: Const!(int) from there it's a short step to just: const(int) i.e. it "const-ifies" its argument.invariant(char)[new] ret = "prefix";Syntax is getting so obscure... I'm scared.
Jun 07 2007
Walter Bright escribió:Leandro Lucarella wrote:I guess that's clear, but what does it mean when there're no parentheses? -- Carlos Santander BernalCarlos Santander, el 5 de junio a las 21:15 me escribiste:Think of invariant(type) this way: imagine a template that creates a pointer to its type argument: template Pointer(T) { alias T* Pointer; } and used like: int x; Pointer!(int) p = &x; This is the notion of the "type constructor". So, let's think about doing it for const/invariant: template Const(T) and: Const!(int) from there it's a short step to just: const(int) i.e. it "const-ifies" its argument.invariant(char)[new] ret = "prefix";Syntax is getting so obscure... I'm scared.
Jun 07 2007
Carlos Santander wrote:I guess that's clear, but what does it mean when there're no parentheses?When it has no parentheses, it behaves as a "storage class" rather than a "type constructor", and has the current meaning (along with all of the type is const, and the symbol is final).
Jun 07 2007
Walter Bright escribió:Carlos Santander wrote:And here's where it gets confusing. However, like I said before, I'll understand it later, when it's actually out. Thanks! -- Carlos Santander BernalI guess that's clear, but what does it mean when there're no parentheses?When it has no parentheses, it behaves as a "storage class" rather than a "type constructor", and has the current meaning (along with all of the type is const, and the symbol is final).
Jun 08 2007
Walter Bright, el 7 de junio a las 13:18 me escribiste:Leandro Lucarella wrote:I _understand_ the syntax, but it's getting really hard to read the code (and D used to be a very clean language to read, not like C++). I undestand C++ syntax too, but I think it's way to far from the ideal ;) I can't think of a better syntax though, but I just had to say it, I'm a little concerned of how much readabilty D could loose with this. I think const/invariant/scope are generally good. It's a great tool for autodocumentation, for compiler optimization and for compile-time check of bugs. But the thread about steping back got me thinking... -- LUCA - Leandro Lucarella - Usando Debian GNU/Linux Sid - GNU Generation ------------------------------------------------------------------------ E-Mail / JID: luca lugmen.org.ar GPG Fingerprint: D9E1 4545 0F4B 7928 E82C 375D 4B02 0FE0 B08B 4FB2 GPG Key: gpg --keyserver pks.lugmen.org.ar --recv-keys B08B4FB2 ------------------------------------------------------------------------ Yo soy Peperino él que nunca toma vino, yo soy aquel que por la mañana escucha Salatino. -- Peperino PómoroCarlos Santander, el 5 de junio a las 21:15 me escribiste:Think of invariant(type) this way: imagine a template that creates a pointer to its type argument:invariant(char)[new] ret = "prefix";Syntax is getting so obscure... I'm scared.
Jun 08 2007
Leandro Lucarella Wrote:Walter Bright, el 7 de junio a las 13:18 me escribiste:Yet another reason for const by default with implicit 'in'. The source stays 'clean' but still provides the protection. ReganLeandro Lucarella wrote:I _understand_ the syntax, but it's getting really hard to read the code (and D used to be a very clean language to read, not like C++). I undestand C++ syntax too, but I think it's way to far from the ideal ;) I can't think of a better syntax though, but I just had to say it, I'm a little concerned of how much readabilty D could loose with this.Carlos Santander, el 5 de junio a las 21:15 me escribiste:Think of invariant(type) this way: imagine a template that creates a pointer to its type argument:invariant(char)[new] ret = "prefix";Syntax is getting so obscure... I'm scared.
Jun 08 2007
Sean Kelly wrote:That said, I suspect it may be important for D to gain acceptance in corporate environments, and wonder why no one has ever complained about Java's lack of const (to my knowledge).People have: http://www.google.coam/search?q=java+const I'm a Java programmer, and I have often wished for something like const. It would save me some bugs and some peace of mind in reasoning about my code. It would definitely help with threading. By the way, from the above search I found a Java bug filed in 1999 requesting something like const. The comments from Sun on why they will not add it sound familiar: http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4211070 "It's possible, but I would rather hold the line on creaping featurism. One can design around this (using interfaces, wrappers etc.)." "In addition to creeping featurism, we see the following problems with this feature: * Adding const is too late now. Had this been added from 1.0, the situation could have been different. * Const pollution: the C++ approach requires all const methods to be marked with a const keyword. This means that most methods will have to be marked const explicitly. This tend to clutter all methods in C++. * Compatibility is a very important feature of the JDK. Arguably, the collection classes should be modified to indicate that the elements are const. That would require all existing implementations to be updated in the same way, effectively breaking all existing non-JDK implementations of the collection interfaces. Similarly, hashCode would have to be const, breaking the current implementation of String."
Jun 05 2007
Jeff Nowakowski wrote:Sean Kelly wrote:Wow. That's too funny. Good find! Reading some of the comments I can't help but chuckle to myself -- oh there's Don Clugston's doppleganger! oh and this guy sounds just like Yauheni Akhotnikau! Wait, there's Sean Kelly's twin too! :-) --bbThat said, I suspect it may be important for D to gain acceptance in corporate environments, and wonder why no one has ever complained about Java's lack of const (to my knowledge).People have: http://www.google.coam/search?q=java+const I'm a Java programmer, and I have often wished for something like const. It would save me some bugs and some peace of mind in reasoning about my code. It would definitely help with threading. By the way, from the above search I found a Java bug filed in 1999 requesting something like const. The comments from Sun on why they will not add it sound familiar: http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4211070
Jun 05 2007
Sean Kelly wrote:corporate environments, and wonder why no one has ever complained about Java's lack of const (to my knowledge).Well, I guess Java's lack of const is somewhat mitigated by having custom made mutable/const versions of classes. Like the mutable String vs. mutable StringBuilder, or the readonly Collection vs. the mutables List/Map/Set, etc.. -- Bruno Medeiros - MSc in CS/E student http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
Jun 09 2007
I agree, though for a slightly different reason (now that I'm 99% sure of the scope of these changes). I believe const/final/scope is an implementation detail. If a function makes a change to a parameter, it better have a damn good reason, but I expect whomever coded it to know that. A lot of functions I write simply pass object references around without ever actually looking at them, so going six levels up the hierarchy to remove a "const" doesn't appeal to me. The data doesn't "belong" to the caller or anyone else - when explicit memory management is an issue, it's important to track the "owner" of a memory location, but with garbage collection, I consider the owner of a piece of mutable data to be either the thread it was created in or the thread that currently holds the lock on it. I rarely craft "functions" in the pure mathematical/FP sense, they almost all somehow change state. If the data passed in is changed somewhere in those functions, that's part of how that function works and not something the caller of the function should need to care about in most cases (encapsulating a lot of functionality in structurally sound objects with preconditions/postconditions/invariants helps a lot with this, too). As far as needing const for large-scale enterprise development, that's utter hogwash. I work on a team of 30-40 developers (okay; they work and I post on the D newsgroup...) on a mature codebase that's probably had over 100 people working on it at some time or another, not mentioning architects, testers, and everyone else. The code is 95% Java, and the rest in Perl, neither language which has a true "const" (Java has final as an implementation detail, but it is not inherited, not transitive, and completely transparent to the caller - i.e. even though the "final" appears in the method definition, it's not part of the method signature). But, whatever... Walter's post convinced me that this can't be too bad, and hopefully some automated tool will come around that inserts all the "in"s. That said, I do agree that a little time off from implementing syntax changes, and rather polishing/fixing/adding non-breaking features (reflection, getting rid of forward reference problems, etc.) might do D some good. All the best, Fraser Jarrett Billingsley Wrote:I won't lie, I'm not fond of the changes that are being developed. Now that even my dear, sweet dynamic arrays are having their syntax changed to something like int[new], I figured I'd voice my concerns over it. I feel (and this coming from someone who has never truly learned const-correctness, this may not mean much) that all the new const stuff is doing is the same thing that C and C++ do. Yes, there's more granularity by having const, scope, final, and invariant, and even with a new type of unresizeable array. But it doesn't feel like a D solution for it. As an example, I'll give the C preprocessor. D didn't inherit it; instead, it stepped back, looked at what it was used for, and designed the most common features into the language, giving them first-class benefits that the CPP can never give. We have version statements, debug statements, constants and enums, a proper module system, templates, mixins (templates and string), and soon even AST-based macros, all of which blow the CPP out of the water in terms of parsing and features. What I'm asking you more seasoned programmers, and those more experienced with const-correctness to do, is to do something similar here. Step back, and have a look at what constness is for. What problems does it solve? Is it necessarily the best way to solve them? More importantly, what problems does it introduce? Remember, how the language _looks_ is just as important as the features it has. After all, what do you want to look at for hours on end each day if you use it at your job? I realize that this is late in the game. I feel like Walter and company have probably already ironed out most of the issues and semantics of the new syntaxes, if not begun to work on them. I don't want to discredit all they've done at all. I'm just wondering if it's the best thing for the language. (And one final concern is -- what if this is implemented and it's a mess? A total disaster? Will Walter just say "sorry guys, we messed up" and take it out? Or will he, having put in so much time and effort, just say "well you're stuck with it"? This is a huge feature. This isn't regex match expressions. If it gets put into the language, it might not be taken back out. Especially since this is pretty much _the_ D 2.0 feature.)
Jun 05 2007
Guess I should put in my 2 cents also, since there are so many people who seem to dislike the idea of const in D. I am a C++ programmer that uses const a lot. I am the primary author of a very big API that is used by other developers. There are cases where things get complicated, but for the most part, const really helps. It helps that the compiler enforces the rules. So I like the idea of adding const to D in some form. I admit I don't understand all the details of D's const implementation, but I don't think that adding const in D is a bad thing. Walter said himself that the next release of D will be alpha, so changes can be made if necessary. Therefore, I would consider the current approach to const to be a first draft. D's const implementation seems much more thought-out than C++'s. Personally, I am excited to see what this feature will give us. -Craig
Jun 05 2007
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 Craig Black schrieb am 2007-06-05:Guess I should put in my 2 cents also, since there are so many people who seem to dislike the idea of const in D. I am a C++ programmer that uses const a lot. I am the primary author of a very big API that is used by other developers. There are cases where things get complicated, but for the most part, const really helps. It helps that the compiler enforces the rules. So I like the idea of adding const to D in some form. I admit I don't understand all the details of D's const implementation, but I don't think that adding const in D is a bad thing.I don't work with much C++ code, however when I do it is mostly maintenance work of poorly documented code and "const" really helps. Without "const" I'd have to read much more code to understand what is going on, with "const" functions and "const" arguments the job is vastly easier. Yes, C++'s "const" can be subverted in several ways but is a fairly good indication of the intended behaviour. Thomas -----BEGIN PGP SIGNATURE----- iQIVAwUBRmcOK7ZlboUnBhRKAQIEVA//RIck6oBs9Y4FFfBgwAdNN8K4dX6RRfKG nv3vuFAynOKqYi381quExJt2AfIRnZHk3jtWMrZTjkJRXcg0j31H3zkmKPwwN9Cp rS20sKeCSStRs+UsrOaikieHBtC0sRPQHQcqVmALmbu7mDIiA/aGbrCnSStoE+6d 2zbQO+GlW/r21YTubu9QaM1UtK4wbBytDthRgVXYMYwrFvAd5cXnF1qvuoYJU01W 52iFJPAWlDQ5trJAis2F/uFt6xrG1Ht/Ms7QYSnf8FlZ/svgGA0ArbT3IdZCzOET HFQB3e4dyd0Bstc+beVCUi0EB83GB3ABzrgc6rEiDTLvBtSeyAaPWReu3KsRasob gxviQV+5X0IpP//LMLyN1FLOWR4ttt6/xvkxl1rLz+9BFR+tCiAypG66i5RTiWMp RXZxtCDqAmbYrSDfDHNiM8KUwYptLMPrP/ezwX3rT5hGE/PZNtFt3se8NIhS5scd sEQqj0CUTy3t9mkP+7yzGc+JWvsj4bWK02yiRssDp3T6JhV4XzByq+p4o2PFPd+6 5k7lUUOo0j3Id/bGTxTt5Bk3oTifXtX9YW1nmCmWRVrePOgBdJLrWbaqykeFgNsc +PESLCA4+xLIWvL1NQJFP3fJ63TwbIHbC0j4TZnZJk5g1nrEoxQMbkoFxEwSwKcG 1/uaLXyNwbA= =xc/Y -----END PGP SIGNATURE-----
Jun 06 2007
Ary Manzana wrote:Yes, you're right. Your analogy is great. I think I was too tired yesterday and some of my neurons weren't working well. Anyway, I'd like to see some real-world examples where const avoids bugs. Because if those examples don't exist, then the keywords are there just for compiler optimization. (BTW, I would have liked to have throws specified in functions... when programming in .Net, I just don't know what exceptions to expect while calling a function like File.open, for example. Of course, this is a trivial example, it's sure IOException, but with other classes it's not that obvious...)Actually, I think that this should go the other way, the language should be constructed in such a way that things like throws can be trivially found by static code analysis. It should be easy to build a tool that can look at a code base and answer that kind of question.
Jun 06 2007
Denton Cockburn Wrote:On Thu, 07 Jun 2007 16:25:21 +1000, Reiner Pope wrote:I thought so too. As Don says; 'ref' is an alias of 'inout'. It's ver y purpose is to indicate a variable which is going to be modified. Therefore it stands to reason that function parameters passed as 'ref' will be mutable by default. :) ReganAnother problem with const by default came to mind. Consider the following code, with const-not-by-default: void foo( ref const(char)[] c) { c = c[1..$]; } I would hate to see this in const-by-default, which would try to wrap a const( ) around the whole type. To counter that, you would probably have to write some mess like void foo ( mutable( ref const(char)[] ) c ) { c = c[1..$]; } -- ReinerAgain, I'm also trying to understand this, knowing that things change a lot over the course of the discussion. That being said, I thought the current position is that by adding any other qualifier, such as ref, const/scope/final is no longer automatically applied.
Jun 07 2007
Regan Heath wrote:Denton Cockburn Wrote:Actually, it might be a nice distinction to keep both 'inout' and 'ref', where 'inout' would be mutable, but 'ref' not? 'ref' would then be an optimized version of 'in'. Or will 'inout' disappear completely? L.Again, I'm also trying to understand this, knowing that things change a lot over the course of the discussion. That being said, I thought the current position is that by adding any other qualifier, such as ref, const/scope/final is no longer automatically applied.I thought so too. As Don says; 'ref' is an alias of 'inout'. It's ver y purpose is to indicate a variable which is going to be modified. Therefore it stands to reason that function parameters passed as 'ref' will be mutable by default. :)
Jun 08 2007
Lionello Lunesu wrote:Regan Heath wrote:'ref' meaning what C++'s 'const foo&' is how I originally understood it, but from all the discussion I've gathered that 'inout' will be deprecated and then phased out after all. So 'ref' will be only the new name for 'inout'. I hope I'm wrong, I think 'inout' is more self-documenting than 'ref'. -- Remove ".doesnotlike.spam" from the mail address.As Don says; 'ref' is an alias of 'inout'. It's ver y purpose is to indicate a variable which is going to be modified. Therefore it stands to reason that function parameters passed as 'ref' will be mutable by default. :)Actually, it might be a nice distinction to keep both 'inout' and 'ref', where 'inout' would be mutable, but 'ref' not? 'ref' would then be an optimized version of 'in'. Or will 'inout' disappear completely?
Jun 08 2007
Deewiant Wrote:Lionello Lunesu wrote:I liked 'inout' better than 'ref' too. I was against the initial proposal before I left for 6 months and when I got back, there it was! 'inout' was originally intended to signal the intent of the function to "read from the parameter and write back to it". Much like 'out' just meant "I will only write to this" and the default 'in' meant "I will only read this". 'ref' however is an instruction to pass by reference, which is a whole other kettle of fish. Yes, 'inout' has to pass by reference in order to carry out it's intention but that doesn't make it the same thing. My original argument against adding anything more to the language was something along the lines of.. If you want to pass something by reference, why not pass a pointer to it. After all, in D, you can dereference a pointer in exactly the same way as you would the thing itself, eg. struct A { int member; } void foo(A a) { a.member = 5; } void foo(A* a) { a.member = 5; } notice that to dereference a struct passed by value, or a pointer to a struct you always use '.' That said, the call site would look like foo(&a); instead of the cleaner foo(a)... which is perhaps why C++ added a pass-by-reference & to be used in place of the pointer * syntax. Hmm.. idea; why not allow foo(a) and automatically convert to foo(&a) internally if 'a' is not a pointer. I mean, the compiler knows what 'a' is, it knows what 'foo' wants, why can't we have the syntax automatically, much like we do for dereferencing. ReganRegan Heath wrote:'ref' meaning what C++'s 'const foo&' is how I originally understood it, but from all the discussion I've gathered that 'inout' will be deprecated and then phased out after all. So 'ref' will be only the new name for 'inout'. I hope I'm wrong, I think 'inout' is more self-documenting than 'ref'.As Don says; 'ref' is an alias of 'inout'. It's ver y purpose is to indicate a variable which is going to be modified. Therefore it stands to reason that function parameters passed as 'ref' will be mutable by default. :)Actually, it might be a nice distinction to keep both 'inout' and 'ref', where 'inout' would be mutable, but 'ref' not? 'ref' would then be an optimized version of 'in'. Or will 'inout' disappear completely?
Jun 08 2007
Main reason I can think of is function overloading. This is an acceptable overload: void foo(A a) void foo(A* a) And if you allow 'a' to automatically convert to '&a': foo(a); // Ambiguous error Regan Heath wrote:Hmm.. idea; why not allow foo(a) and automatically convert to foo(&a) internally if 'a' is not a pointer. I mean, the compiler knows what 'a' is, it knows what 'foo' wants, why can't we have the syntax automatically, much like we do for dereferencing. Regan
Jun 08 2007
Regan Heath wrote:Deewiant Wrote:We'd lose the ability to overload a function with both foo(A) and foo(A*), but to be honest I can't think of a use case for that anyway. I like it! We could lose 'ref' completely. -- Remove ".doesnotlike.spam" from the mail address.'ref' meaning what C++'s 'const foo&' is how I originally understood it, but from all the discussion I've gathered that 'inout' will be deprecated and then phased out after all. So 'ref' will be only the new name for 'inout'. I hope I'm wrong, I think 'inout' is more self-documenting than 'ref'.I liked 'inout' better than 'ref' too. I was against the initial proposal before I left for 6 months and when I got back, there it was! 'inout' was originally intended to signal the intent of the function to "read from the parameter and write back to it". Much like 'out' just meant "I will only write to this" and the default 'in' meant "I will only read this". 'ref' however is an instruction to pass by reference, which is a whole other kettle of fish. Yes, 'inout' has to pass by reference in order to carry out it's intention but that doesn't make it the same thing. My original argument against adding anything more to the language was something along the lines of.. If you want to pass something by reference, why not pass a pointer to it. After all, in D, you can dereference a pointer in exactly the same way as you would the thing itself, eg. struct A { int member; } void foo(A a) { a.member = 5; } void foo(A* a) { a.member = 5; } notice that to dereference a struct passed by value, or a pointer to a struct you always use '.' That said, the call site would look like foo(&a); instead of the cleaner foo(a)... which is perhaps why C++ added a pass-by-reference & to be used in place of the pointer * syntax. Hmm.. idea; why not allow foo(a) and automatically convert to foo(&a) internally if 'a' is not a pointer. I mean, the compiler knows what 'a' is, it knows what 'foo' wants, why can't we have the syntax automatically, much like we do for dereferencing.
Jun 08 2007
Deewiant wrote:Regan Heath wrote:Didn't take long to come up with a case: bool contains(char[] string, char character); bool contains(char[] string, char* cstring); bool contains(char[] string, char[] string2); With first-class arrays I'd say this isn't a big problem, and I'd say the handiness of your idea is worth the minor hassle of having to pass cstring to a toString function, but it's something to consider. -- Remove ".doesnotlike.spam" from the mail address.Hmm.. idea; why not allow foo(a) and automatically convert to foo(&a) internally if 'a' is not a pointer. I mean, the compiler knows what 'a' is, it knows what 'foo' wants, why can't we have the syntax automatically, much like we do for dereferencing.We'd lose the ability to overload a function with both foo(A) and foo(A*), but to be honest I can't think of a use case for that anyway. I like it! We could lose 'ref' completely.
Jun 08 2007
Regan Heath wrote:Deewiant Wrote:Good idea. I'd be perfectly happy to do that with structs. Now that you mention it, it is kind of inconsistent that D basically turns astruct.foo into (&astruct).foo for you, but it wont turn foo(astruct) into foo(&astruct) for you. (Here assuming the D meaning of '.' is like C++ '->') But how do you handle overloads on '*'? void foo(int x) { /* sets foo to x */ } void foo(int* x) { /* returns foo via x*/ } I guess that's already impossible with inout anyway. We get along currently just fine without being able to overload on inout, so maybe the loss of overload on '*' would go mostly unnoticed? I think this feature should be limited to one level, though. Calling a 'void foo(int **x)' with a plain int is probably not going to do what the user expects. --bbLionello Lunesu wrote:If you want to pass something by reference, why not pass a pointer to it. After all, in D, you can dereference a pointer in exactly the same way as you would the thing itself, eg. struct A { int member; } void foo(A a) { a.member = 5; } void foo(A* a) { a.member = 5; } notice that to dereference a struct passed by value, or a pointer to a struct you always use '.' That said, the call site would look like foo(&a); instead of the cleaner foo(a)... which is perhaps why C++ added a pass-by-reference & to be used in place of the pointer * syntax. Hmm.. idea; why not allow foo(a) and automatically convert to foo(&a) internally if 'a' is not a pointer. I mean, the compiler knows what 'a' is, it knows what 'foo' wants, why can't we have the syntax automatically, much like we do for dereferencing.
Jun 08 2007
Bill Baxter Wrote:Regan Heath wrote:True, but calling with an int* should be allowed? I'm wondering if that would be ok or just confusing... not sure. ReganDeewiant Wrote:Good idea. I'd be perfectly happy to do that with structs. Now that you mention it, it is kind of inconsistent that D basically turns astruct.foo into (&astruct).foo for you, but it wont turn foo(astruct) into foo(&astruct) for you. (Here assuming the D meaning of '.' is like C++ '->') But how do you handle overloads on '*'? void foo(int x) { /* sets foo to x */ } void foo(int* x) { /* returns foo via x*/ } I guess that's already impossible with inout anyway. We get along currently just fine without being able to overload on inout, so maybe the loss of overload on '*' would go mostly unnoticed? I think this feature should be limited to one level, though. Calling a 'void foo(int **x)' with a plain int is probably not going to do what the user expects.Lionello Lunesu wrote:If you want to pass something by reference, why not pass a pointer to it. After all, in D, you can dereference a pointer in exactly the same way as you would the thing itself, eg. struct A { int member; } void foo(A a) { a.member = 5; } void foo(A* a) { a.member = 5; } notice that to dereference a struct passed by value, or a pointer to a struct you always use '.' That said, the call site would look like foo(&a); instead of the cleaner foo(a)... which is perhaps why C++ added a pass-by-reference & to be used in place of the pointer * syntax. Hmm.. idea; why not allow foo(a) and automatically convert to foo(&a) internally if 'a' is not a pointer. I mean, the compiler knows what 'a' is, it knows what 'foo' wants, why can't we have the syntax automatically, much like we do for dereferencing.
Jun 08 2007
On Thu, 07 Jun 2007 18:42:03 -0400, Regan Heath wrote:Denton Cockburn Wrote:I have to disagree Regan. 'ref' might mean that the passing mechanism is "by reference" but it does not necessarily mean that the access permission is 'allowed to change". Do not get parameter passing mechanics confused with access permissions. -- Derek Parnell Melbourne, Australia "Justice for David Hicks!" skype: derek.j.parnellOn Thu, 07 Jun 2007 16:25:21 +1000, Reiner Pope wrote:I thought so too. As Don says; 'ref' is an alias of 'inout'. It's ver y purpose is to indicate a variable which is going to be modified. Therefore it stands to reason that function parameters passed as 'ref' will be mutable by default. :) ReganAnother problem with const by default came to mind. Consider the following code, with const-not-by-default: void foo( ref const(char)[] c) { c = c[1..$]; } I would hate to see this in const-by-default, which would try to wrap a const( ) around the whole type. To counter that, you would probably have to write some mess like void foo ( mutable( ref const(char)[] ) c ) { c = c[1..$]; } -- ReinerAgain, I'm also trying to understand this, knowing that things change a lot over the course of the discussion. That being said, I thought the current position is that by adding any other qualifier, such as ref, const/scope/final is no longer automatically applied.
Jun 08 2007
Derek Parnell Wrote:On Thu, 07 Jun 2007 18:42:03 -0400, Regan Heath wrote:You're dead right. In fact 'ref' (a passing mechanic) is/has replaced 'inout' (signifying 'this reference is allowed to change') so it seems Walter/D is confusing the two. ReganDenton Cockburn Wrote:I have to disagree Regan. 'ref' might mean that the passing mechanism is "by reference" but it does not necessarily mean that the access permission is 'allowed to change". Do not get parameter passing mechanics confused with access permissions.On Thu, 07 Jun 2007 16:25:21 +1000, Reiner Pope wrote:I thought so too. As Don says; 'ref' is an alias of 'inout'. It's ver y purpose is to indicate a variable which is going to be modified. Therefore it stands to reason that function parameters passed as 'ref' will be mutable by default. :) ReganAnother problem with const by default came to mind. Consider the following code, with const-not-by-default: void foo( ref const(char)[] c) { c = c[1..$]; } I would hate to see this in const-by-default, which would try to wrap a const( ) around the whole type. To counter that, you would probably have to write some mess like void foo ( mutable( ref const(char)[] ) c ) { c = c[1..$]; } -- ReinerAgain, I'm also trying to understand this, knowing that things change a lot over the course of the discussion. That being said, I thought the current position is that by adding any other qualifier, such as ref, const/scope/final is no longer automatically applied.
Jun 08 2007