TEAM-ADA Archives

Team Ada: Ada Programming Language Advocacy

TEAM-ADA@LISTSERV.ACM.ORG

Options: Use Forum View

Use Monospaced Font
Show Text Part by Default
Show All Mail Headers

Message: [<< First] [< Prev] [Next >] [Last >>]
Topic: [<< First] [< Prev] [Next >] [Last >>]
Author: [<< First] [< Prev] [Next >] [Last >>]

Print Reply
Subject:
From:
Matthew Heaney <[log in to unmask]>
Reply To:
Matthew Heaney <[log in to unmask]>
Date:
Tue, 24 Nov 1998 21:06:01 -0800
Content-Type:
text/plain
Parts/Attachments:
text/plain (334 lines)
Sender: [log in to unmask]
To: Samuel Mize <[log in to unmask]>
Cc: [log in to unmask]
Subject: Re: "Classes" as packages in Ada
References: <[log in to unmask]>
From: Matthew Heaney <[log in to unmask]>
Date: 24 Nov 1998 21:06:01 -0800
In-Reply-To: Samuel Mize's message of "Tue, 24 Nov 1998 13:18:13 -0600"
Message-ID: <[log in to unmask]>
Lines: 325
X-Mailer: Gnus v5.6.45/Emacs 20.2

Samuel Mize <[log in to unmask]> writes:

> Ada's tagged types provide (in my opinion) excellent facilities for
> object-oriented programming.  However, some people find it disturbing
> that Ada doesn't provide a "class" wrapper to encapsulate an object's
> data type and operations.

Then these people have to start thinking in Ada.

Ada separates the notion of module and type.

Of course Ada has the notion of a class.  It's called a tagged type.

All a package (module) does is demarcate, from among all the subprograms
in the application that take T as a parameter, those subprograms that
are "primitive" for the type.

It's that simple.

If you haven't groked the concept of separation of module and type, then
of course you will find Ada facilities "disturbing."

Complaining about Ada's design is like an English-speaking person
complaining that in French, you have to say "chien" when you mean "dog."
Imaging that!

> Each class can be "wrapped" with Ada's "package" -- its encapsulation
> construct.  However, this is not required, and nothing indicates the
> difference between a class and any other package.

That is an artifact of the separation of module and type.

It is not a flaw, it's a feature.

> I find this to be a partial and unsatisfactory answer.

Then you have to start thinking in Ada.  For some reason, I don't seem
to have this problem.

> It is not in keeping with Ada's general philosophy of expressing your
> design intent in the code.  Doing so helps others, later, to
> understand it.  It also lets the compiler help you find errors
> earlier -- for instance, by telling you that a "positive" number has
> just been assigned a negative value.

I don't know what you're talking about.  A class --ie tagged type--
has the keyword "tagged" in the declaration of the type.  A type with
the keyword tagged in its declaration means that this is a class.   A
package containing the declaration of a tagged type is a package that
declares a tagged type.  What's the problem?


> Note that Ada already provides a tremendous amount of such support
> for object-oriented development.  For example, it isn't possible to
> send a message to an object that has no method for that message.  I
> believe that Ada provides as much automated checking of an
> object-oriented design as C++, probably more (a different design
> philosophy is at work there).

Why call it "object-oriented development"?  Why not just call it "strong
type checking"?  I can't do this:

   I : Integer;
begin
   Howdy (I);

because object I has no "Howdy" message.

What's the problem?  There is nothing new or interesting about saying
"it isn't possible to send a message to an object that has no method for
that message."  That's been true since 1980!

> However, I want to express some checkable object-oriented semantics
> that go beyond Ada's current expressive ability.

I'm sorry to hear that, because you are complexifying the language.  It
is expressive enough thank you very much.

> And -- again, because of the Ada 95 "toolbox" philosophy -- we can do
> so in an orderly way.  A tool vendor can define a specific set of
> semantic limitations, which we can then apply to selected parts of a
> program.  This lets us define how object-oriented programming should
> be done, and lets the compiler warn us when we fail to follow the
> paradigm correctly.

If you want to do object-oriented a certain way, then do it your certain
way.  There's no need for any help from the compiler.

This is not unlike Republican congressmen here in the US, who want to
amend the US Constitution to include a requirement for a balanced
budget.  Why do this?  Why not just balance the damn budget, without
adding more baggage to the constitution?

If you want to program in a certain style (a style I don't agree with,
by the way), then why not just program in that style, without adding
more baggage to the language?


> 1. Would checking for these semantic items be helpful to catch design
>    or coding errors in an object-oriented design?

No.


> 2. What other things might we look for?

Keep it simple.

If an abstraction is too complex, then it means the programmer made it
too complex.  Fix the programmer, not the language.


> 3. Is there a better technical approach?

Yes.  Don't do what you are suggesting.


> The specific list of pragmas follows.
>
> - - - - -
> 1. pragma Class;
>
> Legal directly inside the declarative region of a package (Ada's
> module-building construct), plain or generic.
>
> The package must export exactly one tagged type.  It may only export
> subprograms in one of these categories:
>
> - has one or more parameters of that tagged type
> - has one or more parameters of the tagged type's class-wide type
> - follows a Class_Subprograms pragma (see below)

If you want only one type per package (naive, and often impossible),
then just declare one tagged type per package.

You seem to want a package to be a type.  But an Ada package is not a
type!

Ada separates the notion of module and type.  An Ada package is a
module.  Therefore, a package is not a type.

There are MANY times when putting two tagged types together in one
package IS VERY NATURAL.  In fact, it's often MORE NATURAL than the
artificial separation you advocate.

An Ada package is not a type.

> This assures the package's user that this is a complete definition of the
> class, and nothing else.

I already know that!

Here's a little type declaration:

package P is

  type T is tagged private;

  procedure Op (O : in out T);

private

  type T is
    tagged record
      I : Integer;
    end record;

end P;

Now, let's see if we can perform an extensive analysis of this example:

1) P is a package.  A package is not a type.  Therefore, P is not a
   type.

2) T is a type declared in P.  T is a type.  T is not a module.  T is a
   type, therefore T is a type.

3) The declaration of type T includes the keyword "tagged."  Therefore,
   type T is a tagged type.

4) All the primitive operations for type T are declared in the module
   in which in type is declared.  Type T is declared in module P.
   Therefore, the "complete definition" of type T is in module P.

Somehow, we've been able to determine that T is a tagged type, declared
in package P, and that all the primitive operations for T are in P.  And
all without the help of yet another pragma.  Imagine that!

> Ideally, the compiler would refuse to allow this type to be extended
> except in another "Class" package, but I'm not sure if that kind of
> restriction is possiible.

But of course a tagged type can only be extended in a(nother) package.
This is already the case.  No more pragmas are required, thank you very
much.

> 2. pragma Class_Subprograms;
>
> Legal directly inside the declarative region of a "Class" package (plain
> or generic), after the tagged type has been defined.
>
> Subprograms following a "pragma Class_Subprograms" may have no tagged type
> or class-wide parameters.  This allows for selectors and operations on the
> class itself, such as a function to query how many objects of that class
> exist, without requiring them to have dummy parameters of the tagged type
> or its class-wide type.

I already know which operations are "class subprograms", because they
don't take T as a parameter.  And I'm able to determine this without the
help of a pragma.

> Note that none of the class's methods can follow pragma Class_Subprograms.
>
>
> 3. pragma Inherits;
>    pragma Inherits (subprogram_name...)
>
> Legal directly inside the declarative region of a "Class" package (plain
> or generic, although it is unlikely to be used in a generic).

Your first good idea!

However, this issue has been discussed here already, and was debated
during the Ada 9x design.

> 5. pragma Object_First;
>
> Legal directly inside the declarative region of a "Class" package (plain
> or generic).
>
> In all parameter lists for the tagged type's subprograms, the controlling
> tagged-type parameters will come first.  If there are none, the class-wide
> parameters will come first.  If the subprogram is a function, "first" may
> be either the return value, or the first parameter.
>
> This enforces a regimen that, like an "object.method" syntax, helps the
> reader to immediately identify the object that is receiving the message.

If it's not obvious which object is "receiving the message," then
something in clearly wrong with your abstraction!

When programming using abstract data types (tagged, or otherwise), the
subprogram parameter list should only have one or two or maybe three
parameters.  If it's longer than that, then you're not object-oriented
programming (which is not in itself a bad thing).

If I say,

  Push (Item, On => Stack);

then it should be obvious that object Stack is the recipient of the
message Push.  The declaration of Push:

  procedure Push
    (Item : in     Item_Type;
     On   : in out Stack_Type);

even declares the recipient as the second parameter.  Imagine that!

Did you know that when a French person refers to a "cat," she says
"chat"?  With such a confusing nomenclature, it's hard to fathom how
French people communicate with anyone at all!

> (Note that we can't really enforce the concept of "recipient" in Ada, but
> this will at least help the user easily locate the right package.)

We can help the user easily locate the right package, without the help
of unnecessary pragmas.  Just program abstract data types using
object-oriented programming, and there will be no problem.

> 6. pragma Mixin;
>
> Legal directly inside the declarative region of a generic "Class" package.
>
> This generic must have exactly one tagged-type input parameter, and extend
> it as its exported "class" tagged type.
>
> This simply clarifies the intent of the programmer.

A mixin package looks like this:

generic
  type Parent (<>) is abstract tagged limited private;
package <Something>_Mixin is

   type T is new Parent with private;

   <ops here>

private

...

end <Something>_Mixin;


You know this is a mixin package, because it's named Mixin.  And because
the type publically derives from a tagged type imported as a generic
formal type.

No pragma is required thank you very much.

> That ends the pragmas I have thought of to date.

I think you need to understand the concept that Ada is not Smalltalk.
Ada is not Eiffel.  Ada is not C++.  Ada is Ada.  Didn't you read Atlas
Strugged?.

In Ada, package and type are orthogonal language features.  Because of
this design decision, certain idioms emerge that don't apply to
languages in which module and type are the same.

You will only have difficulty (read: add complexity) when you try to
compare the model "module equals type" to the model "module doesn't not
equal type."

You are comparing apples and oranges.

Your suggestions for "improving" the language won't.

Matt

ATOM RSS1 RSS2