Private & Confidential

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.

Declaring Things with Parameters

As Kim recently pointed out, we have several inconsistent ways of
declaring “things with parameters”:

  1. Methods. For example:

    Methods are not first class. Still, a typical method request,

    looks a bit like a function application.

  1. Blocks, which are first-class values. For example:

    Blocks denote objects, with a single method named apply that takes as many arguments as the block has parameters, in this case, exactly one. Note that the self inside that above block denotes not the block object, but the object in which the block itself lives.

    This block would be used as follows:

  1. Class constructors, which are also first-class values.

    Class constructors denote objects too; these objects have a single method named new that takes as many arguments as the class constructor has parameters. You can see examples of the application of the resulting class in the bodies of the methods + and -. Note that the self inside the above class constructor denotes not the class object that it constructs, but the object that will be created when the method new is invoked on the class. Note that we may want, in addition to the above, for this form to also bind Complex to a type that looks like the body of the class constructor:

  1. Fields, which are really a shorthand for method declarations. For example, in the above object constructor, we have

    which can be seen as a shorthand for

    But, you say, constant reader methods don’t have parameters. Quite true. However, variable writer methods do, and

    can be seen as a shorthand for

    which does have a parameter.

Notational Differences

There are several dimensions of difference in the above four notations:

  1. nominal versus anonymous. Method and field declarations always construct named entities; block and class constructors always create anonymous entities, which are typically returned as answers or bound to names in a separate step;
  2. parameter placement – the nominal forms put the parameters with the name; the anonymous forms put them after an invisible lambda;
  3. the meaning of self; and
  4. the presence or absence of a keyword; blocks alone have no keyword.

Open Questions

The elephant underlying this post is the question: should we abolish one or more of these forms, or seek to unify them? At present Andrew proposes that the answer to both of these questions should be no. We introduced each one for a good reason; I believe that we need them all. And I don’t think that a uniform syntax will make programs more readable; Beta did that, and I don’t think that it works.

Still, I may be wrong, and I may change my mind. Or we may want to discuss this further. This post provides a place for that discussion.