View mode: basic / threaded / horizontal-split · Log in · Help
June 25, 2007
Const by Default
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
June 25, 2007
Re: Const by Default
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
June 25, 2007
Re: Const by Default
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
June 25, 2007
Re: Const by Default
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?
June 25, 2007
Re: Const by Default
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
June 25, 2007
Re: Const by Default
"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
June 25, 2007
Re: Const by Default
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.
June 28, 2007
Re: Const by Default
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'. ;>
> 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?

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
June 28, 2007
Re: Const by Default
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'. ;>
>> 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?
> 
> 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
Top | Discussion index | About this forum | D home