I think the idea to use packages to specify module protocol (interface, contract) is right.
And also I think this is better than to have a separate public/protected/private language feature for classes.
What would be the motivation for a separate language feature? What is the difference for modularity in case of subclassing, from other cases were we apply modularity? I can't find the difference.
You provide two examples. One is sublcallsing:
My favorite simple example is an "output stream" protocol, that has a write-character operation and a write-string operation. The common base class provides an implementation of write-string that works by iterating over the characters of the string and calling write-character. Any output stream that can write strings in a more efficient way can override that method.
The second example is modularity: the fhash library hides the implementation details of the fhash-table object from the library client under a generic API.
I am trying to find an example where modularity is applied to subclassing to explore the specifics of the modularity in this case, but I can't think of a good example which will highlight the specifics.
We may note that "protected" is actually public, in the sense that when we specify something as "protected", we specify that the library has external clients which can rely on this protected method or field. Anyone may inherit from our public class, and access the "protected" members, therefore these members are available to public. If we change the protected member, we affect the library clients.
This drives me to a conclusion, that a single language feature is sufficient which allows to separate a protocol for interaction with a module from the module part we expect to change without affecting clients.
I made observation on many java systems, that addition to packages the public/protected/private for classes has bad influence on the system modularity.
Many java programmers tend to hide attributes of every single class behind get/set accessors, even for simple classes which just hold values passed between methods and do not contain any logic; to create hierarchies of interface/abstract class/concrete for various minor classes, employing protected methods.
But at the same time, people do not care to create structure at larger level, to divide the system into modules. It is not uncommon to see large systems where all these classes, subclasses in all the packages are public: everything is available to everything.
Separate access control for classes obscures the idea of modularity (especially in case of such an OO centric language like java, the programmers are mostly concentrated on classes/object, and often don't even think about packages;).
Class is too small entity to form a module. A module API (protocol) usually consists of set of classes, methods, factory functions, maybe some global variables.
Another thought is that the interface between base class and its subclasses is not a second interface, but a part of the first interface. As you said:
So we could have one package for the code that calls into our library, which would have the usual protocol, which we can call the "external protocol". Then we could have a *second package*, which presents a second protocol that subclases would use!
What would be the name for the second protocol? I.e. what is the interaction the base class and the subclass perform via this protocol?
As you point, it may be reusing of the implementation (derived streams reuse write-string implementation from the base class).
Looks like a thing intended for reusing can rightly be called an "external protocol" too.
Another view on the interaction in subclassing is that the base class serves as a template of some functionality, and we can customize its logic by overriding some methods. The output stream example may be viewed in this perspective: we plug-in our write-character implementation into the functionality implemented by the base class in order to customise it to work with different character source.
The means to parametrize/customize the protocol behavior can also be considered as a part of the protocol. In simple case it's method parameters, in more complex case we provide, for example, custom strategies, and can do it by defining a method for the sublcass.
Therefore it looks to me that in many cases we deal with a single interface, not two distinct ones.
Sometimes a module can interact with the outside world via several protocols, but it not necessary relates to sublcassing, and it's not necessary that both (all) the protocols are defined by this module.
Now, there's a great thing about CL packages: you can have more than one defpackage for the *same* symbols.
It's a great feature. I think you mean something like when we want to define one protocol as an extension of another protocol. Although in many cases I suppose it's possible instead of extending interface to have a separate interfaces. I.e. if one API is (func1, func2, func3), and another API is (finc1, func2, func3, extra-func), we could have the second API just as (extra-func).
A good example is needed. Maybe something like database connection API, where implementation for particular database is provided by a pluggable "driver"; or maybe swank, and swank backends for different lisps; or something simpler.
- Anton