www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Stepping back and looking at constness from another angle.

reply "Jarrett Billingsley" <kb3ctd2 yahoo.com> writes:
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
next sibling parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
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
next sibling parent reply Charlie <charlie.fats gmail.com> writes:
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
next sibling parent reply Jason House <jason.james.house gmail.com> writes:
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
parent Bill Baxter <dnewsgroup billbaxter.com> writes:
Jason House wrote:
 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.
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.
 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
prev sibling parent Derek Parnell <derek nomail.afraid.org> writes:
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
prev sibling parent reply "Jarrett Billingsley" <kb3ctd2 yahoo.com> writes:
"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
next sibling parent Bill Baxter <dnewsgroup billbaxter.com> writes:
Jarrett Billingsley wrote:
 "Bill Baxter" <dnewsgroup billbaxter.com> wrote in message 
 news:f42c51$1ap8$1 digitalmars.com...
 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.
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. --bb
Jun 04 2007
prev sibling parent Leandro Lucarella <llucax gmail.com> writes:
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...

 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.
+1
 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.
+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."
Jun 05 2007
prev sibling next sibling parent "Ameer Armaly" <ameer_armaly hotmail.com> writes:
"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
prev sibling next sibling parent "Lionello Lunesu" <lionello lunesu.remove.com> writes:
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
prev sibling next sibling parent reply "David B. Held" <dheld codelogicconsulting.com> writes:
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
next sibling parent reply Johan Granberg <lijat.meREM OVEgmail.com> writes:
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.
 
 Dave
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.
Jun 05 2007
parent Charlie <charlie.fats gmail.com> writes:
Johan Granberg Wrote:

 David B. Held wrote:
 
 Let me point out a few guiding principles that are worth considering:
<snip>
 
 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
prev sibling next sibling parent Bill Baxter <dnewsgroup billbaxter.com> writes:
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
prev sibling next sibling parent Derek Parnell <derek nomail.afraid.org> writes:
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 bad
Yep 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 declarations
Why? 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
prev sibling next sibling parent reply Regan Heath <regan netmail.co.nz> writes:
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
parent reply "David B. Held" <dheld codelogicconsulting.com> writes:
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
next sibling parent reply Derek Parnell <derek nomail.afraid.org> writes:
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
parent Walter Bright <newshound1 digitalmars.com> writes:
Derek Parnell wrote:
 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.
Function argument lists are expression tuples, and function parameter lists are type tuples.
 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
prev sibling parent reply Bruno Medeiros <brunodomedeiros+spam com.gmail> writes:
David B. Held wrote:
 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
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
Jun 11 2007
parent Don Clugston <dac nospam.com.au> writes:
Bruno Medeiros wrote:
 David B. Held wrote:
 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
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).
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.
Jun 11 2007
prev sibling parent reply Reiner Pope <some address.com> writes:
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
next sibling parent Denton Cockburn <diboss hotmail.com> writes:
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..$];
 }
 
    -- Reiner
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.
Jun 06 2007
prev sibling parent Don Clugston <dac nospam.com.au> writes:
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..$];
 }
 
   -- Reiner
I would expect 'ref' be implicitly non-const. Especially since 'ref' is a synonym for 'inout' at present.
Jun 06 2007
prev sibling next sibling parent reply Reiner Pope <some address.com> writes:
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
parent Frits van Bommel <fvbommel REMwOVExCAPSs.nl> writes:
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
prev sibling next sibling parent reply Walter Bright <newshound1 digitalmars.com> writes:
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
parent James Dennett <jdennett acm.org> writes:
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
prev sibling next sibling parent reply Sean Kelly <sean f4.ca> writes:
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
next sibling parent reply Frits van Bommel <fvbommel REMwOVExCAPSs.nl> writes:
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
parent Sean Kelly <sean f4.ca> writes:
Frits van Bommel wrote:
 
 Also, you misspelled my name.
Sorry about that. Sean
Jun 05 2007
prev sibling next sibling parent reply Don Clugston <dac nospam.com.au> writes:
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
parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
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
parent Don Clugston <dac nospam.com.au> writes:
Bill Baxter wrote:
 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
What is a const T[new] ?
Jun 05 2007
prev sibling next sibling parent reply Carlos Santander <csantander619 gmail.com> writes:
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
next sibling parent reply Ary Manzana <ary esperanto.org.ar> writes:
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
parent reply Carlos Santander <csantander619 gmail.com> writes:
Ary Manzana escribió:
 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.

I agree with this...

 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
 }
... 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
parent reply Ary Manzana <ary esperanto.org.ar> writes:
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
parent reply eao197 <eao197 intervale.ru> writes:
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
parent reply Ary Manzana <ary esperanto.org.ar> writes:
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
parent eao197 <eao197 intervale.ru> writes:
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
prev sibling parent reply Leandro Lucarella <llucax gmail.com> writes:
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
parent reply Walter Bright <newshound1 digitalmars.com> writes:
Leandro Lucarella wrote:
 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.
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.
Jun 07 2007
next sibling parent reply Carlos Santander <csantander619 gmail.com> writes:
Walter Bright escribió:
 Leandro Lucarella wrote:
 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.
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.
I guess that's clear, but what does it mean when there're no parentheses? -- Carlos Santander Bernal
Jun 07 2007
parent reply Walter Bright <newshound1 digitalmars.com> writes:
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
parent Carlos Santander <csantander619 gmail.com> writes:
Walter Bright escribió:
 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).
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 Bernal
Jun 08 2007
prev sibling parent reply Leandro Lucarella <llucax gmail.com> writes:
Walter Bright, el  7 de junio a las 13:18 me escribiste:
 Leandro Lucarella wrote:
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.
Think of invariant(type) this way: imagine a template that creates a pointer to its type argument:
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ómoro
Jun 08 2007
parent Regan Heath <regan netmail.co.nz> writes:
Leandro Lucarella Wrote:
 Walter Bright, el  7 de junio a las 13:18 me escribiste:
 Leandro Lucarella wrote:
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.
Think of invariant(type) this way: imagine a template that creates a pointer to its type argument:
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.
Yet another reason for const by default with implicit 'in'. The source stays 'clean' but still provides the protection. Regan
Jun 08 2007
prev sibling next sibling parent reply Jeff Nowakowski <jeff dilacero.org> writes:
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
parent Bill Baxter <dnewsgroup billbaxter.com> writes:
Jeff Nowakowski wrote:
 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
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! :-) --bb
Jun 05 2007
prev sibling parent Bruno Medeiros <brunodomedeiros+spam com.gmail> writes:
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
prev sibling next sibling parent Robert Fraser <fraserofthenight gmail.com> writes:
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
prev sibling next sibling parent reply "Craig Black" <cblack ara.com> writes:
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
parent Thomas Kuehne <thomas-dloop kuehne.cn> writes:
-----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
prev sibling next sibling parent BCS <BCS pathlink.com> writes:
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
prev sibling parent reply Regan Heath <regan netmail.co.nz> writes:
Denton Cockburn Wrote:
 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..$];
 }
 
    -- Reiner
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. :) Regan
Jun 07 2007
next sibling parent reply Lionello Lunesu <lio lunesu.remove.com> writes:
Regan Heath wrote:
 Denton Cockburn Wrote:
 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. :)
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.
Jun 08 2007
parent reply Deewiant <deewiant.doesnotlike.spam gmail.com> writes:
Lionello Lunesu wrote:
 Regan Heath wrote:
 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?
'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.
Jun 08 2007
parent reply Regan Heath <regan netmail.co.nz> writes:
Deewiant Wrote:
 Lionello Lunesu wrote:
 Regan Heath wrote:
 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?
'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. Regan
Jun 08 2007
next sibling parent Xinok <xnknet gmail.com> writes:
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
prev sibling next sibling parent reply Deewiant <deewiant.doesnotlike.spam gmail.com> writes:
Regan Heath wrote:
 Deewiant 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'.
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.
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.
Jun 08 2007
parent Deewiant <deewiant.doesnotlike.spam gmail.com> writes:
Deewiant wrote:
 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.
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.
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.
Jun 08 2007
prev sibling parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
Regan Heath wrote:
 Deewiant Wrote:
 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.
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. --bb
Jun 08 2007
parent Regan Heath <regan netmail.co.nz> writes:
Bill Baxter Wrote:
 Regan Heath wrote:
 Deewiant Wrote:
 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.
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.
True, but calling with an int* should be allowed? I'm wondering if that would be ok or just confusing... not sure. Regan
Jun 08 2007
prev sibling parent reply Derek Parnell <derek psych.ward> writes:
On Thu, 07 Jun 2007 18:42:03 -0400, Regan Heath wrote:

 Denton Cockburn Wrote:
 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..$];
 }
 
    -- Reiner
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. :) Regan
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.parnell
Jun 08 2007
parent Regan Heath <regan netmail.co.nz> writes:
Derek Parnell Wrote:
 On Thu, 07 Jun 2007 18:42:03 -0400, Regan Heath wrote:
 
 Denton Cockburn Wrote:
 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..$];
 }
 
    -- Reiner
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. :) Regan
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.
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. Regan
Jun 08 2007