We’ve been discussing encapsulation controls in Grace. For teaching, a rather conventional “visibility system” that lets features of an object be accessible only to
(a) the object itself, or
(b) the object and the heirs of that object, or
(c) the object, heirs and clients of the object
is probably necessary and sufficient – although we’re not 100% sure we need to support each of these visibility levels. Necessary, because at least some instructors will want to teach students how to use such a system, and sufficient for our intended audience. (I still believe that an industrial-strength language in which reuse is a goal should put control of encapsulation in the hands of the client, as we did in the Schärli, Black & Ducasse OOPSLA 2004 paper. However, even with controllable encapsulation policies, there still needs to be a way to define default policies for heirs and clients. )
If once accepts that as a starting point, there are still a number of decision to be made. We need to pick names for each kind of visibility — so we can have this conversation — and then we need to decide how to apply the different levels to the features of an object — its methods, variables and constants. Another decision is whether the visibility declarations should be advisory, or whether there should be work-arounds, as there are in almost every industrial-strength language. (Even if you really want a feature to be private, you probably still want to be able to look at it in the debugger.)
The problem with names is that all of the good ones are taken. The best name that I can think of for (a) is private, but Java uses private to mean “accessible not only by me, but also by any object that happens to share the same class as me”, and Ruby uses private to say that the method can be used only with self as the receiver, which means that it can be used by heirs as well by an object itself. Still, private is the best name that we could think of for (a). For access level (c), public seems entirely appropriate, but level (b) is tricky. “Family access only” is the most descriptive phrase that I could come up with. “Confidential” is a possibility — less private than private, but not public. Other possibilities are “hidden”, but it’s not clear that hidden is more accessible than private, and “protected”, which has the same disadvantage, as well as being taken by Java to mean “more accessible than the default”. Can you suggest good terms?
Speaking of defaults, should we have default visibilities, or must they always be declared? The principles of clarity and readability tell us that we should tag each feature with its visibility explicitly. However, the principle of brevity tells us that the common case should require less characters on the screen. The common case, I believe — at least in the beginning — is that methods should be public and that constants and variables should be private, or perhaps confidential. But that gives rise to a complicated rule: the default depends on how the feature is implemented. A simpler rule — simpler to teach and simpler to remember — would be for every feature to have the same default visibility. So those of us who are enthusiasts for encapsulation would make all features default to private, and those of us who are enthusiasts for reuse would make everything default to confidential. However, in both cases, methods intended for clients would have to be declared to be public explicitly. Is this an undue burden, considering that, at least in the beginning, the only reason to declare a method is to provide public access?
Another issue is the syntax for these accessibility declarations. Should we use keywords public, private and confidential? Should we sigils, such as UML’s +, – and #? Should we use annotations (attributes), as we are considering for “overrides”? The argument for sigils is brevity, and lack of syntactic clutter, especially if every method, constant and variable needs to have its visibility made explicit; the argument against is that we want students to learn the right vocabulary, and refer to private methods as “private methods” and not as “- methods”. This is the rationale that has led us towards using more keywords in Grace than in many other languages (for example, labeling methods with method), and generally spelling keywords out in full (integer rather than int).
A final issue — and one that is often neglected — is to define the semantics of the visibility annotations. Private methods are not really methods at all: they are early bound and have lexical scope, and thus they should not be part of an object’s type. Public and Confidential methods are both real, late bound methods; the distinction is that confidential methods can be requested only from self. We think it’s reasonable to make that a syntactic restriction, so that
self.protectedMethod
is OK, but
o := self
o.protectedMethod
is an error.
Should Confidential methods appear in an object’s type? That rather depends on the rôle of types: do they exist to describe what clients can do, or what heirs can do? It’s actually quite reasonable to have two different type systems with those two different goals, but the type system for clients will be the more widely used, and probably the one that teachers will want to emphasize. We think that all of the checks required for the correct use of inheritance can be done statically in a whole-program analysis without burdening the programmer with a second type system.
I think the benefit of encapsulation is apparent only in bigger programs, so in a beginner language I would make “public” to be default.
I think ‘protected’ is general enough to mean what you want it to mean. I don’t see problem in Java assigning a slightly different meaning to it — you can explain the difference.
Have you considered the class forming a closure over “private” things, smth like:
class Foo(x:integer) {
method bar() {…; blah(); …; use(yyy) ;….}
} where {
yyy: String
function blah() { …; use(yyy); … }
}
Hi Aivar – yes interesting comments. The tension between “make it easy” (public as default) vs “do the right thing” or “teach good habits” (private default) is real, and it’s not clear how to resolve it.
Yes I think we can probably get away with different semantics for “private” and “protected” — after all Ruby does!
The most significant difference in usage of the names for different kinds of visibility is not so much about Java package access, as between object encapsulation and class encapsulation. So, Aivar, you are right: we will in any case have to explain that different languages uses these words to mean just what they want them to mean 🙂
One approach to resolving the conflict that I spoke of is to look at beginning examples of good objects (that is, objects that have some real behavior, and don’t just simulate records). Maybe I’ll do that …