July 13, 2006
Walter Bright wrote:
> Dave wrote:
>> Everyone seems to agree that 'private' should not be accessible and the current behavior is a bug. What we're all wondering is if 'private' can also mean 'invisible' because that seems to be more intuitive. Than you don't have that extra level of complexity for lookup resolution and things like error messages describing a private interface.
> 
> The original reason why private members would be visible but not accessible has been forgotten. However, there were some significant issues brought up with making them invisible:
> 
> 1) function overloading - if various overloads of a function have different protections, different functions will be selected even though the same arguments are presented. This can be surprising when code is moved around. If they are visible, you'll get an error message instead of silently varying behavior.
> 
> 2) function overloading - one could lose the ability to 'poison' an operation on certain argument types, because instead the private function will not be seen and another selected.
> 
> 3) function overriding - if a private function in a derived class overrides a public one in a base class, this overriding will not happen if the private function is invisible. Not only does this break encapsulation, it prevents the design pattern of being able to 'poison' certain operations on a class.

I don't wish to belabor this, but it would seem that the above great reasons for "visible but not accessible" conflict with the current behavior described here:

http://www.digitalmars.com/drn-bin/wwwnews?digitalmars.D.bugs/7649

So there are probably some bugs in there, especially with regard to #3 above and #2 in Kris' post. This to me is a before 1.0 thing.

Thanks,

- Dave
July 13, 2006
Dave wrote:
> Dave wrote:
>> Lars Ivar Igesund wrote:
>>> Dave wrote:
>>>
>>>> Lars Ivar Igesund wrote:
>>>>  > Lucas Goss wrote:
>>>>  >
>>>>  >> Was there ever any resolve as to private being visible? I know
>>>> Walter
>>>>  >> said he saw the value of private by default, but what about private
>>>>  >> being visible? It just seems to have dropped off the radar and I
>>>> don't
>>>>  >> know if thats good or bad.
>>>>  >>
>>>>  >> Lucas
>>>>  >
>>>>  > Did you mean accessible? Anyway, see Bruno's post, although I'm
>>>> quite
>>>> sure
>>>>  > it won't be forgotten by some of us ;)
>>>>  >
>>>>
>>>> Here's my take - feel free to correct:
>>>>
>>>> - accessible: the symbol can be used (accessed). Must be visible as
>>>> well
>>>> for the lookup (I realize this is obvious).
>>>
>>> Yes, that would be the sane thing, but one of the privacy problems in
>>> D, is
>>> that it has been possible to access symbols by using the FQN, even
>>> though
>>> it really isn't visible (declared to be private). As it is, I see no
>>> reason
>>> to differ between visible and accessible, IMO they're two sides of
>>> the same
>>> coin, or should be.
>>>
>>
>> See: http://www.digitalmars.com/drn-bin/wwwnews?digitalmars.D/39754
> 
> A little too terse, I was... I tend to agree that they should be two sides of the same coin but the gist of it is that they aren't in D because they aren't in C++ for (possibly obscure) reasons that might not apply to D.
> 
> Everyone seems to agree that 'private' should not be accessible and the current behavior is a bug.

IMO 'private' works quite well now. The only problem is that there's no access control when using FQN's. I haven't yet tested all the special cases like inner classes, but overall it seems to be better now in 0.162.

> What we're all wondering is if 'private' can
> also mean 'invisible' because that seems to be more intuitive. Than you
> don't have that extra level of complexity for lookup resolution and
> things like error messages describing a private interface.

IMO more compiler logic here makes programmer more productive. I find
this situation analogous to includes in C/C++ vs. imports in D. C/C++ is
lacking a lot of great functionality there. I'm not that sure if it's
that much slower to handle both levels of accessibility separately, but
I can surely see the benefits arising from better compiler error messages.

AFAICS the only argument against current behavior is that people think it's somehow "easier" to implement a compiler that does only visibility checks. Of course it's a bit annoying to see that this "bug" has been open for many years now, but Walter has been long busy working on the cool new features that have added much to the value of D. But now that D is reaching stable, maybe it's finally time to find a solution to these. IMO fixing the FQN's would be enough. Walter, could it be possible to implement these checks as a special case for FQN's if it's too difficult to form some general rules for the order of phases (access control, name lookup)?

-- 
Jari-Matti
July 13, 2006
Jari-Matti Mäkelä wrote:
> Dave wrote:
>> Dave wrote:
>>> Lars Ivar Igesund wrote:
>>>> Dave wrote:
>>>>
>>>>> Lars Ivar Igesund wrote:
>>>>>  > Lucas Goss wrote:
>>>>>  >
>>>>>  >> Was there ever any resolve as to private being visible? I know
>>>>> Walter
>>>>>  >> said he saw the value of private by default, but what about private
>>>>>  >> being visible? It just seems to have dropped off the radar and I
>>>>> don't
>>>>>  >> know if thats good or bad.
>>>>>  >>
>>>>>  >> Lucas
>>>>>  >
>>>>>  > Did you mean accessible? Anyway, see Bruno's post, although I'm
>>>>> quite
>>>>> sure
>>>>>  > it won't be forgotten by some of us ;)
>>>>>  >
>>>>>
>>>>> Here's my take - feel free to correct:
>>>>>
>>>>> - accessible: the symbol can be used (accessed). Must be visible as
>>>>> well
>>>>> for the lookup (I realize this is obvious).
>>>> Yes, that would be the sane thing, but one of the privacy problems in
>>>> D, is
>>>> that it has been possible to access symbols by using the FQN, even
>>>> though
>>>> it really isn't visible (declared to be private). As it is, I see no
>>>> reason
>>>> to differ between visible and accessible, IMO they're two sides of
>>>> the same
>>>> coin, or should be.
>>>>
>>> See: http://www.digitalmars.com/drn-bin/wwwnews?digitalmars.D/39754
>> A little too terse, I was... I tend to agree that they should be two
>> sides of the same coin but the gist of it is that they aren't in D
>> because they aren't in C++ for (possibly obscure) reasons that might not
>> apply to D.
>>
>> Everyone seems to agree that 'private' should not be accessible and the
>> current behavior is a bug.
> 
> IMO 'private' works quite well now. The only problem is that there's no
> access control when using FQN's. I haven't yet tested all the special
> cases like inner classes, but overall it seems to be better now in 0.162.
> 
>> What we're all wondering is if 'private' can
>> also mean 'invisible' because that seems to be more intuitive. Than you
>> don't have that extra level of complexity for lookup resolution and
>> things like error messages describing a private interface.
> 
> IMO more compiler logic here makes programmer more productive. I find
> this situation analogous to includes in C/C++ vs. imports in D. C/C++ is
> lacking a lot of great functionality there. I'm not that sure if it's
> that much slower to handle both levels of accessibility separately, but
> I can surely see the benefits arising from better compiler error messages.
> 
> AFAICS the only argument against current behavior is that people think
> it's somehow "easier" to implement a compiler that does only visibility
> checks. Of course it's a bit annoying to see that this "bug" has been
> open for many years now, but Walter has been long busy working on the
> cool new features that have added much to the value of D. But now that D
> is reaching stable, maybe it's finally time to find a solution to these.
> IMO fixing the FQN's would be enough. Walter, could it be possible to
> implement these checks as a special case for FQN's if it's too difficult
> to form some general rules for the order of phases (access control, name
> lookup)?
> 

Here's an example that doesn't require FQN to expose the bug:

module foo;
void bar(int i)
{
  printf("bar(int)\n");
}
private void bar(char[] str)
{
  printf("bar(char[]): %s\n",cast(char*)str);
}

module main;
import foo;
void main()
{
  bar("test");
}

If you comment bar(int) out then it issues the correct compiler message otherwise it happily prints what you'd expect if bar(char[]) was not private.

- Dave
July 13, 2006
Dave wrote:
> Here's an example that doesn't require FQN to expose the bug:
> 
> module foo;
> void bar(int i)
> {
>   printf("bar(int)\n");
> }
> private void bar(char[] str)
> {
>   printf("bar(char[]): %s\n",cast(char*)str);
> }
> 
> module main;
> import foo;
> void main()
> {
>   bar("test");
> }
> 
> If you comment bar(int) out then it issues the correct compiler message
> otherwise it happily prints what you'd expect if bar(char[]) was not
> private.

Doh, didn't see that :">

-- 
Jari-Matti
July 13, 2006
Walter Bright wrote:
> Dave wrote:
>> Everyone seems to agree that 'private' should not be accessible and the current behavior is a bug. What we're all wondering is if 'private' can also mean 'invisible' because that seems to be more intuitive. Than you don't have that extra level of complexity for lookup resolution and things like error messages describing a private interface.
> 
> The original reason why private members would be visible but not accessible has been forgotten. However, there were some significant issues brought up with making them invisible:

Thank you very much for looking into this.

> 1) function overloading - if various overloads of a function have different protections, different functions will be selected even though the same arguments are presented. This can be surprising when code is moved around. If they are visible, you'll get an error message instead of silently varying behavior.

I would assert that mixing protection attributes on overloads is Bad Programming.  Further, I think the principle of least surprise may not be a particularly strong argument here because the truly surprising behavior seems to be that private symbols are considered at all.  That the current C++ behavior has been elevated to the status of a newbie FAQ question is ample evidence here.

> 2) function overloading - one could lose the ability to 'poison' an operation on certain argument types, because instead the private function will not be seen and another selected.

So basically intentionally exploiting the above behavior to prevent certain implicit conversions from occurring if the programmer does not have access to the base class (where it may be possible to add an 'explicit' in C++).  Is this practice at all common?  And how much does it apply to D, given D's far simpler overload resolution mechanism?

> 3) function overriding - if a private function in a derived class overrides a public one in a base class, this overriding will not happen if the private function is invisible. Not only does this break encapsulation, it prevents the design pattern of being able to 'poison' certain operations on a class.

Perhaps there should be some discussion of what 'private' means in D compared to C++.  For example, private functions in D are not overridable, but they are in C++.  I think this represents a somewhat fundamental departure from the C++ mindset, and perhaps warrants a different interpretation of how symbol resolution should be handled. Also, we have the benefit of weighing nearly 20 years of experience with the C++ mechanism, which I suspect had to be considered in purely hypothetical terms when the original decision was made.

In hindsight, does the C++ mechanism seem optimal for C++?  And is the language structure of D sufficiently similar to C++ that it is optimal for D, by extension?  I realize that these are difficult questions to answer, but I don't want to close the door on the idea just yet simply because the decision for C++ involved reasons that *may* also apply to D.  At the very least, it does seem clear that this behavior is confusing for novice programmers, and this is a red flag to me that the design may not be ideal.


Sean
July 13, 2006
Walter Bright wrote:
> 
> The original reason why private members would be visible but not accessible has been forgotten. However, there were some significant issues brought up with making them invisible:
> 
> 1) function overloading - if various overloads of a function have different protections, different functions will be selected even though the same arguments are presented. This can be surprising when code is moved around. If they are visible, you'll get an error message instead of silently varying behavior.
> 
> 2) function overloading - one could lose the ability to 'poison' an operation on certain argument types, because instead the private function will not be seen and another selected.
> 
> 3) function overriding - if a private function in a derived class overrides a public one in a base class, this overriding will not happen if the private function is invisible. Not only does this break encapsulation, it prevents the design pattern of being able to 'poison' certain operations on a class.

Interesting. But if I use a third party library:

import lib.thirdparty;
...
commonFuncName() // error (declared in my module and in thirdparty)

I would be dumbfounded. Especially when I look at there documentation and there is no "commonFuncName".

I had some more thoughts... but I lost them in the midst of other responsibilities. I'll see if I can remember.

Lucas
July 13, 2006
Lucas Goss wrote:
> Walter Bright wrote:
>>
>> The original reason why private members would be visible but not accessible has been forgotten. However, there were some significant issues brought up with making them invisible:
>>
>> 1) function overloading - if various overloads of a function have different protections, different functions will be selected even though the same arguments are presented. This can be surprising when code is moved around. If they are visible, you'll get an error message instead of silently varying behavior.
>>
>> 2) function overloading - one could lose the ability to 'poison' an operation on certain argument types, because instead the private function will not be seen and another selected.
>>
>> 3) function overriding - if a private function in a derived class overrides a public one in a base class, this overriding will not happen if the private function is invisible. Not only does this break encapsulation, it prevents the design pattern of being able to 'poison' certain operations on a class.
>
> Interesting. But if I use a third party library:
>
> import lib.thirdparty;
> ...
> commonFuncName() // error (declared in my module and in thirdparty)
>
> I would be dumbfounded. Especially when I look at there documentation and there is no "commonFuncName".
>
> I had some more thoughts... but I lost them in the midst of other responsibilities. I'll see if I can remember.
>
> Lucas

It will look for and use the one in your module. If it is in your library that is also imported, then you'd get something like:

mylib.d(123): function mylib.baz conflicts with otherLib.baz at otherLib.d(456)

Then right now you could alias or hopefully soon use the new import syntax to disambiguate.

- Dave
July 14, 2006
Dave wrote:
> 
> It will look for and use the one in your module. If it is in your library that is also imported, then you'd get something like:
> 
> mylib.d(123): function mylib.baz conflicts with otherLib.baz at otherLib.d(456)
> 
> Then right now you could alias or hopefully soon use the new import syntax to disambiguate.
> 
> - Dave

That makes no sense to me (I understand what you're talking about, but it's like a gotcha). How could my public function conflict with a private function? I think the problem is that private functions are imported into the current namespace (I'm pretty sure that's been said before).

I remembered what I was thinking previously...
Is there any way to make private functions only be available by fully qualified name lookup and not imported into the current namespace? Or would that be too much work?

Lucas
July 14, 2006
On Thu, 13 Jul 2006 11:37:50 -0700, Walter Bright wrote:

> Dave wrote:
>> Everyone seems to agree that 'private' should not be accessible and the current behavior is a bug. What we're all wondering is if 'private' can also mean 'invisible' because that seems to be more intuitive. Than you don't have that extra level of complexity for lookup resolution and things like error messages describing a private interface.
> 
> The original reason why private members would be visible but not accessible has been forgotten. However, there were some significant issues brought up with making them invisible:
> 
> 1) function overloading - if various overloads of a function have different protections, different functions will be selected even though the same arguments are presented. This can be surprising when code is moved around. If they are visible, you'll get an error message instead of silently varying behavior.

Error messages are good, but don't let private stuff be accessible out of
scope.

> 2) function overloading - one could lose the ability to 'poison' an operation on certain argument types, because instead the private function will not be seen and another selected.

As it should. 'private' means 'this is mine and not yours so don't touch, okay!?'

> 3) function overriding - if a private function in a derived class overrides a public one in a base class, this overriding will not happen if the private function is invisible. Not only does this break encapsulation, it prevents the design pattern of being able to 'poison' certain operations on a class.

And yet to me this sounds like a good thing.

Maybe I am confusing visibility and accessibility here, but in my simple way of looking at things, 'private' should have the meaning nothing can reference a private member by name except for things in the same scope and things in the same module. In simple layman's terms, 'keep your grubby hands off my member' ;-)

The current implementation of private in D is just about /perfect/, except for the couple of acknowledged bugs and the error messages that reveal private information.

Bug 1. FQN usage ignores privacy.
Bug 2. Function matching ignores privacy if a public name also exists.

-- 
Derek
(skype: derek.j.parnell)
Melbourne, Australia
"Down with mediocrity!"
14/07/2006 10:53:38 AM
July 14, 2006
Walter Bright wrote:
> Dave wrote:
>> Everyone seems to agree that 'private' should not be accessible and the current behavior is a bug. What we're all wondering is if 'private' can also mean 'invisible' because that seems to be more intuitive. Than you don't have that extra level of complexity for lookup resolution and things like error messages describing a private interface.
> 
> The original reason why private members would be visible but not accessible has been forgotten. However, there were some significant issues brought up with making them invisible:
> 
> 1) function overloading - if various overloads of a function have different protections, different functions will be selected even though the same arguments are presented. This can be surprising when code is moved around. If they are visible, you'll get an error message instead of silently varying behavior.
> 

If I am using a library do I really need to get error messages about private functions from that library? To me this sounds not only like a bad design of that library but also of the language that allows it.

As others said mixing various protections on different functions of the same name is most of the times a bad design. But if it isn't and it is intentional, then what is the point in getting an error that something is private. If something is private isn't it then meant not to be seen?

> 2) function overloading - one could lose the ability to 'poison' an operation on certain argument types, because instead the private function will not be seen and another selected.
> 
> 3) function overriding - if a private function in a derived class overrides a public one in a base class, this overriding will not happen if the private function is invisible. Not only does this break encapsulation, it prevents the design pattern of being able to 'poison' certain operations on a class.

Various 'poisoning' techniques sound a lot like C++ to me. There one can make classes that can not be constructed, can not be inherited from, can not be passed by value, cannot be allocated on stack and many different things by making something that is private. But are these techniques realistic or even possible in D?

I don't know what to do about 3) but is this the way to do things in D as is in C++?