www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Can you publicly alias a private type?

reply Peter Alexander <peter.alexander.au gmail.com> writes:
Is the following legal D?

// module A
private class Foo;
public alias Foo Bar;

// module B
import A;
Bar b; // I can't use Foo, but can I use Bar?


I'm adding module level protection for types into DMD and wondering if 
this should be legal.
Jun 21 2011
next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On 2011-06-21 07:14, Peter Alexander wrote:
 Is the following legal D?
 
 // module A
 private class Foo;
 public alias Foo Bar;
 
 // module B
 import A;
 Bar b; // I can't use Foo, but can I use Bar?
 
 
 I'm adding module level protection for types into DMD and wondering if
 this should be legal.

IIRC, there was a big debate about that a while back, but I don't remember the outcome. I would have thought that it would be illegal, but I don't know. Regardless, alias and access modifiers are broken anyway ( http://d.puremagic.com/issues/show_bug.cgi?id=6013 ). Also, these bugs seem to relate to the issue: http://d.puremagic.com/issues/show_bug.cgi?id=1161 http://d.puremagic.com/issues/show_bug.cgi?id=4533 - Jonathan M Davis
Jun 21 2011
prev sibling parent reply "Nick Sabalausky" <a a.a> writes:
"Peter Alexander" <peter.alexander.au gmail.com> wrote in message 
news:itq945$2ag0$1 digitalmars.com...
 Is the following legal D?

 // module A
 private class Foo;
 public alias Foo Bar;

 // module B
 import A;
 Bar b; // I can't use Foo, but can I use Bar?


 I'm adding module level protection for types into DMD and wondering if 
 this should be legal.

I'm no authority on this, but I'm pretty sure that's supposed to be legal. Access modifiers work non-transitively on just the given symbol. And it's a useful idiom in cases that are more complex than that (ex: Using a templated alias to provide a cleaner public interface to a private implementation that has a more complex interface). Your code above is analogous to this, which *definitely* is supposed to be legal: // module A private foo() {} public bar() { foo(); } // module B import A; bar(); // I can't use foo(), but can I use bar(). Nothin' wrong with that.
Jun 21 2011
next sibling parent reply Trass3r <un known.com> writes:
 Using a templated alias to provide a cleaner public interface to a  
 private implementation that has a more complex interface).

Yep, I also used this feature to restrict instantiations for a template function to a known finite set: private void foo(alias func)() { ... func(); ... } public alias foo!f bar; public alias foo!g bla;
Jun 21 2011
parent "Nick Sabalausky" <a a.a> writes:
"Trass3r" <un known.com> wrote in message news:op.vxf267mf3ncmek enigma...
 Or more precisely to disallow instantiation by the user and only provide a 
 finite set of (nicer) methods.

Yea. I did something similar in an early draft of the "Dynamic Fallback" example in my contest article ( http://www.semitwist.com/articles/EfficientAndFlexible/SinglePage/#part6-4 ). In the final version, I ended up defining the DynamicGizmo separately from the compile-time-configurable Gizmo, because in that particular case, it just happened to work out a little cleaner that way. But, as I said in the article, "It would have also been possible to use a single definition for both the metaprogramming Gizmo and the DynamicGizmo...Doing so would probably be a good idea if only part of your struct is affected by the change from runtime options to compile-time options." And that's exactly how I originally had it in an earlier draft: // Gizmo Implementation struct GizmoImpl(bool isDynamic, int _numPorts, bool _isSpinnable) { /+ ...snipped... +/ } Basically, if isDynamic is true, then it uses ordinary member variables for numPorts and isSpinnable instead of the template parameters. But since numPorts and isSpinnable are *only* applicable if isDynamic is false, that makes the interface a bit funky. Very funky, in fact, if you consider that GizmoImpl!(true,2,true) and GizmoImpl!(true,3,false) are separate types even though, semantically, they're supposed to be the same (since the last two params are not supposed to be applicable if isDynamic is true). So I cleaned up the interface like this: // A few convenience aliases just to clean up GizmoImpl's funky parameters: template Gizmo(int numPorts, bool isSpinnable) { alias GizmoImpl!(false, numPorts, isSpinnable) Gizmo; } alias GizmoImpl!(true, int.max, false) DynamicGizmo; Clearly, the intent is that actual users should only use Gizmo and DynamicGizmo (*especially* if they want a dynamic gizmo). GizmoImpl is just a private implementation detail that's only there so the two publically-visible types can share the same definition. This was just a one-module example, so I didn't make anything private, but if it were a real library module, then naturally you'd want GizmoImpl to be private and the aliases to be public. My Goldie parsing library makes heavy use of the same basic idiom for its "static-style" Token types ( See http://www.semitwist.com/goldie/APIOver/StatVsDyn/#Types and http://www.semitwist.com/goldie/APIOver/AmbiguousSym/ ). It has a few private "implementation" classes that, by necessity, aren't the most user-friendly (a big part of the issue is that, due to the nature of templates and classes, there *must* be a one-to-one mapping between the arguments to the templates and the actual resulting types). But then it exposes various public templated aliases to provide a clean, flexible system for referencing the implementation types. Another good use of access-expanding aliases was brought up by someone else in the last discussion we had about them. Suppose you want to privately import a module, but you want one of its symbols to be publically imported (maybe under an alternate name). Sure, you could probably do something like: import std.stdio; public import std.stdio : echo = writeln; But it's reasonable to expect that this would achieve the same thing: import std.stdio; alias writeln echo; And I suspect the alias method might be more flexible for metaprogramming. For instance, if you wanted to use metaprogramming to determine *which* symbol to publically import (which I can imagine could be a nifty trick).
Jun 21 2011
prev sibling next sibling parent Trass3r <un known.com> writes:
Or more precisely to disallow instantiation by the user and only provide a  
finite set of (nicer) methods.
Jun 21 2011
prev sibling next sibling parent reply Peter Alexander <peter.alexander.au gmail.com> writes:
On 21/06/11 7:59 PM, Nick Sabalausky wrote:
 "Peter Alexander"<peter.alexander.au gmail.com>  wrote in message
 news:itq945$2ag0$1 digitalmars.com...
 Is the following legal D?

 // module A
 private class Foo;
 public alias Foo Bar;

 // module B
 import A;
 Bar b; // I can't use Foo, but can I use Bar?


 I'm adding module level protection for types into DMD and wondering if
 this should be legal.

I'm no authority on this, but I'm pretty sure that's supposed to be legal. Access modifiers work non-transitively on just the given symbol. And it's a useful idiom in cases that are more complex than that (ex: Using a templated alias to provide a cleaner public interface to a private implementation that has a more complex interface). Your code above is analogous to this, which *definitely* is supposed to be legal: // module A private foo() {} public bar() { foo(); } // module B import A; bar(); // I can't use foo(), but can I use bar(). Nothin' wrong with that.

That is similar, but not analogous. The problem arises when we come to more complex types: // module A private class Foo; public alias Foo[] Foos; // module B void main() { Foos fs; // Legal? Seems so. Foo f = fs[0]; // Clearly can't use Foo here, it's private. auto f2 = fs[0]; // Legal? fs[0].memFun(); // Legal? } Of course, the same problems arise if you alias something like Array!Foo. So the difference between class aliasing and functional composition is that class aliasing does not completely encapsulate the class, while the function does. Function composition is more analogous to structural composition. In my opinion, it should be illegal to publicly alias a private type (or any derivative type) because it introduces too many issues, as I have demonstrated above.
Jun 22 2011
parent reply "Nick Sabalausky" <a a.a> writes:
"Peter Alexander" <peter.alexander.au gmail.com> wrote in message 
news:itsl71$1cnq$1 digitalmars.com...
 On 21/06/11 7:59 PM, Nick Sabalausky wrote:
 "Peter Alexander"<peter.alexander.au gmail.com>  wrote in message
 news:itq945$2ag0$1 digitalmars.com...
 Is the following legal D?

 // module A
 private class Foo;
 public alias Foo Bar;

 // module B
 import A;
 Bar b; // I can't use Foo, but can I use Bar?


 I'm adding module level protection for types into DMD and wondering if
 this should be legal.

I'm no authority on this, but I'm pretty sure that's supposed to be legal. Access modifiers work non-transitively on just the given symbol. And it's a useful idiom in cases that are more complex than that (ex: Using a templated alias to provide a cleaner public interface to a private implementation that has a more complex interface). Your code above is analogous to this, which *definitely* is supposed to be legal: // module A private foo() {} public bar() { foo(); } // module B import A; bar(); // I can't use foo(), but can I use bar(). Nothin' wrong with that.

That is similar, but not analogous. The problem arises when we come to more complex types: // module A private class Foo; public alias Foo[] Foos; // module B void main() { Foos fs; // Legal? Seems so. Foo f = fs[0]; // Clearly can't use Foo here, it's private. auto f2 = fs[0]; // Legal? fs[0].memFun(); // Legal? } Of course, the same problems arise if you alias something like Array!Foo. So the difference between class aliasing and functional composition is that class aliasing does not completely encapsulate the class, while the function does. Function composition is more analogous to structural composition. In my opinion, it should be illegal to publicly alias a private type (or any derivative type) because it introduces too many issues, as I have demonstrated above.

That issue is not unique to alias: private class Foo {} public bar(Foo f) {}
Jun 22 2011
parent reply Peter Alexander <peter.alexander.au gmail.com> writes:
On 22/06/11 1:41 PM, Nick Sabalausky wrote:
 "Peter Alexander"<peter.alexander.au gmail.com>  wrote in message
 news:itsl71$1cnq$1 digitalmars.com...
 On 21/06/11 7:59 PM, Nick Sabalausky wrote:
 "Peter Alexander"<peter.alexander.au gmail.com>   wrote in message
 news:itq945$2ag0$1 digitalmars.com...
 Is the following legal D?

 // module A
 private class Foo;
 public alias Foo Bar;

 // module B
 import A;
 Bar b; // I can't use Foo, but can I use Bar?


 I'm adding module level protection for types into DMD and wondering if
 this should be legal.

I'm no authority on this, but I'm pretty sure that's supposed to be legal. Access modifiers work non-transitively on just the given symbol. And it's a useful idiom in cases that are more complex than that (ex: Using a templated alias to provide a cleaner public interface to a private implementation that has a more complex interface). Your code above is analogous to this, which *definitely* is supposed to be legal: // module A private foo() {} public bar() { foo(); } // module B import A; bar(); // I can't use foo(), but can I use bar(). Nothin' wrong with that.

That is similar, but not analogous. The problem arises when we come to more complex types: // module A private class Foo; public alias Foo[] Foos; // module B void main() { Foos fs; // Legal? Seems so. Foo f = fs[0]; // Clearly can't use Foo here, it's private. auto f2 = fs[0]; // Legal? fs[0].memFun(); // Legal? } Of course, the same problems arise if you alias something like Array!Foo. So the difference between class aliasing and functional composition is that class aliasing does not completely encapsulate the class, while the function does. Function composition is more analogous to structural composition. In my opinion, it should be illegal to publicly alias a private type (or any derivative type) because it introduces too many issues, as I have demonstrated above.

That issue is not unique to alias: private class Foo {} public bar(Foo f) {}

Yeah, there's quite a few places where this crops up. Basically, I think that private types (and related types, e.g. arrays of private types and templates of private types) should be disallowed from appearing anywhere in the public interface of a module. So that would include: - aliases - function arguments - function return types There's probably more places where we'd need to add checks.
Jun 22 2011
parent reply "Nick Sabalausky" <a a.a> writes:
"Peter Alexander" <peter.alexander.au gmail.com> wrote in message 
news:itsp16$1jl5$1 digitalmars.com...
 On 22/06/11 1:41 PM, Nick Sabalausky wrote:
 "Peter Alexander"<peter.alexander.au gmail.com>  wrote in message
 news:itsl71$1cnq$1 digitalmars.com...
 On 21/06/11 7:59 PM, Nick Sabalausky wrote:
 "Peter Alexander"<peter.alexander.au gmail.com>   wrote in message
 news:itq945$2ag0$1 digitalmars.com...
 Is the following legal D?

 // module A
 private class Foo;
 public alias Foo Bar;

 // module B
 import A;
 Bar b; // I can't use Foo, but can I use Bar?


 I'm adding module level protection for types into DMD and wondering if
 this should be legal.

I'm no authority on this, but I'm pretty sure that's supposed to be legal. Access modifiers work non-transitively on just the given symbol. And it's a useful idiom in cases that are more complex than that (ex: Using a templated alias to provide a cleaner public interface to a private implementation that has a more complex interface). Your code above is analogous to this, which *definitely* is supposed to be legal: // module A private foo() {} public bar() { foo(); } // module B import A; bar(); // I can't use foo(), but can I use bar(). Nothin' wrong with that.

That is similar, but not analogous. The problem arises when we come to more complex types: // module A private class Foo; public alias Foo[] Foos; // module B void main() { Foos fs; // Legal? Seems so. Foo f = fs[0]; // Clearly can't use Foo here, it's private. auto f2 = fs[0]; // Legal? fs[0].memFun(); // Legal? } Of course, the same problems arise if you alias something like Array!Foo. So the difference between class aliasing and functional composition is that class aliasing does not completely encapsulate the class, while the function does. Function composition is more analogous to structural composition. In my opinion, it should be illegal to publicly alias a private type (or any derivative type) because it introduces too many issues, as I have demonstrated above.

That issue is not unique to alias: private class Foo {} public bar(Foo f) {}

Yeah, there's quite a few places where this crops up. Basically, I think that private types (and related types, e.g. arrays of private types and templates of private types) should be disallowed from appearing anywhere in the public interface of a module. So that would include: - aliases - function arguments - function return types There's probably more places where we'd need to add checks.

Ultimately, the problem boils down to when a private type is required to use a public interface. I agree that's a potential issue. However, I don't believe that's relevent to the original scenario being discussed: private class Foo {} public alias Foo Bar; In your example, the user needs to be able to use the Foo symbol in order to get full use out of Foos. However, in my example, the user does *not* need to use the Foo symbol in order to get full use out of Bar. They can just use Bar in place of Foo. The "Foo" symbol is a necessary part of Foos's interface, but it is *not* a necessary part Bar's interface.
Jun 22 2011
parent reply Peter Alexander <peter.alexander.au gmail.com> writes:
On 22/06/11 2:19 PM, Nick Sabalausky wrote:
 In your example, the user needs to be able to use the Foo symbol in order to
 get full use out of Foos. However, in my example, the user does *not* need
 to use the Foo symbol in order to get full use out of Bar. They can just use
 Bar in place of Foo. The "Foo" symbol is a necessary part of Foos's
 interface, but it is *not* a necessary part Bar's interface.

They don't *need* the Foo symbol: // module A private class Foo; public alias Foo[] Foos; Foos getFoos(); // module B import A; void main() { Foos foos = getFoos(); foos[0].someFunc(); auto f = foos[0]; } Here, module B hasn't used the Foo symbol, but it's as if Foo isn't private at all. I don't know about you, but this feel really wrong to me.
Jun 22 2011
parent "Nick Sabalausky" <a a.a> writes:
"Peter Alexander" <peter.alexander.au gmail.com> wrote in message 
news:itsqov$1mnt$1 digitalmars.com...
 On 22/06/11 2:19 PM, Nick Sabalausky wrote:
 In your example, the user needs to be able to use the Foo symbol in order 
 to
 get full use out of Foos. However, in my example, the user does *not* 
 need
 to use the Foo symbol in order to get full use out of Bar. They can just 
 use
 Bar in place of Foo. The "Foo" symbol is a necessary part of Foos's
 interface, but it is *not* a necessary part Bar's interface.

They don't *need* the Foo symbol: // module A private class Foo; public alias Foo[] Foos; Foos getFoos(); // module B import A; void main() { Foos foos = getFoos(); foos[0].someFunc(); auto f = foos[0]; } Here, module B hasn't used the Foo symbol, but it's as if Foo isn't private at all. I don't know about you, but this feel really wrong to me.

Perhaps. I could probably be persuaded either way. But again, I don't see that as being relevent to: private class Foo; public alias Foo Bar;
Jun 22 2011
prev sibling parent so <so so.so> writes:
On Wed, 22 Jun 2011 14:52:52 +0300, Peter Alexander  
<peter.alexander.au gmail.com> wrote:

 // module A
 private class Foo;
 public alias Foo[] Foos;

 // module B
 void main()
 {
      Foos fs; // Legal? Seems so.
      Foo f = fs[0]; // Clearly can't use Foo here, it's private.
      auto f2 = fs[0]; // Legal?
      fs[0].memFun(); // Legal?
 }

I don't think this is a big problem because both Foo and Foos are defined/aliased by the library developer. Similar reasoning would be applied to many other cases where a library developer allows/disallows/does something that ends up with errors on the user side, intended or not, competent or not.
 Of course, the same problems arise if you alias something like Array!Foo.

Well, as long as Foo is private. On Nick's example: private class Foo {} public bar(Foo f) {} Just like the Foo/Foos example this is also entirely lib developers fault, what is he thinking, and why is this enough? He could after all do something like this and be done with it. static this() { unleash_hell(); }
 So the difference between class aliasing and functional composition is  
 that class aliasing does not completely encapsulate the class, while the  
 function does. Function composition is more analogous to structural  
 composition.

 In my opinion, it should be illegal to publicly alias a private type (or  
 any derivative type) because it introduces too many issues, as I have  
 demonstrated above.

The template example alone is enough of a reason to have this IMO.
Jun 22 2011