comp.lang.ada
 help / color / mirror / Atom feed
* Re: Decoupled Mutual Recursion Challenger [Was: Mut. Recurs. in Ada9X w/o Breaking Encaps.? (LONG)]
       [not found] <1994Oct7.130100.26953@swlvx2.msd.ray.com>
@ 1994-10-11 13:58 ` Norman H. Cohen
  1994-10-12 13:17   ` Decoupled Mutual Recursion Challenger John Volan
       [not found] ` <1994Oct7.165517@di.epfl.ch>
  1 sibling, 1 reply; 3+ messages in thread
From: Norman H. Cohen @ 1994-10-11 13:58 UTC (permalink / raw)


In article <1994Oct7.130100.26953@swlvx2.msd.ray.com>, jgv@swl.msd.ray.com
(John Volan) writes: 

|> So far on this thread, all the solutions to the "withing" problem
|> (decoupled mutual recursion in Ada9x) share one common feature: They
|> have all used type derivation and inheritance to support the
|> distinction between "opaque" and "transparent" object identities, with
|> the translation between them being handled by safe downcasting
|> ("narrowing") and safe upcasting ("widening"?).
...
|>
|> The trouble with solutions that exploit inheritance is that they use
|> it solely as a mechanism for decoupling, without (as Adam Beneschan
|> pointed out) correlating it to any true generalization/specialization
|> relationship present within the problem domain.  It can be argued that
|> this is an abuse of the inheritance mechanism, an example of the
|> reason why inheritance has been branded by some as the "GOTO of the
|> Nineties".  In fact, such inheritance schemes can even *get in the
|> way* of more "legitimate" uses of inheritance that do implement
|> generalization/specialization relationships.

I think this is a natural and appropriate use of inheritance.  To review,
the scheme in question is: 

    package Type_1_Parent_Package is
       type Type_1_Parent is tagged null record;
       type Type_1_Pointer is access all Type_1_Parent'Class;
    end Type_1_Parent_Package;

    package Type_2_Parent_Package is
       type Type_2_Parent is tagged null record;
       type Type_2_Pointer is access all Type_2_Parent'Class;
    end Type_2_Parent_Package;

    with Type_2_Parent_Package;
    package Type_1_Parent_Package.Refinement is
       type Type_1 is new Type_1_Parent with private;
       -- declaration of various primitive Type_1 subprograms, including
       --   some involving Type_2_Pointer
    private
       type Type_1 is new Type_1_Parent with
          record
             Associated_Type_2_Value: Type_2_Pointer;
             -- other components
          end record;
    end Type_1_Parent_Package.Refinement;

    with Type_1_Parent_Package;
    package Type_2_Parent_Package.Refinement is
       type Type_2 is new Type_2_Parent with private;
       -- declaration of various primitive Type_2 subprograms, including
       --   some involving Type_1_Pointer
    private
       type Type_2 is new Type_2_Parent with
          record
             Associated_Type_1_Value: Type_1_Pointer;
             -- other components
          end record;
    end Type_2_Parent_Package.Refinement;

The type Type_1_Parent models some real-world entity, perhaps an office
or an employee.  When a type models a real-world entity, it does not
model every property of that entity, only those that are relevant to the
intended use of the type.  For example, a type modeling an employee might
not include a Favorite_Scotch component if that information is not
relevant to the application.  The only feature of Type_1_Parent is its
ability to be pointed to by a Type_1_Pointer value.

The type Type_1 is a specialization of Type_1_Parent.  The real-world
entity it models happens to be the same as that modeled by Type_1_Parent,
but it provides a more specialized VIEW of that entity, one that offers
not only the ability to be pointed to by a Type_1_Pointer value, but a
number of primitive subprograms as well.  An instance of Type_1 "is an"
instance of Type_1_Parent, because it can be used in the same way as any
other instance of Type_1 (and in additional ways as well).

There is nothing wrong with a class and a subclass modeling the same
real-world entity, but with the subclass providing a more specialized
view of that entity, with a more complete programming interface.

--
Norman H. Cohen    ncohen@watson.ibm.com



^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: Decoupled Mutual Recursion Challenger
       [not found]     ` <1994Oct11.122356@di.epfl.ch>
@ 1994-10-11 23:34       ` John Volan
  0 siblings, 0 replies; 3+ messages in thread
From: John Volan @ 1994-10-11 23:34 UTC (permalink / raw)


Robb.Nebbe@di.epfl.ch (Robb Nebbe) writes:

>|> I should point out that, in any object-oriented application,
>|> responsibility for *many* aspects of the system's functionality winds
>|> up being distributed across *many* classes of objects.

>This is the way it should be. However, you are suggesting that a single
>aspect of the system's functionality be distributed accrossed multiple
>packages, which isn't the same thing.

I am indeed suggesting that a *single* aspect of system functionality
can be, and often should be, distributed across multiple classes (and
therefore, packages).  That's what I thought I said in the quote
above, but obviously, my phrasing wasn't very clear.  Let me rephrase:
It's *often* the case that if you take any *particular* *single*
"functionality" of an object-oriented system, you might *not* find that
functionality totally encapsulated within any *one* object class.
Instead, that functionality may actually be manifested as a multitude
of similar methods scattered over many different classes of objects.
Each of these methods only deals with that "functionality" directly in
terms of its own particular object class, and that is as it should be.
However, there may very well be *collaborations* among these methods:
Any one of these methods, in any one of these classes, may wind up
calling the corresponding methods in one or more of the other classes,
and these may wind up propagating related calls to yet other classes,
and so on.  But the "algorithm" for that overall system functionality
never appears completely in one place.  The only way to visualize the
"algorithm" is to trace the collaborative propagation of method calls
across the various classes.

That, I think, is the difference between functionally-designed systems
and systems designed in an object-oriented fashion.  In a
functionally-designed system, you may very well see a major system
functionality manifested as a single module of code.  The algorithm
can be found all in one place, but it winds up touching many different
types of data.  Other functional modules may wind up touching all of
the same data types.  So if you ask the question "What code affects
this one particular data *type*?" you will not be able to find the
answer encapsulated in any one place.  You wind up having to search
through all the functional modules.

On the other hand, in an object-oriented system, we make the data
types (i.e., the object classes) central, and the functions become
secondary.  Any one class of object may participate in many different
areas of system functionality, to its own extent.  But those same
functional areas might also be dealt with partly within many other
object classes as well.  So if you ask the question "What code affects
this one particular data *type*?" the answer is simple: the methods in
this class.  But if you ask the question "What code implements this
particular *functionality*?" you wind up having to search through
many object classes to find it all.

As I said before, this difficulty in "seeing" overall functionality
within an object-oriented system is only a symptom of *code*.
Object-oriented *analysis* and *design* techniques have the liberty of
presenting multiple complementary views to show different aspects of a
developing object-oriented system.  These views can include
functionally-oriented "cuts" at the design, where you can focus on
just those object classes, and just those methods of those classes,
that pertain to a given functionality.  That way, you can get a sense
of the overall "algorithm" implemented by these disparate methods, by
seeing their pattern of propagation.  But just because you provide
such a view during analysis or design, does *not* suggest that your
final implementation must have a functionally-oriented module that
totally encapsulates all of those methods in one place.

>Your solution seems to contradict itself. You want to put the
>abstractions in separate packages which suggests loose coupling but
>you want them to include pointers to each other which suggests tight
>coupling. You seem to be sitting on the fence trying to decide which
>side you want to be on.

Face it: 

OBJECT CLASSES WILL BE COUPLED TO EACH OTHER TO THE EXTENT THAT THEY
SHARE RESPONSIBILITIES FOR OVERALL FUNCTIONALITIES OF THE SYSTEM.

This coupling is everywhere. But how *deep* must this coupling be? Can
we limit the coupling to having one separately-encapsulated module
make calls to other related, yet still separately-encapsulated,
modules?  Or do we wind up having to encapsulate all classes within a
single module, every time we find some functionality that they share
responsibility for?

>In article <1994Oct7.222716.8690@swlvx2.msd.ray.com>, jgv@swl.msd.ray.com (John Volan) writes:
>|> C'mon, Robb, you are totally misunderstanding me.  I am not claiming
>|> that "my" implementation is the "best" one possible.  I'm just claiming
>|> that is one *possible* implementation.

>... and for which you have seen good solutions but which don't
>satisfy your constraints. I'm not claiming that there is no advantages
>to implementing the relation with two pointers; I'm claiming that to
>do that in two completely separate packages doesn't provide you with
>any advantages.

>If you want to implement a realtionship with pointers put the two
>abstractions in the same package or in the same hierarchy of packages.
>If you want to put them in completely separate packages then use an
>ADT to handle the relationship.

The issue of associations is only a particularly poignant special case
of this overall issue of "functional coupling between object classes".
You claim that I have only two choices for any association: (1) Have
the two classes relinquish all responsibility over that association
and give it to a third party, just so that the association can be
encapsulated in one place; or (2) Let the two classes take on mutual
responsibility for the association, but only if the two classes are
encapsulated together as a single module, just so that the association
can be encapsulated all in one place.  The first choice means that
we're giving up object-oriented encapsulation: It is no longer
possible to say that all the code that relates *directly* to one
particular class of object can be found encapsulated within the
methods of that class -- you have to hunt through all the association
modules, too.  The second choice also gives up on object-oriented
encapsulation: Object classes no longer act as the units of
modularity/encapsulation/separate compilation at all!

Two choices, both giving up on object-oriented encapsulation -- that's
it, take it or leave it?  I don't buy that.  I think there is a third
alternative: Distribute the responsibility -- we'll live with that --
but still keep the classes separately encapsulated.

I say again: It *can* be done. True, it's not *built in* to Ada9X.
But Ada (even Ada 83) has always had certain facilities for
*extending* the language beyond its *built in* capabilities.
That should be suggestive.

If any of you folks out there think they have a solution -- in
particular, if anyone thinks that exploiting inheritance is the answer
-- then I challenge you to take the acid test: Show that your solution
can handle even the situation of one class in a mutually-recursive
association with one of its own subclasses.  I fully intend to
demonstrate that my own solution can satisfy this test ... but I
don't want to spoil it for those of you who enjoy a challenge ... :-)

-- John Volan

--------------------------------------------------------------------------------
--  Me : Person := (Name                => "John Volan",
--                  Company             => "Raytheon Missile Systems Division",
--                  E_Mail_Address      => "jgv@swl.msd.ray.com",
--                  Affiliation         => "Enthusiastic member of Team Ada!",
--                  Humorous_Disclaimer => "These opinions are undefined " &
--                                         "by my employer and therefore " &
--                                         "any use of them would be "     &
--                                         "totally erroneous.");
--------------------------------------------------------------------------------






^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: Decoupled Mutual Recursion Challenger
  1994-10-11 13:58 ` Decoupled Mutual Recursion Challenger [Was: Mut. Recurs. in Ada9X w/o Breaking Encaps.? (LONG)] Norman H. Cohen
@ 1994-10-12 13:17   ` John Volan
  0 siblings, 0 replies; 3+ messages in thread
From: John Volan @ 1994-10-12 13:17 UTC (permalink / raw)


ncohen@watson.ibm.com (Norman H. Cohen) writes:

>In article <1994Oct7.130100.26953@swlvx2.msd.ray.com>, jgv@swl.msd.ray.com
>(John Volan) writes: 

>|> The trouble with solutions that exploit inheritance is that they use
>|> it solely as a mechanism for decoupling, without (as Adam Beneschan
>|> pointed out) correlating it to any true generalization/specialization
>|> relationship present within the problem domain.  It can be argued that
>|> this is an abuse of the inheritance mechanism, an example of the
>|> reason why inheritance has been branded by some as the "GOTO of the
>|> Nineties".  In fact, such inheritance schemes can even *get in the
>|> way* of more "legitimate" uses of inheritance that do implement
>|> generalization/specialization relationships.

>I think this is a natural and appropriate use of inheritance.  To review,
>the scheme in question is: 

[snip outline of scheme -- but excerpted again below]

>The type Type_1_Parent models some real-world entity, perhaps an office
>or an employee.  When a type models a real-world entity, it does not
>model every property of that entity, only those that are relevant to the
>intended use of the type.  For example, a type modeling an employee might
>not include a Favorite_Scotch component if that information is not
>relevant to the application.  The only feature of Type_1_Parent is its
>ability to be pointed to by a Type_1_Pointer value.

>The type Type_1 is a specialization of Type_1_Parent.  The real-world
>entity it models happens to be the same as that modeled by Type_1_Parent,
>but it provides a more specialized VIEW of that entity, one that offers
>not only the ability to be pointed to by a Type_1_Pointer value, but a
>number of primitive subprograms as well.  An instance of Type_1 "is an"
>instance of Type_1_Parent, because it can be used in the same way as any
>other instance of Type_1 (and in additional ways as well).

>There is nothing wrong with a class and a subclass modeling the same
>real-world entity, but with the subclass providing a more specialized
>view of that entity, with a more complete programming interface.

Everything you say is reasonable from a certain perspective, and I
have no doubt that this scheme can be useful in some applications, as
long as the designers share the same perspective.  But you still have
not addressed the challenge: Show that this use of inheritance
(providing two views of the same class of real-world entities) does
not interfere with the more "customary" use of inheritance (support
for the relationship of generalization/specialization between two
different classes of real-world entities).

To review the challenge:


>    package Type_1_Parent_Package is

     -- e.g. Employee

>       type Type_1_Parent is tagged null record;
>       type Type_1_Pointer is access all Type_1_Parent'Class;
>    end Type_1_Parent_Package;
>
>    package Type_2_Parent_Package is

     -- e.g. Office

>       type Type_2_Parent is tagged null record;
>       type Type_2_Pointer is access all Type_2_Parent'Class;
>    end Type_2_Parent_Package;

     package Type_3_Parent_Package is
     -- e.g. Project
        type Type_3_Parent is tagged null record;
        type Type_3_Pointer is access all Type_3_Parent'Class;
     end Type_3_Parent_Package;

     package Specialization_Of_1_Parent_Package is
     -- e.g. Manager
        type Specialization_Of_1_Parent is tagged null record;
        type Specialization_Of_1_Pointer is 
          access all Specialization_Of_1_Parent'Class;
     end Specialization_Of_1_Parent_Package;

>    with Type_2_Parent_Package;

     -- add this:
     with Specialization_Of_1_Parent_Package;

>    package Type_1_Parent_Package.Refinement is
>       type Type_1 is new Type_1_Parent with private;
>       -- declaration of various primitive Type_1 subprograms, including
>       --   some involving Type_2_Pointer

        --   as well as some involving Specialization_Of_1_Pointer

>    private
>       type Type_1 is new Type_1_Parent with
>          record
>             Associated_Type_2_Value: Type_2_Pointer;

              Associated_Specialization_Of_1 : Specialization_Of_1_Pointer;

>             -- other components
>          end record;
>    end Type_1_Parent_Package.Refinement;
>
>    with Type_1_Parent_Package;
>    package Type_2_Parent_Package.Refinement is
>       type Type_2 is new Type_2_Parent with private;
>       -- declaration of various primitive Type_2 subprograms, including
>       --   some involving Type_1_Pointer
>    private
>       type Type_2 is new Type_2_Parent with
>          record
>             Associated_Type_1_Value: Type_1_Pointer;
>             -- other components
>          end record;
>    end Type_2_Parent_Package.Refinement;

     with Type_1_Parent_Package.Refinement;
     with Type_3_Parent_Package; -- If this "with" were not already here, then
                                 -- adding it should not affect Type_2.
     package Specialization_Of_1_Parent_Package.Refinement is

       -- DILEMMA: Should we say:

       type Specialization_Of_1 is new Specialization_Of_1_Parent with private;
       -- using inheritance to provide a more complete view

       -- or should we say:

       type Specialization_Of_1 is new Type_1 with private;
       -- using inheritance for a real-world generalization/specialization

        -- declaration of various primitive Specialization_Of_1 subprograms, 
        --   including some involving Type_1_Pointer
        --   and some involving Type_3_Pointer
     private
       type Specialization_Of_1 is new ???? with
         record
           Associated_Type_1_Value : Type_1_Pointer; -- assuming cardinality=1
           Associated_Type_3_Value : Type_3_Pointer; -- assuming cardinality=1
           -- other components
         end record;
     end Specialization_Of_1_Parent_Package.Refinement;

     -- (Of course, in all of the above, assume that there is a liberal
     -- sprinkling of "use" clauses :-)

>--
>Norman H. Cohen    ncohen@watson.ibm.com

Let me state it categorically: I think that using the mechanism of
inheritance to solve the problem of decoupled mutual recursion is
misguided.  Luckily, I think there is a better solution, but it
involves another feature of Ada entirely ...

-- John Volan

--------------------------------------------------------------------------------
--  Me : Person := (Name                => "John Volan",
--                  Company             => "Raytheon Missile Systems Division",
--                  E_Mail_Address      => "jgv@swl.msd.ray.com",
--                  Affiliation         => "Enthusiastic member of Team Ada!",
--                  Humorous_Disclaimer => "These opinions are undefined " &
--                                         "by my employer and therefore " &
--                                         "any use of them would be "     &
--                                         "totally erroneous.");
--------------------------------------------------------------------------------



^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~1994-10-12 13:17 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
     [not found] <1994Oct7.130100.26953@swlvx2.msd.ray.com>
1994-10-11 13:58 ` Decoupled Mutual Recursion Challenger [Was: Mut. Recurs. in Ada9X w/o Breaking Encaps.? (LONG)] Norman H. Cohen
1994-10-12 13:17   ` Decoupled Mutual Recursion Challenger John Volan
     [not found] ` <1994Oct7.165517@di.epfl.ch>
     [not found]   ` <1994Oct7.222716.8690@swlvx2.msd.ray.com>
     [not found]     ` <1994Oct11.122356@di.epfl.ch>
1994-10-11 23:34       ` John Volan

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox