[pro] Modularity for subclassing in Common Lisp

Anton Vodonosov avodonosov at yandex.ru
Tue Dec 7 02:56:42 UTC 2010


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




More information about the pro mailing list