digitalmars.D - Const by Default
- David B. Held (64/64) Jun 24 2007 I think an interesting point was brought up in the earlier CbD thread,
- Reiner Pope (4/9) Jun 24 2007 But what's to stop someone turning your argument completely around? *Wit...
- renoX (8/25) Jun 28 2007 Well, if this was always true, this would be a good thing, unfortunately...
- Derek Parnell (9/28) Jun 28 2007 What does your point have to do with whether or not 'const' is the defau...
- Derek Parnell (103/173) Jun 24 2007 On Sun, 24 Jun 2007 18:02:42 -0700, David B. Held wrote:
- Carlos Santander (5/13) Jun 24 2007 D does have it. In fact, I remember Walter once said that ideally it sho...
- Jason House (4/35) Jun 24 2007 What happened to inout? To me, using inout would make perfect sense to
- Henning Hasemann (15/20) Jun 25 2007 Ack, having to write ref or final in order to remove const is ugly.
- Don Clugston (12/30) Jun 25 2007 Because you're retaining a writable copy of a and b, so the class could ...
I think an interesting point was brought up in the earlier CbD thread, which is specifically the issue of c'tors, and generally, the issue of passing mutable references. So consider: class MyObj { this(MyClass a, MyClass b) { a_ = a; b_ = b; } private: MyClass a_; MyClass b_; } With CbD, this code is incorrect. Would you like to explain to a novice programmer why? Now, let's try to fix it: this(ref MyClass a, ref MyClass b) { a_ = a; b_ = b; } This code works, but now you have to explain to the student that even though classes are always passed by reference in D, you have to actually spell it out explicitly sometimes. And further note that the only reason this form is not less efficient is because references to references are collapsed to simple references. Otherwise, there would be an extra level of indirection here. Here's another try: this(final MyClass a, final MyClass b) { a_ = a; b_ = b; } Of course, you also have to explain why a and b are marked 'final' when they already are implicitly. You have to tell the novice that you are specifying 'final' to turn off 'const'. I expect that to elicit a: "Huh!??!" response. Implicit actions are dangerous, which is a lesson we should have learned in spades from C++. Every day I have to deal with scary smart pointer solutions that have each, in their infinite wisdom, exposed implicit conversion to T*, and have bent over backwards to try to prevent the types of programming mistakes that entailed. I really don't see what is so evil about putting 'in' on your arguments. There are a few other places where the absence of something means something. Member functions, for instance. You don't declare the 'this' parameter, as it is implicit. And yes, you can turn it off with 'static'. But it's not like member functions are declared as: member static int foo(); and spelling out: static int foo(); turns off the 'member' attribute. That would be silly. Another place where implicit stuff goes on is builtin type promotion. Think signed vs. unsigned. Who thinks the current rules are A Good Thing(TM) and cannot possibly be improved? At one time, C had 'implicit int' return types. C++ removed them. Why? Because 'implicit int' is A Bad Thing(TM). C++ has implicit template instantiation. D doesn't. While there are probably people who would like D to have implicit template instantiation, it would make the language much more context-sensitive than it is now, because parsing would require symbol table lookups. So if you look at many other areas where 'implicit X' has been tried, you see a lot of examples of bad features, some of which were actually removed. Let's think very carefully and critically before we assume 'implicit in' is not one of them...it's only one char away from 'implicit int'. ;> Dave
Jun 24 2007
David B. Held Wrote:So if you look at many other areas where 'implicit X' has been tried, you see a lot of examples of bad features, some of which were actually removed. Let's think very carefully and critically before we assume 'implicit in' is not one of them...it's only one char away from 'implicit int'. ;>But what's to stop someone turning your argument completely around? *Without* CbD, you can modify the variables even without asking for it: the variable types are implicitly mutable. But *with* CbD, you have to explicitly say, "I want this to be mutable." Doesn't this make it more explicit: a good thing? I agree that things like using 'ref' or 'final' don't entirely make sense to 'turn const off.' But I don't see this problem with a 'mutable' annotation, instead. Reiner
Jun 24 2007
Reiner Pope a écrit :David B. Held Wrote:Well, if this was always true, this would be a good thing, unfortunately this isn't true when there is some aliasing between a mutable parameter and a const parameter.. Sure, aliasing doesn't occur often in real life, but it's still a possibility that you have to take into account in your code.. [And no I don't have a simple solution] renoXSo if you look at many other areas where 'implicit X' has been tried, you see a lot of examples of bad features, some of which were actually removed. Let's think very carefully and critically before we assume 'implicit in' is not one of them...it's only one char away from 'implicit int'. ;>But what's to stop someone turning your argument completely around? *Without* CbD, you can modify the variables even without asking for it: the variable types are implicitly mutable. But *with* CbD, you have to explicitly say, "I want this to be mutable." Doesn't this make it more explicit: a good thing?I agree that things like using 'ref' or 'final' don't entirely make sense to 'turn const off.' But I don't see this problem with a 'mutable' annotation, instead. Reiner
Jun 28 2007
On Thu, 28 Jun 2007 21:25:44 +0200, renoX wrote:Reiner Pope a écrit :What does your point have to do with whether or not 'const' is the default action (if the coder has not said otherwise)? The aliasing issue you point out is the same regardless of whether 'const' is the default or not. Unless I've completely misunderstood you. -- Derek Parnell Melbourne, Australia skype: derek.j.parnellDavid B. Held Wrote:Well, if this was always true, this would be a good thing, unfortunately this isn't true when there is some aliasing between a mutable parameter and a const parameter.. Sure, aliasing doesn't occur often in real life, but it's still a possibility that you have to take into account in your code..So if you look at many other areas where 'implicit X' has been tried, you see a lot of examples of bad features, some of which were actually removed. Let's think very carefully and critically before we assume 'implicit in' is not one of them...it's only one char away from 'implicit int'. ;>But what's to stop someone turning your argument completely around? *Without* CbD, you can modify the variables even without asking for it: the variable types are implicitly mutable. But *with* CbD, you have to explicitly say, "I want this to be mutable." Doesn't this make it more explicit: a good thing?
Jun 28 2007
On Sun, 24 Jun 2007 18:02:42 -0700, David B. Held wrote: A number of very good discussion points and I'm not sure if I am qualified to respond, but fools rush in, as they say ... <g>I think an interesting point was brought up in the earlier CbD thread, which is specifically the issue of c'tors, and generally, the issue of passing mutable references. So consider: class MyObj { this(MyClass a, MyClass b) { a_ = a; b_ = b; } private: MyClass a_; MyClass b_; } With CbD, this code is incorrect. Would you like to explain to a novice programmer why?Ok ... how about ... * Unless told otherwise, data (in objects) passed to a function can not be modified. * Unless told otherwise, data (in objects) local to the can be object can be modified. * Thus the assignment of the parameters to the private objects is contradictory.Now, let's try to fix it: this(ref MyClass a, ref MyClass b) { a_ = a; b_ = b; } This code works, but now you have to explain to the student that even though classes are always passed by reference in D, you have to actually spell it out explicitly sometimes.You are assuming current D syntax only. Another possibility is new syntax ... this(rw MyClass a, rw MyClass b) { a_ = a; b_ = b; } where, for the *sake of this argument*, the keyword 'rw' is used to tell the compiler and code reader that the code author is allowing the new object read-write access to the passed object data. The key to my thinking is that I'm distinguishing the passing mechanism from the access permissions. The fact that it is passed by reference is interesting but not the point of the exercise, which is to tell the object/function what are its access limitations/permissions to passed data. The compiler can determine the most efficient passing mechanism independently of the access permissions.And further note that the only reason this form is not less efficient is because references to references are collapsed to simple references. Otherwise, there would be an extra level of indirection here.Forget the parameter passing mechanics and instead concentrate on what the code author is allowing the function/object to be able to do.Here's another try: this(final MyClass a, final MyClass b) { a_ = a; b_ = b; } Of course, you also have to explain why a and b are marked 'final' when they already are implicitly. You have to tell the novice that you are specifying 'final' to turn off 'const'. I expect that to elicit a: "Huh!??!" response.So would I, and this is plainly a poor solution. But what about ... class MyObj { this(MyClass a, MyClass b) { a_ = a; b_ = b; } private final invariant: // Can be assigned to once, but from then on neither the // reference nor its data can be modified. MyClass a_; MyClass b_; } Or if that's not what the author intended ... class MyObj { this(MyClass a, MyClass b) { a_ = a.clone; // deep copy b_ = b.clone; } private: MyClass a_; MyClass b_; } Or maybe that's not what the author intended either, so how about ... class MyObj { this(MyClass a, MyClass b) { a_ = a.dup; // shallow copy b_ = b.dup; } private: MyClass a_; MyClass b_; } See how the using of implicit 'read-write' function parameters disguises the author's intentions?Implicit actions are dangerous, which is a lesson we should have learned in spades from C++.Well, I'd say that they *can* be dangerous rather than every case is always dangerous. But in any case, we already have implicitness in D parameter passing, namely that implicitly data access is read-write. In other words, if one does not use 'const' and does not use 'invariant' then the default (implicit) action is to allow read-write access. Are you saying that we should not even have that?!Every day I have to deal with scary smart pointer solutions that have each, in their infinite wisdom, exposed implicit conversion to T*, and have bent over backwards to try to prevent the types of programming mistakes that entailed. I really don't see what is so evil about putting 'in' on your arguments.Nothing about putting 'in' in is evil and I think that nobody is actually saying that either. The point is which is the better compromise in terms of making code cheaper to write and maintain? If the default action is to allow "read-only" access it can be argued that it helps the compiler (and code reader) to identify code that causes side-effects by making such code explicitly say that's what it might be doing. If the default action is "read-write" it increases the effort to determine if a function is actually changing data or not; one has to read the function code to make the assessment rather than just look at its signature. Now I know this does not yet apply to accessing public or package identifiers but it does help with know how a function might handle the data passed to it.There are a few other places where the absence of something means something. Member functions, for instance. You don't declare the 'this' parameter, as it is implicit. And yes, you can turn it off with 'static'. But it's not like member functions are declared as: member static int foo(); and spelling out: static int foo(); turns off the 'member' attribute. That would be silly.This is a good case in point about the appropriateness of keyword semantics. I would like to see a more explicit and obvious method of identifying class-scoped functions. For example, something akin to: scope(class) int foo(); // instead of the overloaded 'static'.Another place where implicit stuff goes on is builtin type promotion. Think signed vs. unsigned. Who thinks the current rules are A Good Thing(TM) and cannot possibly be improved?Not me.At one time, C had 'implicit int' return types. C++ removed them. Why? Because 'implicit int' is A Bad Thing(TM). C++ has implicit template instantiation. D doesn't.Huh? I thought D does have a form of implicit template instantiation. -- Derek (skype: derek.j.parnell) Melbourne, Australia 25/06/2007 11:32:57 AM
Jun 24 2007
Derek Parnell escribió:On Sun, 24 Jun 2007 18:02:42 -0700, David B. Held wrote:D does have it. In fact, I remember Walter once said that ideally it should work the same way that C++ does. -- Carlos Santander BernalAt one time, C had 'implicit int' return types. C++ removed them. Why? Because 'implicit int' is A Bad Thing(TM). C++ has implicit template instantiation. D doesn't.Huh? I thought D does have a form of implicit template instantiation.
Jun 24 2007
David B. Held wrote:I think an interesting point was brought up in the earlier CbD thread, which is specifically the issue of c'tors, and generally, the issue of passing mutable references. So consider: class MyObj { this(MyClass a, MyClass b) { a_ = a; b_ = b; } private: MyClass a_; MyClass b_; } With CbD, this code is incorrect. Would you like to explain to a novice programmer why? Now, let's try to fix it: this(ref MyClass a, ref MyClass b) { a_ = a; b_ = b; } This code works, but now you have to explain to the student that even though classes are always passed by reference in D, you have to actually spell it out explicitly sometimes. And further note that the only reason this form is not less efficient is because references to references are collapsed to simple references. Otherwise, there would be an extra level of indirection here.What happened to inout? To me, using inout would make perfect sense to a student. Is ref just an alias for inout or is there something deeper that I'm missing?
Jun 24 2007
"David B. Held" <dheld codelogicconsulting.com> schrieb (Sun, 24 Jun 2007 18:02:42 -0700):this(ref MyClass a, ref MyClass b) { a_ = a; b_ = b; }Ack, having to write ref or final in order to remove const is ugly. What about an anti-const keyword? Is there anything that speaks against the following? this(mutable MyClass a, mutable MyClass b) { // ... } I'm not sure if mutable is already some keyword so there may be better choices (noconst/nofinal/noinvariant? volatile?) Henning -- GPG Public Key: http://keyserver.ganneff.de:11371/pks/lookup?op=get&search=0xDDD6D36D41911851 Fingerprint: 344F 4072 F038 BB9E B35D E6AB DDD6 D36D 4191 1851
Jun 25 2007
David B. Held wrote:I think an interesting point was brought up in the earlier CbD thread, which is specifically the issue of c'tors, and generally, the issue of passing mutable references. So consider: class MyObj { this(MyClass a, MyClass b) { a_ = a; b_ = b; } private: MyClass a_; MyClass b_; } With CbD, this code is incorrect. Would you like to explain to a novice programmer why?Because you're retaining a writable copy of a and b, so the class could modify them at any future time. You probably didn't intend to do that. You need to make a_ and b_ const. But if you truly want to be able to modify them later, you obviously need them to be inout. I don't see how this case is any different to a member function void func(MyClass a, int b) { a.dosomething(); // of course this is incorrect, // you need to get non-const access to a. } I don't see any problem here.
Jun 25 2007