Jump to page: 1 2
Thread overview
Can you publicly alias a private type?
Jun 21, 2011
Peter Alexander
Jun 21, 2011
Jonathan M Davis
Jun 21, 2011
Nick Sabalausky
Jun 21, 2011
Trass3r
Jun 21, 2011
Trass3r
Jun 22, 2011
Nick Sabalausky
Jun 22, 2011
Peter Alexander
Jun 22, 2011
Nick Sabalausky
Jun 22, 2011
Peter Alexander
Jun 22, 2011
Nick Sabalausky
Jun 22, 2011
Peter Alexander
Jun 22, 2011
Nick Sabalausky
Jun 22, 2011
so
June 21, 2011
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.



June 21, 2011
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
June 21, 2011
"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.



June 21, 2011
> 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;
June 21, 2011
Or more precisely to disallow instantiation by the user and only provide a finite set of (nicer) methods.
June 22, 2011
"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).


June 22, 2011
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.

June 22, 2011
"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) {}



June 22, 2011
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.

June 22, 2011
"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.


« First   ‹ Prev
1 2