[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: OO should break when broken (was re: the benefits of immutability)



Just a couple of comments.

Quoting "Michael St . Hippolyte" <mash@brooklyndigital.net>:
> On 2003.09.01 08:59 vkarvone@mappi.helsinki.fi wrote:
> > 1. Derive the subtype relations between the types Real, Number,
> Complex,
> >    and Integer?
> 
> Even this classic example can defy expectations.

Yes, I think that the relationship between Real and Complex (and Integer
and Real) is analogous to the relationship between Circle and Ellipse. In
particular, the superclass essentially requires a bigger representation
than the subclass.

> In Bento I decided to support parametric polymorphism through the type
> system, with syntax which allows an object to pick its implementation
> superclass at runtime, depending on its parameters. I was amazed to find
> out that this made it as easy to use subclassing to generalize as to
> specialize.

The above use of the term parametric polymorphism and the description that
talks about a syntax for run-time parameterization of an object with a
superclass sound somewhat odd to me and I'm not sure that I understand
correctly what you mean. Could you perhaps clarify the wording? For
example, as I understand it, an object is an instance of a class, therefore
it is not entirely obvious what it means that an object is parameterized by
a superclass.

Does bento have a statically checked, syntactic, type system?

> Polymorphic supertyping lets you define a subclass which is effectively
> a union of several types. [...]

The term "polymorphic supertyping" sounds very strange. Are you familiar
with the use of Parameterized Inheritance in C++? The basic idea is to
have a template that looks like this:

  template<class Base>
  struct derived : Base {/*...*/};

Among other things, parameterized inheritance can sometimes be a
compelling alternative to multiple inheritance. For further reading, see
the book Cenerative Programming by Czarnecki and Eisenecker.

> With such syntax it's easy to model a general object such as Number as
> the polymorphic subclass of a range of specific numeric types (e.g.
> Integer, Real and Complex), inheriting the value of whichever superclass
> is selected based on an examination of parameter types at runtime.

A few years ago (probably in 1999), upon reading James O. Coplien's book
Multi-Paradigm Design for C++, I designed a set of class templates that
modelled the Real, Complex, Integer, and Number types using a form of
parameterized inheritance. [Unfortunately, I don't have the code anymore.]
The use of parameterized inheritance worked essentially like a compile
time bridge. IIRC, the class templates basically looked like this:

  template<class Impl> struct Integer : Real   <Impl> {...};
  template<class Impl> struct Real    : Complex<Impl> {...};
  template<class Impl> struct Complex : Number <Impl> {...};
  template<class Impl> struct Number  :         Impl  {...};

The basic idea was to separate the interface and the implementation without
imposing a significant abstraction penalty. I then had different classes
for the implementations (IntegerImpl, RealImpl, etc...). In the below
diagram, which is explicitly arranged to look similar to the diagram of the
Bridge pattern in GoF, all arrows denote inheritance.

    interface             implementation
    hierarchy              hierarchy (or forest)

                             ???
                              ^
                   bridge     |
    Number<Impl> ----------> Impl
      ^                       ^
      |                      /|\
   Complex<Impl>            ? ? ?
      ^
      |
    Real<Impl>
      ^
      |
   Integer<Impl>

You could then write functions, e.g. the gcd(), that could only be
called with subtypes of Integer<>s:

  template<class Impl>
  Integer<Impl> gcd(Integer<Impl> x, Integer<Impl> y) {/*...*/}

Althought there were some details that needed to be done carefully,
I recall some positive experiences. For starters, the classes
modelled the subtype relationships fairly correctly. In addition,
I recall that the compiler (some version of MSVC++) was able to
eliminate most of the abstraction penalty. In the end, I designed
the code for my own amusement and didn't really have a need for such
classes, so I didn't keep them.

[I also designed an Iterator hierarchy using the same form of
parameterized inheritance, which I called constrained pure genericity
at the time. The technique basically emulates bounded quantification
and I recall that one of the main motivations was to avoid loss of
type information in function templates.]

...

I may have other comments later.

Regards,
  Vesa Karvonen