www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Const by Default

reply "David B. Held" <dheld codelogicconsulting.com> writes:
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
next sibling parent reply Reiner Pope <none here.com> writes:
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'. ;>

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
parent reply renoX <renosky free.fr> writes:
Reiner Pope a écrit :
 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'. ;>

*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?

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] renoX
 
 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
parent Derek Parnell <derek psych.ward> writes:
On Thu, 28 Jun 2007 21:25:44 +0200, renoX wrote:

 Reiner Pope a écrit :
 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'. ;>

*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?

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..

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.parnell
Jun 28 2007
prev sibling next sibling parent reply Derek Parnell <derek nomail.afraid.org> writes:
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
parent Carlos Santander <csantander619 gmail.com> writes:
Derek Parnell escribió:
 On Sun, 24 Jun 2007 18:02:42 -0700, David B. Held wrote:
 
 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.

D does have it. In fact, I remember Walter once said that ideally it should work the same way that C++ does. -- Carlos Santander Bernal
Jun 24 2007
prev sibling next sibling parent Jason House <jason.james.house gmail.com> writes:
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
prev sibling next sibling parent Henning Hasemann <hhasemann web.de> writes:
"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
prev sibling parent Don Clugston <dac nospam.com.au> writes:
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