Why do OOPLs type objects as classes?

I am interested in statically typed OO languages with the property that no variable can be typed as a class. Instead all variables have one or more interfaces as types. Other than performance, what would be lost by taking this approach? Are there languages that do this?(Strongtalk?)

(To be more concrete, I am suggesting in a language such as Java or C#, disallowing any variable from having a concrete type either syntactically, or internally. Classes exist solely to specify how interfaces are to be implemented. All classes would specify at least one interface, namely one with its public methods. All inheritance mechanisms still exist.)

Comment viewing options

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

A difference that makes no difference is no difference

This doesn't sound like a subatantive change: if every class Foo implements an interface IFoo, then you can just change every variable of type Foo to be of type IFoo, except inside class Foo, of course (in Java/C# as well as C++, the unit of encapsulation is the class: you can change the private variables of other instances of your class).

if every class Foo

if every class Foo implements an interface IFoo, then you can just change every variable of type Foo to be of type IFoo

It makes a difference, because interfaces are more readily replaced than classes. However, the usefulness is predicated on the assumption that the value is a parameter to the constructor, and not constructed within the class (as you then have a class dependency). This would probably require some heavy dependency injection, and where does it bottom out? A concrete class has to be referenced somewhere.

Essentially, it sounds like union types, where each field is typed as a particular union of interfaces. It could be very flexible, but it sounds too complicated to me. I think more pervasive structural typing is both simpler, and more flexible.

Types and classes

The word "class", as it has been handed down by years and years of OOP, is in at least one way unfortunate; it differs quite a bit from its meaning in the relevant mathematics. In set theory, classes essentially are predicates on objects, which may or may not contain themselves (to avoid paradox). In most OO languages, classes are far more concrete than that, and only one thing in the broader universe of types.

Chris Date (in The Third Manifesto and other works) has a better term for the OO concept of class; "representation".

In many nominally-typed OO languages, classes give you three things:

1) They are the means of object creation--classes are those things which construct objects. Most OO languages allow multiple "constructors" to be contained within a single class; and once constructed, there is no difference between objects of the same class but created with different constructors. FP languages, which generally avoid the term "class"; do permit distinction between different branches of an algebraic sum; OTOH these often have different representations. GADTs (global abstract datatypes) are a useful extension here.

2) Classes are the repository of physical representation of objects. Objects have to ultimately live in memory somewhere and somehow; and have a well-defined (to the runtime at least; the details may be abstracted from the programmer) layout.

3) They are the types themselves. In an OO language, depending on context, this is a source of much perniciousness. A class may represent all the objects created with a particular layout (which would excluded public subclasses); OR it can mean the union of all objects belonging to the class or its subclasses. One reason that "base classes should be astract" is popular advice in OO circles, and that Java interfaces and similar are widely seen as good things, is that both of these techniques effectively make one of the two branches of this ambiguity the empty set. If a class or interface doesn't have any members of its own; then its only purpose in life is to represent the universe of its subtypes.

The moral of the story, then, is--and you are partially along the path to wisdom :)--decouple representation from the notion of type. Of course, "objects with representation X" is just as legit a type as "objects which implement interface Y" (for Java-style interfacing) or "objects which contain method M" (for structural/duck typing). There is no reason to disallow variables from having a concrete (class/representation) type; the problem with C/C++/Java is there is no way to say "an object of type X only; excluding subtypes".

There are, of course, types which are smaller than classes; "predicate types" or "subranges" are one example. There are many other interesting and useful ways to construct types that aren't generally found in the OO universe. Sum types are a highly useful one.

Class/Representation s

Thanks for the comparisons of how classes are used wrt types. I didn't fully follow your reasoning near the bottom; hoping you could clarify. Say I had set up a language as in my original posting. What would I gain by allowing variables to be typed as (class/representation)?

You wouldn't gain

You wouldn't gain much.

There are some places where the ability to specify representation is essential. These include such things as foreign function interfaces, integration of hardware, and structured message representation for serialization to a remote service (i.e. marshalling and demarshalling). Essentially, control of representation is very useful at the 'edges' of a language.

Control over representation is also useful if one is hand-optimizing code. However, in this case the benefit comes with an equivalent disadvantage: explicit access to representation as a means to hand-optimize code reduces the ability to (a) automatically optimize data representation for the code, and (b) automatically integrate features orthogonal to the problem domain such as reactive programming or software transactional memory or transparent persistence or distribution. As compilers grow ever smarter, and software requirements more complex, this hand-optimization advantage (which was of no small influence on the design of C++ and, thereby, all derived object-oriented languages such as C# and Java) diminishes greatly in value.

I am of the opinion that, at least in future language designs, care should be taken to separate specification of type from specification of representation.

Re: Chris Date

i'm very much enjoying his set of articles on LSP.

Modula3

Not sure it meets your criteria, but Modula3 differentiates between Interfaces (type definition) and Implementation (modules).

(And Sather separated the concept of type inheritance from implementation inheritance).

Eiffel

I don't really know Eiffel, but I think it also lets you decouple type specification from implementation specification. I believe it allows you to inherit implementation without subtyping, for instance, which is one of the most interesting parts of this direction, to my mind. Someone more interested in "duck typing" would probably have a different interest here.

FWIW, I'm not sure that completely preventing classes from acting as types is worth it, as it seems to penalize you in the common case. I would guess a better trade-off is to provide a mechanism for separating types from classes when desirable.

On the other hand, given the prevalence of interface-based DI frameworks like Spring, maybe programmers are more and more accustomed to writing a separate interface for every class...

OCaml

OCaml and Sather both do what you want. Of these, OCaml is much more interesting, though: object types are structural interfaces that can be fully inferred. Classes are just stylised ways to create objects (and convenient type abbreviations).

As I recall, Sather is the

As I recall, Sather is the language build around that concept.

OCaml, as mentioned above, is similar at first glance (though for different reasons altogether - what it does could be called "static duck typing").

This may be relevant

It's late, and I'm not sure that I am fully processing the question, but the following note about how objects are to be implemented in BitC may be relevant.

Basically, our objects look a lot like Java interfaces, and carry no information in their type about representation at all. The only reason we have the construct in the language is for support of existential typing. Re-use in the style of inheritance is handled through type classes, which are rather different from traditional OO classes.

You may be interested in this paper

This paper proposes a separation of subclassing and subtyping, such that one can create subtypes of a class (viewed as a type) without inheriting its implementation. This sounds like what you want to achieve.