Relationship between access modifiers and type

I was thinking the other day it'd be interesting if C++ supported something like the following:

template<modifier T>
class A : public SomeClass {
public:
    void GuarunteedMethod();
    void AlwaysThereMethod();

T:
    void DependsOnUser();
    void MayNotBePublic();
};

template<modifier T>
class B : T SomeClass {
};

Then somewhere in code using the templates:

A<public> X; // Object that has public DependsOnUser()
A<private> X; // Object that doesn't

B<public> X; // publically inherits from SomeClass
B<protected> X; // protectedly inherits from SomeClass

You can get the same effect in today's C++ as in A by splitting up the class more or in B's case by having dummy intermediary classes.

A class has two different types I think, that which it presents to calling code, public interface, and that which it provides to extending code, protected interface (you could argue there's a third, the types that it presents to itself but that's not really an observer relationship). So in the sense that it changes the resulting type, the above C++ extension is consistent with the notion of templates as type factories.

This got me thinking about what exactly the relationship between access modifiers and a class's type is. I'm not sure whether or not you could say that access modifiers change the type of member functions. Access modifiers do impose a compile time limit on who can use the function, which sounds related to types, but usually we only think of the type of a function to be it's return type + parameter types.

Maybe I'm just caught up in the wording and access modifiers can be thought of as simply defining complex scopes, but it seems different to me from the concept of scope because it's not that the symbols aren't visible, it's that they're disallowed.

Thoughts? :)

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.

why

Why would you want to do that? I mean, if you can make it public, why don't you just do it across the board?

fair question ;p

If you wanted to quickly create classes where sometimes you want to really subclass from SomeClass (B<public>), and other times you want to inherit from it solely because the implementation of B requires it (B<private>) then it would be useful. I can see it coming up in policy programming -- you sometimes may want to inherit from some class so it will adhere to a policy, but want to do so privately so that users can't tweak the policy through its methods. Maybe the policy has your class use a message buffer and you want to prevent users from being able to call the provided Flush() method. You'd probably hide B<private> behind a typedef, and combine it with a class parameter. Doohickey<private, MessageBuffer> creates a DooHickey class that is forced to use MessageBuffer's defaults.

seems like

Seems like you could do that by using aggregation instead of inheritance.

What are access modifiers?

This got me thinking about what exactly the relationship between access modifiers and a class's type is.

For first-class reasoning about this, you need a fairly powerful type theory. The cleanest way of reasoning about this is to say that a class is a record whose set of fields is existentially quantified with a known lower-bound. Or, for more advanced access, several staged lower bounds, one for each sort of context in which the class can be seen (e.g. within the class scope, in the same package as the class, in an external package).

In other words, represent class {public: int x,y; private: int z;} as something like: exists(s:set>=set{'x','y'} = set{'x','y','z'}) lambda(field:s) case(field) of {'x'->int,'y'->int,'z'->int}. Here, "outside of the definition" you only know the set of fields includes 'x' and 'y' and since you don't know whether or not 'z' belongs you cannot access it; while "inside the definition" you know the exact set of fields is x, y, and z.

With more advanced staging constructs, code in different contexts could see different lower bounds, enabling Java "protected" access within a package, etc.

The best way to reason about features like this is to translate them into a core language with a powerful type theory, rather than building up ad-hoc theories of types-with-access-protection, etc as is typically done when reasoning about object oriented languages.

With this approach, questions like "How do I abstract over access modifiers?" and "Is this thing a subtype of that thing with the same fields but different access modifiers?" are easy to answer by referring back to the core language translation, in which everything is a term which may be analyzed and abstracted over.