Newsgroups: comp.object,comp.lang.smalltalk,comp.lang.ada
Path: cantaloupe.srv.cs.cmu.edu!europa.chnt.gtegsc.com!news.mathworks.com!news.kei.com!world!bobduff
From: bobduff@world.std.com (Robert A Duff)
Subject: Re: Ada95 not OO (was Re: Has C++ had its day?)
Message-ID: <DBBDGL.Kw5@world.std.com>
Organization: The World Public Access UNIX, Brookline, MA
References: <NEWTNews.804269222.4596.sellers@sellers.sellers.com> <3t3mtb$i5@mail.one.net> <DB1zsE.77I@world.std.com> <3th7u0$98g@Starbase.NeoSoft.COM>
Date: Thu, 6 Jul 1995 21:38:45 GMT
Lines: 124
Xref: glinda.oz.cs.cmu.edu comp.object:34548 comp.lang.smalltalk:25569 comp.lang.ada:32199

In article <3th7u0$98g@Starbase.NeoSoft.COM>,
Orpheus <timd@Starbase.NeoSoft.COM> wrote:
>>[...]between the Ada style and the C++ style is a minor syntactic difference;
>>conceptually, the basic capabilities are the same.
>
>I would like to understand this difference better.  Examples?

The syntactic difference is easy to explain.  In Smalltalk, C++, Eiffel,
etc., you use prefix notation:

    x.op(y, z);
    x op: y something: z

In the above, x is the "special" object -- the one that controls the
dispatching, i.e. controls which version of op (or op:something:) is
called.  You know it's special because it comes first.  The arguments y
and z are just normal parameters -- they don't control the dispatching.

In Ada, you might write something like:

    Op(T'Class(X), Y, Z);

Here, X is the special object -- its tag controls which version of Op is
called.  You know that because the argument is of a class-wide type --
whenever a class-wide argument is passed as a parameter to a primitive
operation, run-time dispatching happens.  Y and Z are just normal
parameters.

It's just a different notation -- the way you indicate which argument is
the special one.

Inside the Op procedure, you refer to the special parameter by its name
in Ada.  In prefix-style languages, you have a special parameter name,
"this" or "self" or whatever.

There are some interesting ramifications of this notational difference:

In Ada, the special one doesn't have to come first -- you can make the
second argument control the dispatching, if that makes more sense for
that particular operation.  For example, a procedure Put that writes to
a file might take the file as the first argument, and dispatch on the
second argument: Put(Standard_Output, Some_Type'Class(Some_Object)).
Hardly an important difference -- just syntactic sugar, I'd say.

It makes a bigger difference for binary operations.  In Smalltalk, you
add X and Y by telling X to add Y to itself and return the result.  That
seems like a strange way to look at it, since addition is conceptually
symmetric -- why is X particularly special in this case?

Now suppose you have an abstract Set class, and a subclass that
implements that abstract interface in some particular way -- say, bit
vectors.  You might have:

    package Bit_Vector_Sets is
        type Bit_Vector is new Set with private;
        ...
        function Union(X, Y: Bit_Vector) return Bit_Vector;
        ...
    private
        type Bit_Vector is new Set with
            record
                ... -- An array of bits or some such thing goes here.
            end record;
    end Bit_Vector_Sets;

I'm assuming Union overrides a version inherited from Set.
"X := Union(Set'Class(...), Set'Class(...));" is dispatching on *both*
arguments.  (There is a runtime check that both have the same tag field
-- there are no multi-methods, as there are in CLOS.)

It's nice that the two arguments of Union are symmetric -- neither is
more special than the other.  In the prefix-style languages, you have to
choose one of them as the special one, and put it first (as the prefix
of the call to Union).

In Ada, the body of Bit_Vector's Union function has access to the
internal details of *both* of its arguments -- it can look at the bits
of the two vectors and do an "or" operation on them.  In Smalltalk, the
union method would only have access to the internals of "self" -- the
other argument is opaque.  This is unfortunate, since it forces the
BitVector class to export some extra operations in order to implement
union -- operations to get at the actual bits.  But these operations are
used only within the code for the class itself -- in the union method,
and other methods.  In Ada, this violation of information hiding can be
avoided.  (In Smalltalk, comments are often used to indicate that a
particular exported method isn't really intended to be used outside the
class).

Note that it is also possible to write a Union function that takes the
union of any two sets, no matter where they are in the hierarchy of set
types, including the case where one is a Bit_Vector and the other is a
Hash_Table_Set or whatever.  But that's not what I'm getting at here.

So, in Ada, the encapsulation and information hiding is controlled by
the package, separate from the inheritance/polymorphism functionality.
The two still go hand in hand, though -- the Union function is closely
attached to the type Bit_Vector, since they are in the same package.

I like the Ada way better than the prefix-notation way, but I don't
think it's that big of a deal.  It makes binary operations slightly more
elegant, but doesn't fundamentally change the nature of OOP.

Another feature of Ada's OOP is dispatching-on-result.  For example:

    X := Union(Set'Class(Some_Set), The_Empty_Set);

where The_Empty_Set is a dispatching function (a constructor, in C++
terms) that returns the empty set.  (I don't know why you want to do the
above -- it's just an example.)  Since The_Empty_Set has no arguments
that control which version to call, it uses the tag of Some_Set.  That
is, if Some_Set'Tag = Bit_Vector'Tag, then it will call the version of
The_Empty_Set belonging to Bit_Vector.  This isn't a big deal, either.
It's a nice feature, but it won't fundamentally affect the way you use
OOP, as compared to C++ or other OOP languages.

>>I agree that a package is not an object.
>
>It's more like a namespace?

Well, sort of.  But it also encapsulates the type/class together with
its dispatching operations/methods, like a C++ or Smalltalk class.

- Bob

