comp.lang.ada
 help / color / mirror / Atom feed
* "Classes" as packages in Ada
@ 1998-11-24  0:00 Samuel Mize
  1998-11-24  0:00 ` John Goodsen
  0 siblings, 1 reply; 10+ messages in thread
From: Samuel Mize @ 1998-11-24  0:00 UTC (permalink / raw)


This message concerns object-oriented design, how that interacts with
the Ada language's design philosophy, and some thoughts on additions
to Ada that would better support object-oriented programming.

I'm cross-posting to comp.object, comp.lang.ada and the Team Ada
mailing list, hoping to get some input on this specific suggestion.
I've seen good comments between those groups in the past.

If you just flat think Ada 95 is bad for object-oriented work, or if
you think object-oriented development is nonsense, I hope you'll find
another thread in which to express that.  Thanks.

The particular mechanism I'm proposing is a set of "pragma"
(non-executing) statements.  These would tell the compiler about the
developer's intent for code structures surrounding a tagged type
(Ada's object-oriented programming construct).  This is explained in
detail below.

I've tried to state things so that a non-Ada programmer can
understand and contribute.


- - - - -
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.

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.

I find this to be a partial and unsatisfactory answer.

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.

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).

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

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.

The mechanism is Ada's "pragma" facility.  This lets the programmer
tell the compiler something about the program that is outside the
code.  The general theory is that a pragma shouldn't alter the
semantics of a legal program, but it may cause a program to become
illegal.  For example, there exists

    pragma Restrictions (...);

where one restriction may be that the program has no internal
multi-tasking.  The compiler can make simplifying assumptions to
generate more efficient code.  The compiler should reject a program
that both states and violates a restriction, although the program
would be legal without the pragma.

The "Restrictions" pragma is language-defined, but a compiler vendor
is allowed to define any other pragmas he wants.  (A different
compiler may ignore another vendor's pragmas, although it should warn
the user if a program contains a pragma that it does not recognize.)

I have thought of some pragmas that would let compiler help us
enforce our own restrictions when we are doing object-oriented
development, with an eye toward catching errors in coding or design.
I list them below.  My questions to you-all are:

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

2. What other things might we look for?

3. Is there a better technical approach?


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)

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

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.

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.

Note that none of the class's methods can follow pragma Class_Subprograms.


3. pragma Inherits;
   pragma Inherits (subprogram_name...)

   pragma Inherits_Function (function_name, return_type, parameter_type...)
   pragma Inherits_Procedure (procedure_name, parameter_type...)

   pragma In_Fun (function_name, return_type, parameter_type...)
   pragma In_Pro (procedure_name, parameter_type...)

Legal directly inside the declarative region of a "Class" package (plain
or generic, although it is unlikely to be used in a generic).

With no parameters, "pragma Inherits" states that, for each potentially
dispatching (polymorphic) subprograms that this class's tagged type
inherits, this class either overrides that subprogram or names it in an
"Inherits" or "Hides" pragma.

With parameters, "pragma Inherits" must list subprograms that are
inherited.  Like "pragma Inline," all subprograms of that name are
inherited, and multiple "pragma Inherits" may be used.

The pragmas Inherits_Function and Inherits_Procedure let you specify the
parameter profile of the subprogram being inherited, in case subprograms
with the same name are distinguished by their profiles.

Pragmas In_Fun and In_Pro are abbreviations for Inherits_Function and
Inherits_Procedure, respectively.  (They are not intended to suggest that
functions are more enjoyable, or that procedures are more remunerative :-) 

A subprogram listed in an Inherits pragma may not also be over-ridden.

All of these pragmas imply the semantics of a plain pragma Inherits.

This lets you enumerate, in a given class package, all the methods for
that class, even the ones it inherits.  It also helps catch spelling or
typing errors, where you intend to over-ride a parent class's method but
instead create a new message by mistake (e.g., trying to over-ride
"Initialize" with a procedure named "Initialise").


4. pragma Hides (subprogram_name...)

   pragma Hides_Function (function_name, return_type, parameter_type...)
   pragma Hides_Procedure (procedure_name, parameter_type...)

Legal directly inside the declarative region of a "Class" package (plain
or generic, although it is unlikely to be used in a generic).

"pragma Hides" must list subprograms that are inherited.  Like "pragma
Inline," all subprograms of that name are hidden, and multiple "pragma
Hides" may be used.

A subprogram listed in an Inherits pragma may not also be over-ridden.

All of these pragmas imply the semantics of a plain pragma Inherits.

If a subprogram listed in a Hides pragma is called, a new exception will
be raised.  If the compiler detects at run-time that this exception will
be raised, the compilation should fail.  (This obviously needs some work
to get it semantically precise.)

This breaks Ada's guarantee that no object can receive a message that it
doesn't have a method for, but it does so in an orderly way and in a way
that is visible in the package specification.  It is preferable, in a few
cases, to do so, rather than to inherit an irrational method.

"pragma Hides" implicitly over-rides the subprogram's body with one that
just raises the exception, and that is what any child of this class
inherits.  It may un-hide the subprogram by over-riding it.  However, it
cannot re-inherit the subprogram of the class's parent.


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.

(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.)

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


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.


7. pragma Categorized;
   pragma Category (Identifier, Subprogram_Identifier...);

Legal directly inside the declarative region of a "Class" package (plain
or generic).  "pragma Category()" must follow the declarations of the
subprograms in its parameter list.

"pragma Categorized" causes compilation to fail unless all methods of
the class (subprograms that precede any "pragma Class_Subprograms")
are listed in a "pragma Category".

"pragma Category" indicates that the listed subprograms fall into the
named category.

This is intended to communicate with analysis tools.  For instance, a
tool may draw a line for each message that objects of one class send
to objects of another; all the subprograms in a category could be
lumped into one line, so that instead of showing 20 control functions
the diagram would show only one line labelled "control".

The identifier is arbitrary, and more than one "pragma Category" in a
given compilation may use the same identifier.

"pragma Category" may be used without "pragma Categorized" to indicate
categories for a subset of the class's methods.

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

Best,
Sam Mize

-- 
Samuel Mize -- smize@imagin.net (home email) -- Team Ada
Fight Spam: see http://www.cauce.org/ \\\ Smert Spamonam




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

* Re: "Classes" as packages in Ada
  1998-11-24  0:00 "Classes" as packages in Ada Samuel Mize
@ 1998-11-24  0:00 ` John Goodsen
  1998-11-25  0:00   ` Ed Falis
  0 siblings, 1 reply; 10+ messages in thread
From: John Goodsen @ 1998-11-24  0:00 UTC (permalink / raw)


I argued long and hard for the first-class concept of "class" in Ada95.
My argument was more economical than technical, though.  I don't
dispute the power of the current tagged-type approach to polymorphism
that Ada95 is built upon.  But I think your proposal for a whole new
slew of pragmas to address what some of us felt very strongly would
be an issue for general acceptance of the language only solidifies what
some of us were saying back in '94.

[flame bait begins here - be easy on me now :-) ]

The Ada community is not notorious for drawing a huge following of
mainstream programmers into using the language.  IMHO, too many
of the decision makers still live in a DoD environment and the reality
of competing for marketshare to make it a viable language in an
open market is just not a real concern for most of them.  I don't mean
to be derogatory in that statement - I can back it up with entire conversations
on this very topic from 4-6 years ago.

I still believe that Ada95 is a superior language to C++ or Java (but not Eiffel :-).
Unfortunately, technical superiority doesn't win in the open marketplace.
... and eventually I gave up beating my head against the DoD centric decision
makers and joined the C++ and now Java crowd in order to expand a business.
(and I've been with companies that focused specifically on Ada with the promise
 of a mandated DoD market, only to see the mandate get side-stepped -
 some of these companies no longer exist - and it's due to the dinky Ada market
 that never blossomed to it's potential).  IMHO, the lack of a direct notiion of "class"
in Ada95 only serves to further alienate potential projects from using the language.

BTW, this brings up a curious point.  How do you implement an <<interface>>
in Ada?  Sorry if it's a simple answer - but's its been a few years now
since I've touched an Ada compiler.

--
John Goodsen       RADSoft / Training, Mentoring and Consulting in:
jgoodsen@radsoft.com       - UML modeling and OOA/D principles
http://www.radsoft.com     - Use Centered Object-Oriented Design
888.298.2566                - Rapid Incremental S/W Delivery Process

Samuel Mize wrote in message <73f0p1$4iu8$1@prime.imagin.net>...
>This message concerns object-oriented design, how that interacts with
>the Ada language's design philosophy, and some thoughts on additions
>to Ada that would better support object-oriented programming.
>
>I'm cross-posting to comp.object, comp.lang.ada and the Team Ada
>mailing list, hoping to get some input on this specific suggestion.
>I've seen good comments between those groups in the past.
>
>If you just flat think Ada 95 is bad for object-oriented work, or if
>you think object-oriented development is nonsense, I hope you'll find
>another thread in which to express that.  Thanks.
>
>The particular mechanism I'm proposing is a set of "pragma"
>(non-executing) statements.  These would tell the compiler about the
>developer's intent for code structures surrounding a tagged type
>(Ada's object-oriented programming construct).  This is explained in
>detail below.
>
>I've tried to state things so that a non-Ada programmer can
>understand and contribute.
>
>
>- - - - -
>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.
>
>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.
>
>I find this to be a partial and unsatisfactory answer.
>
>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.
>
>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).
>
>However, I want to express some checkable object-oriented semantics
>that go beyond Ada's current expressive ability.
>
>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.
>
>The mechanism is Ada's "pragma" facility.  This lets the programmer
>tell the compiler something about the program that is outside the
>code.  The general theory is that a pragma shouldn't alter the
>semantics of a legal program, but it may cause a program to become
>illegal.  For example, there exists
>
>    pragma Restrictions (...);
>
>where one restriction may be that the program has no internal
>multi-tasking.  The compiler can make simplifying assumptions to
>generate more efficient code.  The compiler should reject a program
>that both states and violates a restriction, although the program
>would be legal without the pragma.
>
>The "Restrictions" pragma is language-defined, but a compiler vendor
>is allowed to define any other pragmas he wants.  (A different
>compiler may ignore another vendor's pragmas, although it should warn
>the user if a program contains a pragma that it does not recognize.)
>
>I have thought of some pragmas that would let compiler help us
>enforce our own restrictions when we are doing object-oriented
>development, with an eye toward catching errors in coding or design.
>I list them below.  My questions to you-all are:
>
>1. Would checking for these semantic items be helpful to catch design
>   or coding errors in an object-oriented design?
>
>2. What other things might we look for?
>
>3. Is there a better technical approach?
>
>
>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)
>
>This assures the package's user that this is a complete definition of the
>class, and nothing else.
>
>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.
>
>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.
>
>Note that none of the class's methods can follow pragma Class_Subprograms.
>
>
>3. pragma Inherits;
>   pragma Inherits (subprogram_name...)
>
>   pragma Inherits_Function (function_name, return_type, parameter_type...)
>   pragma Inherits_Procedure (procedure_name, parameter_type...)
>
>   pragma In_Fun (function_name, return_type, parameter_type...)
>   pragma In_Pro (procedure_name, parameter_type...)
>
>Legal directly inside the declarative region of a "Class" package (plain
>or generic, although it is unlikely to be used in a generic).
>
>With no parameters, "pragma Inherits" states that, for each potentially
>dispatching (polymorphic) subprograms that this class's tagged type
>inherits, this class either overrides that subprogram or names it in an
>"Inherits" or "Hides" pragma.
>
>With parameters, "pragma Inherits" must list subprograms that are
>inherited.  Like "pragma Inline," all subprograms of that name are
>inherited, and multiple "pragma Inherits" may be used.
>
>The pragmas Inherits_Function and Inherits_Procedure let you specify the
>parameter profile of the subprogram being inherited, in case subprograms
>with the same name are distinguished by their profiles.
>
>Pragmas In_Fun and In_Pro are abbreviations for Inherits_Function and
>Inherits_Procedure, respectively.  (They are not intended to suggest that
>functions are more enjoyable, or that procedures are more remunerative :-)
>
>A subprogram listed in an Inherits pragma may not also be over-ridden.
>
>All of these pragmas imply the semantics of a plain pragma Inherits.
>
>This lets you enumerate, in a given class package, all the methods for
>that class, even the ones it inherits.  It also helps catch spelling or
>typing errors, where you intend to over-ride a parent class's method but
>instead create a new message by mistake (e.g., trying to over-ride
>"Initialize" with a procedure named "Initialise").
>
>
>4. pragma Hides (subprogram_name...)
>
>   pragma Hides_Function (function_name, return_type, parameter_type...)
>   pragma Hides_Procedure (procedure_name, parameter_type...)
>
>Legal directly inside the declarative region of a "Class" package (plain
>or generic, although it is unlikely to be used in a generic).
>
>"pragma Hides" must list subprograms that are inherited.  Like "pragma
>Inline," all subprograms of that name are hidden, and multiple "pragma
>Hides" may be used.
>
>A subprogram listed in an Inherits pragma may not also be over-ridden.
>
>All of these pragmas imply the semantics of a plain pragma Inherits.
>
>If a subprogram listed in a Hides pragma is called, a new exception will
>be raised.  If the compiler detects at run-time that this exception will
>be raised, the compilation should fail.  (This obviously needs some work
>to get it semantically precise.)
>
>This breaks Ada's guarantee that no object can receive a message that it
>doesn't have a method for, but it does so in an orderly way and in a way
>that is visible in the package specification.  It is preferable, in a few
>cases, to do so, rather than to inherit an irrational method.
>
>"pragma Hides" implicitly over-rides the subprogram's body with one that
>just raises the exception, and that is what any child of this class
>inherits.  It may un-hide the subprogram by over-riding it.  However, it
>cannot re-inherit the subprogram of the class's parent.
>
>
>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.
>
>(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.)
>
>Ideally, the compiler would refuse to allow this type to be extended
>except in another "Object_First" package, but I'm not sure if that kind of
>restriction is possiible.
>
>
>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.
>
>
>7. pragma Categorized;
>   pragma Category (Identifier, Subprogram_Identifier...);
>
>Legal directly inside the declarative region of a "Class" package (plain
>or generic).  "pragma Category()" must follow the declarations of the
>subprograms in its parameter list.
>
>"pragma Categorized" causes compilation to fail unless all methods of
>the class (subprograms that precede any "pragma Class_Subprograms")
>are listed in a "pragma Category".
>
>"pragma Category" indicates that the listed subprograms fall into the
>named category.
>
>This is intended to communicate with analysis tools.  For instance, a
>tool may draw a line for each message that objects of one class send
>to objects of another; all the subprograms in a category could be
>lumped into one line, so that instead of showing 20 control functions
>the diagram would show only one line labelled "control".
>
>The identifier is arbitrary, and more than one "pragma Category" in a
>given compilation may use the same identifier.
>
>"pragma Category" may be used without "pragma Categorized" to indicate
>categories for a subset of the class's methods.
>
>- - - - -
>That ends the pragmas I have thought of to date.
>
>Best,
>Sam Mize
>
>--
>Samuel Mize -- smize@imagin.net (home email) -- Team Ada
>Fight Spam: see http://www.cauce.org/ \\\ Smert Spamonam






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

* Re: "Classes" as packages in Ada
  1998-11-25  0:00   ` Ed Falis
@ 1998-11-25  0:00     ` John Goodsen
  1998-11-25  0:00       ` Robert I. Eachus
                         ` (2 more replies)
  0 siblings, 3 replies; 10+ messages in thread
From: John Goodsen @ 1998-11-25  0:00 UTC (permalink / raw)


So my conclusion is that Ada code for an interface is not even
close in simplicity (from the programmer's point of view) as
something like:

public class ObserverEvent {
    ...
}
public interface Observer {
    public void Update(Observable);
}
public interface Observerable {
    public void AddObserver(Observer);
    public void RemoveObserver(Observer);
    public void NotifyObservers(Observer, ObserverEvent).
}

Is it?  And that's been my point all along.  Any goals of making
Ada generally acceptable were, IMHO, just paid a bit a lip service
and now we've got this beautiful, powerful language that scares
the hell out of people from trying it out.  It's a shame.

--
John Goodsen       RADSoft / Training, Mentoring and Consulting in:
jgoodsen@radsoft.com       - UML modeling and OOA/D principles
http://www.radsoft.com     - Use Centered Object-Oriented Design
888.298.2566                - Rapid Incremental S/W Delivery Process

Ed Falis wrote in message <1103_911962898@DZOG-CHEN>...
>On Tue, 24 Nov 1998 20:15:40 GMT, "John Goodsen" <jgoodsen@radsoft.com> wrote:
>> BTW, this brings up a curious point.  How do you implement an <<interface>>
>> in Ada?  Sorry if it's a simple answer - but's its been a few years now
>> since I've touched an Ada compiler.
>>
>
>Well, I'm willing to take a crack at it.  It's actually a specific instance of the most general (but not only) way of doing
multiple inheritance in Ada.  For mix-ins, generics with a
>formal derived type offer a more straightforward approach.
>
>Suppose we've put together a portion of a design in the form of a role model (a la Reenskaug) or a collaboration diagram.  What we
want to do is define a class (or tagged
>type in Ada parlance) that implements one or more roles.  We can choose to have some behavioral implementation associated in
general with the role, or keep the whole thing
>abstract as in a Java interface.
>
>So, suppose we look at the publish-subscribe / observer-observalbe pattern.  In Ada style we'd likely put the entire pattern into a
single package, since it doesn't make a lot of
>sense to define one with out the other.  (I'm hoping my mailer preserves indentation here):
>
>package Publish_Subscribe is
>    -- "Push" information associated with observer notification,
>    -- basically a "command" subpattern:
>    type Event is abstract tagged limited private;
>    type Event_Ptr is access all Event'Class;
>
>    procedure Action(This: access Event; Subject: access Observable'Class) is abstract;
>
>    -- The publisher/observable interface / role:
>    type Observable is abstract tagged limited private;
>    type Observable_Ptr is access all Observable'Class;
>
>    procedure Add_Observer(This: access Observable; Obs: access Observer'Class);
>    procedure Delete_Observer(This: access Observable; Obs: access Observer'Class);
>    procedure Notify_Observers(This: access Observable; Evt: access Event'Class);
>
>
>    -- The observer interface / role:
>    type Observer is abstract tagged limited private;
>    type Observer_Ptr is access all Observer'Class;
>
>
>    procedure Update(
>        This: access Observer;
>        Source: access Observable'Class;
>        Evt: access Event'Class) is abstract;
>
>
>private
>    -- Infomation necessary for the compiler to physically handle the defined types/classes:
>    type Event is abstract tagged limited null record;
>
>    type Observable is abstract tagged limited record
>        -- Head of a naive list implementation:
>        Observers: Observer_Ptr;
>    end record;
>
>    type Observer is abstract tagged limited record
>        -- Pointer to next observer in the naive list implementation
>        Next_Observer: Observer_Ptr;
>    end record;
>
>end Publish_Subscribe;
>
>
>Note that the approach taken here is to provide an implementation of common parts of the role.  Therefore, the various methods
other than Action for the Event type, and
>Update for the Observer are not abstract - they're involved with a common implementation used for the Observer/Observable pattern.
Also note that the three typed defined
>are all abstract - one never creates a role "object" directly.   I've left out the implementation of the concrete methods here.
>
>Now, supposing we want to define a class or type of object that implements the observer role.  First we create a concrete observer
class relative to object we wish to create:
>
>-- Forward reference to the type implementing the observer interface::
>type  Implementing_an_Observer;
>
>-- Just a way to look at the implementing class as an observer.
>-- The "access" parameter "This" allows access to other views and fields of an instance of the class:
>type Observer_Role(This: access Implementing_an_Observer) is new Observer with null record;
>
>-- Provide an implementation of the abstract update method:
>procedure Update(This: access Observer_Role;
>        Source: access Observable'Class;
>        Evt: access Event'Class) ;
>
>-- Full definition of the implementing type:
>type Implementing_an_Observer is record
>     -- view allows looking at a type instance as an observer:
>     Observer_View: aliased Observer_Role(Implementing_an_Observer'access);
>     -- Other fields, possibly providing other role views
>end record;
>
>
>Having declared an object:
>
>Obs: Implementing_an_Observer;
>
>we can now look view it as an observer like so:
>
>Add_Observer(Some_Model, Obs.Observer_View'access);
>
>Any number of subfields can provide different views of the object corresponding to multiple inheritance, with the concrete type for
the view either inheriting methods or
>overriding them.  A fully abstract role description corresponds to a Java interface.
>
>This IS a bit cumbersome when you first run into it, but it's pretty straightforward once you get the idea, and it's extremely
flexible.  It also avoids the multiple inheritance
>diamond problem since each view has its own associated members.
>
>And, as I mentioned, if you just want to do a decorator or similar mix-in, a generic with a formal that allows anything inheriting
from some type provides a simpler mechanism for
>such more common uses of multiple inheritance.
>
>- Ed
>
>






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

* Re: "Classes" as packages in Ada
  1998-11-25  0:00     ` John Goodsen
@ 1998-11-25  0:00       ` Robert I. Eachus
  1998-11-28  0:00         ` Brian Rogoff
                           ` (2 more replies)
  1998-11-27  0:00       ` Larry Kilgallen
  1999-03-28  0:00       ` Matthew Heaney
  2 siblings, 3 replies; 10+ messages in thread
From: Robert I. Eachus @ 1998-11-25  0:00 UTC (permalink / raw)


In article <3UU62.124$8X3.638914@news.rdc1.az.home.com> "John Goodsen" <jgoodsen@radsoft.com> writes:

  > So my conclusion is that Ada code for an interface is not even
  > close in simplicity (from the programmer's point of view) as
  > something like...

   About all I can say, is you are right, and you are wrong.  As Ed
pointed out, most of what requires interfaces in other languages is
done with generic mix-ins in Ada 95.  I'd say you were comparing
apples and oranges, but it is more like comparing Waldorf salad and
apples.  I have a package (actually family of packages) that is simple
to use for event notification which not only allows for all the bells
and whistles Ed showed, but a few more besides.  (You can have
multiple delivery methods for the same class of messages, it is
closely integrated with Ada tasking, etc., etc.)

   But if I wanted to simply implement the Ada equivalent to John's
classes, I'd probably use an Ada protected object.  (Note to Ada 95
programmers, this is a perfect case for using requeue to insure that
each observer is notified once for each event.)

    protected type Observed_Object is
      procedure Set(Observed: in Object);
      function Value return Object;
      entry Modified(Observed: out Object);
    private
      entry Waiting(Observed: out Object);
      Current: Object;
      Reporting: Boolean := False;
    end Observed_Object;
       
    protected body Observed_Object is
      procedure Set (Observed: in Object) is
      begin
        Current := Observed;
        Reporting := True;
      end Set;

      function Value return Object is begin return Current; end Value;

      entry Modiified(Observed: out Object) when not Reporting is  
      begin requeue Waiting; end;

      entry Waiting(Observed: out Object) when Reporting is
      begin
        Observed := Current;
        if Waiting'Count = 0 then Reporting := False; end if;
      end; 

    end Observed_Object;

    Now I probably could spend pages describing why I made certain
choices in this version, and how to modify it to deal with other
cases, but I'll refrain.  (Knowing the list though, I'm sure there
will be a dozen variations posted by Monday ;-)  Some other
possibilities include a class of events, allowing callers to wait for
more than one event, using access types to allow the object value to
be limited, etc.

    I'll just say that this implementation deals naturally with
concurrency, the protected declaration is of a size with John's
interface, and the implementation is almost trivial.

    For those that don't know Ada too well, and this is a new and not
to well explored corner of Ada 95, here is how it works.  A thread
wanting to be notified of the next event calls Modified usually in a
loop:

    while Something loop
      Some_Object.Modified(Value);
      Do_Something_with(Value;
    end loop;

    If the protected object is not currently reporting to queued
callers, the call is immediately requeued on Waiting.  When someone
modifies the current value, Reporting is set to true, all the tasks
currently waiting on Waiting are given the new value to do with as
they will, then any tasks that have already called Modified get moved
to Waiting.

--

					Robert I. Eachus

with Standard_Disclaimer;
use  Standard_Disclaimer;
function Message (Text: in Clever_Ideas) return Better_Ideas is...




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

* Re: "Classes" as packages in Ada
  1998-11-24  0:00 ` John Goodsen
@ 1998-11-25  0:00   ` Ed Falis
  1998-11-25  0:00     ` John Goodsen
  0 siblings, 1 reply; 10+ messages in thread
From: Ed Falis @ 1998-11-25  0:00 UTC (permalink / raw)


On Tue, 24 Nov 1998 20:15:40 GMT, "John Goodsen" <jgoodsen@radsoft.com> wrote:
> BTW, this brings up a curious point.  How do you implement an <<interface>>
> in Ada?  Sorry if it's a simple answer - but's its been a few years now
> since I've touched an Ada compiler.
> 

Well, I'm willing to take a crack at it.  It's actually a specific instance of the most general (but not only) way of doing multiple inheritance in Ada.  For mix-ins, generics with a 
formal derived type offer a more straightforward approach.

Suppose we've put together a portion of a design in the form of a role model (a la Reenskaug) or a collaboration diagram.  What we want to do is define a class (or tagged 
type in Ada parlance) that implements one or more roles.  We can choose to have some behavioral implementation associated in general with the role, or keep the whole thing 
abstract as in a Java interface.

So, suppose we look at the publish-subscribe / observer-observalbe pattern.  In Ada style we'd likely put the entire pattern into a single package, since it doesn't make a lot of 
sense to define one with out the other.  (I'm hoping my mailer preserves indentation here):

package Publish_Subscribe is
    -- "Push" information associated with observer notification,
    -- basically a "command" subpattern:
    type Event is abstract tagged limited private;
    type Event_Ptr is access all Event'Class;
    
    procedure Action(This: access Event; Subject: access Observable'Class) is abstract;

    -- The publisher/observable interface / role:
    type Observable is abstract tagged limited private;
    type Observable_Ptr is access all Observable'Class;
    
    procedure Add_Observer(This: access Observable; Obs: access Observer'Class);
    procedure Delete_Observer(This: access Observable; Obs: access Observer'Class);
    procedure Notify_Observers(This: access Observable; Evt: access Event'Class);


    -- The observer interface / role:
    type Observer is abstract tagged limited private;
    type Observer_Ptr is access all Observer'Class;
    
    
    procedure Update(
        This: access Observer; 
        Source: access Observable'Class;
        Evt: access Event'Class) is abstract;
    
    
private
    -- Infomation necessary for the compiler to physically handle the defined types/classes:
    type Event is abstract tagged limited null record;
    
    type Observable is abstract tagged limited record
        -- Head of a naive list implementation:
        Observers: Observer_Ptr;
    end record;
    
    type Observer is abstract tagged limited record
        -- Pointer to next observer in the naive list implementation
        Next_Observer: Observer_Ptr;
    end record;
    
end Publish_Subscribe;

 
Note that the approach taken here is to provide an implementation of common parts of the role.  Therefore, the various methods other than Action for the Event type, and 
Update for the Observer are not abstract - they're involved with a common implementation used for the Observer/Observable pattern.  Also note that the three typed defined 
are all abstract - one never creates a role "object" directly.   I've left out the implementation of the concrete methods here.

Now, supposing we want to define a class or type of object that implements the observer role.  First we create a concrete observer class relative to object we wish to create:

-- Forward reference to the type implementing the observer interface::
type  Implementing_an_Observer;

-- Just a way to look at the implementing class as an observer.
-- The "access" parameter "This" allows access to other views and fields of an instance of the class:
type Observer_Role(This: access Implementing_an_Observer) is new Observer with null record; 

-- Provide an implementation of the abstract update method:
procedure Update(This: access Observer_Role; 
        Source: access Observable'Class;
        Evt: access Event'Class) ;

-- Full definition of the implementing type:
type Implementing_an_Observer is record
     -- view allows looking at a type instance as an observer:
     Observer_View: aliased Observer_Role(Implementing_an_Observer'access);   
     -- Other fields, possibly providing other role views
end record;


Having declared an object:

Obs: Implementing_an_Observer;

we can now look view it as an observer like so:

Add_Observer(Some_Model, Obs.Observer_View'access);

Any number of subfields can provide different views of the object corresponding to multiple inheritance, with the concrete type for the view either inheriting methods or 
overriding them.  A fully abstract role description corresponds to a Java interface.

This IS a bit cumbersome when you first run into it, but it's pretty straightforward once you get the idea, and it's extremely flexible.  It also avoids the multiple inheritance 
diamond problem since each view has its own associated members.

And, as I mentioned, if you just want to do a decorator or similar mix-in, a generic with a formal that allows anything inheriting from some type provides a simpler mechanism for 
such more common uses of multiple inheritance.

- Ed






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

* Re: "Classes" as packages in Ada
  1998-11-25  0:00     ` John Goodsen
  1998-11-25  0:00       ` Robert I. Eachus
@ 1998-11-27  0:00       ` Larry Kilgallen
  1999-03-28  0:00       ` Matthew Heaney
  2 siblings, 0 replies; 10+ messages in thread
From: Larry Kilgallen @ 1998-11-27  0:00 UTC (permalink / raw)


In article <3UU62.124$8X3.638914@news.rdc1.az.home.com>, "John Goodsen" <jgoodsen@radsoft.com> writes:

> Is it?  And that's been my point all along.  Any goals of making
> Ada generally acceptable were, IMHO, just paid a bit a lip service
> and now we've got this beautiful, powerful language that scares
> the hell out of people from trying it out.  It's a shame.

Certainly it does not scare poeple in general.
It may scare people who are terminally set in their ways.
To the extent that they are scared away, the average quality
of code written in Ada may be enhanced.

Larry Kilgallen




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

* Re: "Classes" as packages in Ada
  1998-11-25  0:00       ` Robert I. Eachus
@ 1998-11-28  0:00         ` Brian Rogoff
  1998-11-30  0:00         ` Robert I. Eachus
  1999-03-28  0:00         ` Matthew Heaney
  2 siblings, 0 replies; 10+ messages in thread
From: Brian Rogoff @ 1998-11-28  0:00 UTC (permalink / raw)


On 25 Nov 1998, Robert I. Eachus wrote:
> In article <3UU62.124$8X3.638914@news.rdc1.az.home.com> "John Goodsen" <jgoodsen@radsoft.com> writes:
> 
>   > So my conclusion is that Ada code for an interface is not even
>   > close in simplicity (from the programmer's point of view) as
>   > something like...
> 
>    About all I can say, is you are right, and you are wrong.  As Ed
> pointed out, most of what requires interfaces in other languages is
> done with generic mix-ins in Ada 95.  I'd say you were comparing
> apples and oranges, but it is more like comparing Waldorf salad and
> apples. 

Well, I think in this case that John is more right than wrong, though I'd
add a great big "So !@#$ing what?" to that since any number of things
involving generics, enums, range types, etc., etc. are expressed simply in
Ada and with a bit more difficulty in Java. 

OTOH, the interface feature of Java, or the "signature" feature of 
GNU C++, are IMO appealing candidates for consideration in a future
version of Ada. Interfaces, besides capturing the most common use of 
multiple inheritance, make Norman Cohen's workaround to the "withing
problem" (the dummy parent approach) much more acceptable IMO.

-- Brian






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

* Re: "Classes" as packages in Ada
  1998-11-25  0:00       ` Robert I. Eachus
  1998-11-28  0:00         ` Brian Rogoff
@ 1998-11-30  0:00         ` Robert I. Eachus
  1999-03-28  0:00         ` Matthew Heaney
  2 siblings, 0 replies; 10+ messages in thread
From: Robert I. Eachus @ 1998-11-30  0:00 UTC (permalink / raw)


In article <EACHUS.98Nov25182652@spectre.mitre.org> eachus@spectre.mitre.org (Robert I. Eachus) writes:


   It's not usual to respond to your own message, but there are some
race conditions in the body of the generic I posted.   I realized that
they existed as I was driving home.  (And that is one of the very nice
things about Ada tasking--it makes these things pretty obvious.)

   The first is easy to fix, and in this case it is really a fix.  If
a call to Set occurs during notification, some waiters will get
notified of the first event and some of the second, but none will hear
of both.  The fix is to make the procedure Set an entry, and put on a
guard:

       protected type Observed_Object is
--	 procedure Set(Observed: in Object);
         entry Set(Observed: in Object);
	 function Value return Object;
	 entry Modified(Observed: out Object);
       private
	 entry Waiting(Observed: out Object);
	 Current: Object;
	 Reporting: Boolean := False;
       end Observed_Object;

       protected body Observed_Object is
         entry Set (Observed: in Object) when not Reporting is
--	 procedure Set (Observed: in Object) is
	 begin
	   Current := Observed;
	   Reporting := True;
	 end Set;

	 function Value return Object is begin return Current; end Value;

	 entry Modiified(Observed: out Object) when not Reporting is  
	 begin requeue Waiting; end;

	 entry Waiting(Observed: out Object) when Reporting is
	 begin
	   Observed := Current;
	   if Waiting'Count = 0 then Reporting := False; end if;
	 end; 

       end Observed_Object;

    The second potential problem is that an event may occur while one
of the consumers is still processing the previous event.  The best way
to deal with this is to put a counter in the events, and to keep a
buffer of previous events to be dealt with.  Of course, the reality is
that some consumers may really need all events, and others may only
need to see the most recent event.  (I'm used to situations where
updates are periodic and only the most recent event should be
handled.)  So I choose the KISS principle and leave it to the consumer
of the events to choose which type of processing is required.

    However, that doesn't deal with all possibilities.  A consumer may
process all known events then queue up, but just miss the next event
in a race.  So let's internalize the counter to simplify things:


       protected type Observed_Object is
         entry Set(Observed: in Object);
	 function Value return Object;
	 entry Modified(Observed: out Object; Last: in out Integer);
       private
	 entry Waiting(Observed: out Object; Last: in out Integer);
	 Current: Object;
         Count: Integer := 0;
	 Reporting: Boolean := False;
       end Observed_Object;

       protected body Observed_Object is
         entry Set (Observed: in Object) when not Reporting is
	 begin
	   Current := Observed;
           if Count = Integer'Last
           then Count := 0;
           else Count := Count + 1;
           end if;  -- at 32 bits and 100 events per second rolls over
                    -- after eight months.
	   Reporting := True;
	 end Set;

	 function Value return Object is begin return Current; end Value;

         function Current_Count return Integer is 
         begin
           return Count;
         end Current_Count;

	 entry Modified(Observed: out Object; Last: in out Integer)
            when not Reporting is  
	 begin
           if Last /= Count
           then
             Observed := Current;
             Last := Count;
           else 
             requeue Waiting;
           end if;
         end Modified;

	 entry Waiting(Observed: out Object; Last: in out Integer)
           when Reporting is
	 begin
	   Observed := Current;
           Last := Count;
	   if Waiting'Count = 0 then Reporting := False; end if;
	 end; 

       end Observed_Object;

       If you prefer, the counter can be made part of the obesrved
object, but then you really need to pass the old count value as a
separate parameter, so the interface isn't that much cleaner.
--

					Robert I. Eachus

with Standard_Disclaimer;
use  Standard_Disclaimer;
function Message (Text: in Clever_Ideas) return Better_Ideas is...




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

* Re: "Classes" as packages in Ada
  1998-11-25  0:00     ` John Goodsen
  1998-11-25  0:00       ` Robert I. Eachus
  1998-11-27  0:00       ` Larry Kilgallen
@ 1999-03-28  0:00       ` Matthew Heaney
  2 siblings, 0 replies; 10+ messages in thread
From: Matthew Heaney @ 1999-03-28  0:00 UTC (permalink / raw)


"John Goodsen" <jgoodsen@radsoft.com> writes:

> So my conclusion is that Ada code for an interface is not even
> close in simplicity (from the programmer's point of view) as
> something like:
> 
> public class ObserverEvent {
>     ...
> }
> public interface Observer {
>     public void Update(Observable);
> }
> public interface Observerable {
>     public void AddObserver(Observer);
>     public void RemoveObserver(Observer);
>     public void NotifyObservers(Observer, ObserverEvent).
> }


There are a few variations of the Observer pattern theme in the March 99
ACM patterns archive.

<mailto:patterns@acm.org>
<http://www.acm.org/archives/patterns.html>


The example in the article "Observer Is Subject" has a spec very similar
to what you wrote above:


package Subjects_And_Observers is

   type Root_Subject_Type is
     abstract tagged limited private;

   procedure Notify (Subject : in out Root_Subject_Type'Class);


   type Root_Observer_Type is
     abstract new Root_Subject_Type with private;

   procedure Update (Observer : access Root_Observer_Type) is abstract;


   procedure Attach
     (Observer : access Root_Observer_Type'Class;
      To       : access Root_Subject_Type'Class);

   procedure Detach
     (Observer : access Root_Observer_Type'Class;
      From     : access Root_Subject_Type'Class);
...
end Subjects_And_Observers;


See also my versions of the Mediator pattern, which use the Observer
pattern for their implementation.

I have converted every example in the GoF Design Patterns book to
Ada95.  All the work is archived at the URL above.

You can subscribe to the ACM patterns list by sending the message (body)

subscribe patterns <your full name>


to the ACM mailing-list server.

<mailto:listserv@acm.org>


Matt




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

* Re: "Classes" as packages in Ada
  1998-11-25  0:00       ` Robert I. Eachus
  1998-11-28  0:00         ` Brian Rogoff
  1998-11-30  0:00         ` Robert I. Eachus
@ 1999-03-28  0:00         ` Matthew Heaney
  2 siblings, 0 replies; 10+ messages in thread
From: Matthew Heaney @ 1999-03-28  0:00 UTC (permalink / raw)


eachus@spectre.mitre.org (Robert I. Eachus) writes:

>    But if I wanted to simply implement the Ada equivalent to John's
> classes, I'd probably use an Ada protected object.  (Note to Ada 95
> programmers, this is a perfect case for using requeue to insure that
> each observer is notified once for each event.)
> 
>     protected type Observed_Object is
>       procedure Set(Observed: in Object);
>       function Value return Object;
>       entry Modified(Observed: out Object);
>     private
>       entry Waiting(Observed: out Object);
>       Current: Object;
>       Reporting: Boolean := False;
>     end Observed_Object;
>        
>     protected body Observed_Object is
>       procedure Set (Observed: in Object) is
>       begin
>         Current := Observed;
>         Reporting := True;
>       end Set;
> 
>       function Value return Object is begin return Current; end Value;
> 
>       entry Modiified(Observed: out Object) when not Reporting is  
>       begin requeue Waiting; end;
> 
>       entry Waiting(Observed: out Object) when Reporting is
>       begin
>         Observed := Current;
>         if Waiting'Count = 0 then Reporting := False; end if;
>       end; 
> 
>     end Observed_Object;
> 
>     Now I probably could spend pages describing why I made certain
> choices in this version, and how to modify it to deal with other
> cases, but I'll refrain.  (Knowing the list though, I'm sure there
> will be a dozen variations posted by Monday ;-)


Oh, they were posted by Monday, just not the Monday you had in mind. ;^)

In the March 99 ACM patterns archive, you'll find these examples of the
Observer pattern:

  observer (see also revised version)
  observer is subject
  observers of multiple subjects
  mediator
  mediator (alternate version)


I have converted every example in the GoF Design Patterns book to Ada95,
and posted them to the ACM patterns list.

<mailto:patterns@acm.org>
<http://www.acm.org/archives/patterns.html>

You can subscribe to the patterns list by sending the message (body)

subscribe patterns <your full name>


to the ACM mailing list server.

<mailto:listserv@acm.org>


Matt










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

end of thread, other threads:[~1999-03-28  0:00 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
1998-11-24  0:00 "Classes" as packages in Ada Samuel Mize
1998-11-24  0:00 ` John Goodsen
1998-11-25  0:00   ` Ed Falis
1998-11-25  0:00     ` John Goodsen
1998-11-25  0:00       ` Robert I. Eachus
1998-11-28  0:00         ` Brian Rogoff
1998-11-30  0:00         ` Robert I. Eachus
1999-03-28  0:00         ` Matthew Heaney
1998-11-27  0:00       ` Larry Kilgallen
1999-03-28  0:00       ` Matthew Heaney

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