CVS difference for ais/ai-00345.txt

Differences between 1.6 and version 1.7
Log of other versions for file ais/ai-00345.txt

--- ais/ai-00345.txt	2004/01/23 01:26:41	1.6
+++ ais/ai-00345.txt	2004/01/23 04:40:26	1.7
@@ -676,6 +676,60 @@
 ****************************************************************
 
 From: Tucker Taft
+Sent: Monday, January 12, 2004  3:51 PM
+
+Pascal grumbled a bit about task and protected interfaces
+not being unified with other kinds of interfaces.
+We have also discussed the possibility of allowing
+limited interfaces as ancestors of non-limited types.
+
+It would seem possible to allow limited interfaces to be
+ancestors of more than just limited tagged types and interfaces.
+Perhaps we should consider (or put on the "roadmap" for Ada 2015? ;-)
+allowing limited interfaces to be ancestors of non-limited interfaces/tagged
+types, as well as protected/task interfaces/types.
+
+It would mean that protected/task objects would need a "true" tag
+rather than a pseudo-tag, but I doubt if that would be a huge
+burden, since they often already include a pointer to some
+kind of type descriptor.
+
+Does anyone know of any specific semantic or implementation problems with
+allowing limited interfaces being ancestors of non-limited types
+and/or protected/task types?
+
+We might simplify the problem by restricting it to limited interfaces
+with no functions with controlling results (vaguely analogous to
+the restriction we have on tagged incomplete types), to avoid
+the morass associated with returning limited objects.
+
+Note that we don't allow a tagged limited private type to be non-limited
+in the full view to avoid ending up with assignment being applied to some
+limited extension of the partial view that has limited components.
+This isn't a problem for limited interfaces, since they don't have
+any components.
+
+****************************************************************
+
+From: Randy Brukardt
+Sent: Monday, January 12, 2004  4:14 PM
+
+> Does anyone know of any specific semantic or implementation problems with
+> allowing limited interfaces being ancestors of non-limited types
+> and/or protected/task types?
+
+Steve can correct me if I'm wrong, but I believe the problem was with
+return-by-reference functions. With these interfaces, you couldn't tell at
+compile-time whether a function was return-by-reference or not.
+
+Of course, if 318 is adopted with the semantics discussed at the meeting,
+that would be no longer a problem. If that was in fact the only problem, we
+ought to reconsider the idea *IF* (and that's a big if) we have the stomach
+for the incompability in function definitions.
+
+****************************************************************
+
+From: Tucker Taft
 Sent: Thursday, January 22, 2004  10:34 AM
 
 Here is the first write-up of this AI that includes
@@ -1081,3 +1135,434 @@
 cases of wanting multiple implementations of protected objects or tasks.
 
 ****************************************************************
+
+From: Robert I. Eachus
+Sent: Thursday, January 22, 2004   7:33 PM
+
+Tucker Taft wrote:
+
+>>That opens a can of worms, but I think it is one that is worth opening.
+>>Right now, the proposal says that task interface types and protected
+>>interface types are also abstract types.  If we change the definitions
+>>around, we could have "abstract types", "task interface types", and
+>>"protected interface types" be exclusive.
+>
+>Why would that be a good idea?  An abstract type is one that
+>is not allowed to have objects (aka "instances" in OO parlance).
+>
+I think you are missing what I intended.  What I don't like is the term
+"limited abstract type" as it is used in the current write-up: "Limited
+interface types that are specifically for use in defining task or
+protected types may also be defined (see 9.1 and 9.4).}"
+
+There is nothing really wrong with the sentence.  But including the
+technical/syntax terms "task interface type" and
+protected interface type in "abstract types" makes for some complext
+wording.  If you wanted to make the technical  term something other than
+abstract type, that would be fine.  But the idea is to have a name for
+those abstract types that may include task or protected components but
+are not abstract task types or abstract protected types.
+
+We got into this problem in Ada 83.  Record types with discriminants
+that have default values are significantly different from other record
+types.  But since Ada didn't introduce a technical term, it was one of
+those things that had to be taught specially so that Ada users
+understood that these types were somewhat magic.  It seems to me we are
+creating a similar potential problem here.  Non-limited abstract types
+are one category, abstract types that happen to be limited are another.
+But task abstract types and protected abstract types are special in
+other ways.  I don't like the fact that adding a well understood
+qualifier ("limited") to another well understood term ("abstract type")
+suddenly brings in these two categories from left field.
+
+I think we all understand what the intent is here.  I am just trying to
+look at the whole thing from outside and see how easy or hard it is to
+learn or teach.  Having a separate technical term here would help.  That
+is all that I am really trying to do.  I think that keeping the
+technical meaning of "abstract type" unchanged, and adding "abstract
+task types" and "abstract protected types" as something else works.  But
+I am not tied to a particular technical term or group of technical
+terms.  Just as long as specifying "abstract types that include limited
+abstract types but not task abstract types or protected abstract types"
+isn't such a jawbreaker.
+
+>>... What does get used is abstract subprogram.  Adding abstract
+>>entries means that we should add that a call to an abstract entry must
+>>be dispatching.  (Excuse me, shall be dispatching.)..
+>
+>I'm not sure that is necessary.  Entry queues are
+>associated with objects, and calls are added to entry
+>queues and then "serviced".  I think that provides
+>the implicit level of indirection that is equivalent
+>to dispatching.  On the other hand, we may need
+>to say something more about protected subprogram calls
+>when the prefix is class-wide.
+
+I was thinking about cases where an entry call maps to an abstract entry
+declaration, but there is no corresponding object.  For there to be code
+like that the entry name must be bare.  (Not object.entry)  I haven't
+figured out if such calls can actually get executed.
+
+****************************************************************
+
+From: Tucker Taft
+Sent: Thursday, January 22, 2004   8:15 PM
+
+Randy Brukardt wrote:
+
+> Tucker Taft wrote:
+>
+>>>... I have modified this
+>>>proposal to allow task/protected types to be derived
+>>>from "normal" limited interfaces as well as from
+>>>task/protected interfaces.  ...
+>>
+>>I suppose we could take one further step, and allow
+>>task types/interfaces to be derived from protected
+>>interfaces that have only abstract entry/entry families.
+>
+>
+> I don't know if that is useful. But the problem with this proposal is that
+> it doesn't seem to help in all of the cases where you might want to mix
+> implementations.
+>
+> For one thing, while a task can be derived from a "regular" limited
+> interface, such an interface has to be devoid of operations -- making the
+> capability fairly useless.
+
+Why must the interface be devoid of operations?
+
+In the original proposal I disallowed operations
+"outside" the task interface type, but I changed
+that in the current proposal, and no longer disallow
+primitive operations of a task/protected interface type.
+These are effectively dispatching operations, just
+like those of a tagged type, and would require essentially
+the same mechanism to implement them.  The only difference
+is that there is no hierarchy of "concrete" task/protected
+types possible.  The concrete types appear at the leaves
+only.  I'm not sure what difference that would make from
+an implementation point of view...
+
+>
+> For another thing, a lot of the capabilities that you might want don't seem
+> to be possible.
+>
+> Consider a queue interface (I'm using fixed items here for simplicity; it's
+> likely that this whole thing would be wrapped in a generic):
+>
+>   package AQ is
+>     type Abstract_Queue is limited interface;
+>     procedure Add_Item (Queue : in out Abstract_Queue; Item : in Integer);
+>     procedure Remove_Item (Queue : in out Abstract_Queue; Item : out
+> Integer);
+>   end AQ;
+>
+> Then the standard implementation would look something like:
+>    with AQ;
+>    package NQ is
+>       type Queue is new AQ.Abstract_Queue with private;
+>       procedure Add_Item (Queue : in out Queue; Item : in Integer);
+>       procedure Remove_Item (Queue : in out Queue; Item : out Integer);
+>          -- Raises Empty_Error if empty.
+>       Empty_Error : exception;
+>    end NQ;
+>
+> Of course, the interface could be used in other components that needed a
+> queue.
+>
+> Now, say you want a version of a queue to use for multiple tasks, which
+> blocks on empty rather than raising an exception. You'd write something
+> like:
+>
+>    with AQ;
+>    package PQ is
+>       protected type Blocking_Queue is new AQ.Abstract_Queue with
+>          procedure Add_Item (Item : in Integer);
+>          entry Remove_Item (Item : out Integer);
+>       private ...
+>       end Blocking_Queue;
+>    end PQ;
+>
+> But of course that isn't allowed, because Remove_Item isn't a procedure.
+> Moreover, these aren't subtype conformant. Your wording fails to take into
+> account the implied object of the calls.
+
+Apparently I wasn't being clear at all.
+
+If a protected type implements a limited interface, it must have
+primitive operations *outside* the type declaration that override
+those inherited from the interface.  These will presumably be
+implemented in terms of protected operations of the type.  E.g.:
+
+     package PQ is
+         protected type Blocking_Queue ... [as you have it]
+         procedure Add_Item (Queue : in out Blocking_Queue;
+           Item : in Integer);
+         procedure Remove_Item (Queue : in out Blocking_Queue;
+           Item : out Integer);
+     end PQ;
+     package body PQ is
+         protected body Blocking_Queue ... [as you would expect]
+         procedure Add_Item (Queue : in out Blocking_Queue;
+           Item : in Integer) is
+         begin
+             Queue.Add_Item(Item);
+         end Add_Item;
+         procedure Remove_Item (Queue : in out Blocking_Queue;
+           Item : out Integer) is
+         begin
+             Queue.Remove_Item(Item);
+         end Remove_Item;
+     end PQ;
+
+This is essentially the same thing you would have to do
+if you had a generic that took a limited private type with
+formal subprograms Add_Item and Remove_Item.  To pass
+in a protected type to such a generic, you
+basically have to create "wrappers" that turn around
+and call the protected operations.  The capability
+provided by interfaces is very similar to that provided
+by a generic with various formal subprograms, except
+of course it supports run-time polymorphism, rather than
+compile-time polymorphism.  Since task and protected types
+can be passed in as the actual type for a limited private
+formal type, there seems some logic in allowing task
+and protected types to be derived from limited interface
+types.
+
+Since protected and task types generally already have type
+descriptors of some sort, giving them a structure that would
+allow them to also double as (limited) tagged type descriptors
+seems relatively straightforward.  Whether the effort is
+worth it depends on the relative importance of this
+inheritance integration.
+
+
+ > ...
+> Without [compiler-provided wrappers],
+ > ... I don't see much point in the whole mechanism. Other than basic
+> locks (which you should avoid anyway), there don't seem to be that many
+> cases of wanting multiple implementations of protected objects or tasks.
+
+The advantage of the proposal seems to be that if you want a
+type to be *visibly* a protected or task type, but you *also*
+want it to implement some important interface, this gives you
+that capability.
+
+Alternatively, you could wrap the task or protected type
+in a visible tagged record type that implemented the various interfaces,
+but that seems to defeat the level of inheritance integration
+we might be trying to achieve.
+
+****************************************************************
+
+From: Randy Brukardt
+Sent: Thursday, January 22, 2004   9:37 PM
+
+> Randy Brukardt wrote:
+> > Tucker Taft wrote:
+> >
+> >>>... I have modified this
+> >>>proposal to allow task/protected types to be derived
+> >>>from "normal" limited interfaces as well as from
+> >>>task/protected interfaces.  ...
+> >>
+> >>I suppose we could take one further step, and allow
+> >>task types/interfaces to be derived from protected
+> >>interfaces that have only abstract entry/entry families.
+> >
+> >
+> > I don't know if that is useful. But the problem with this proposal is
+that
+> > it doesn't seem to help in all of the cases where you might want to mix
+> > implementations.
+> >
+> > For one thing, while a task can be derived from a "regular" limited
+> > interface, such an interface has to be devoid of operations -- making
+the
+> > capability fairly useless.
+>
+> Why must the interface be devoid of operations?
+
+Because the only things allowed in a task are entries, so you couldn't
+provide the needed concrete operations of the type.
+
+...
+> Apparently I wasn't being clear at all.
+
+No, I thought that you were trying to provide an integration of interfaces,
+not a framework for wrappers that you could write yourself.
+
+> If a protected type implements a limited interface, it must have
+> primitive operations *outside* the type declaration that override
+> those inherited from the interface.  These will presumably be
+> implemented in terms of protected operations of the type.  E.g.:
+>
+>      package PQ is
+>          protected type Blocking_Queue ... [as you have it]
+>          procedure Add_Item (Queue : in out Blocking_Queue;
+>            Item : in Integer);
+>          procedure Remove_Item (Queue : in out Blocking_Queue;
+>            Item : out Integer);
+>      end PQ;
+>      package body PQ is
+>          protected body Blocking_Queue ... [as you would expect]
+>          procedure Add_Item (Queue : in out Blocking_Queue;
+>            Item : in Integer) is
+>          begin
+>              Queue.Add_Item(Item);
+>          end Add_Item;
+>          procedure Remove_Item (Queue : in out Blocking_Queue;
+>            Item : out Integer) is
+>          begin
+>              Queue.Remove_Item(Item);
+>          end Remove_Item;
+>      end PQ;
+>
+> This is essentially the same thing you would have to do
+> if you had a generic that took a limited private type with
+> formal subprograms Add_Item and Remove_Item.  To pass
+> in a protected type to such a generic, you
+> basically have to create "wrappers" that turn around
+> and call the protected operations.  The capability
+> provided by interfaces is very similar to that provided
+> by a generic with various formal subprograms, except
+> of course it supports run-time polymorphism, rather than
+> compile-time polymorphism.  Since task and protected types
+> can be passed in as the actual type for a limited private
+> formal type, there seems some logic in allowing task
+> and protected types to be derived from limited interface
+> types.
+>
+> Since protected and task types generally already have type
+> descriptors of some sort, giving them a structure that would
+> allow them to also double as (limited) tagged type descriptors
+> seems relatively straightforward.  Whether the effort is
+> worth it depends on the relative importance of this
+> inheritance integration.
+
+What integration? I don't see any integration here, I just see a wrapper
+that you have to write virtually all of yourself. And you could have written
+it yourself without any special interfaces at all (beyond the AI-251 ones).
+Indeed, this wrapper saves all of two lines of code (and the need to insert
+".Q" in a few places). And it's less flexible than the hand-generated
+wrapper, because you can't have extra data if you need it.
+
+So what is it about this that makes it worth any effort at all?
+
+>
+> > ...
+> > Without [compiler-provided wrappers],
+> > ... I don't see much point in the whole mechanism. Other than basic
+> > locks (which you should avoid anyway), there don't seem to be that many
+> > cases of wanting multiple implementations of protected objects or tasks.
+>
+> The advantage of the proposal seems to be that if you want a
+> type to be *visibly* a protected or task type, but you *also*
+> want it to implement some important interface, this gives you
+> that capability.
+>
+> Alternatively, you could wrap the task or protected type
+> in a visible tagged record type that implemented the various interfaces,
+> but that seems to defeat the level of inheritance integration
+> we might be trying to achieve.
+
+This doesn't come close to the level of interface integration that *I* was
+expecting to get.
+
+Ada's problem has always been that many of its features are isolated from
+the others. That's especially true of the real-time ones. Here, we have a
+chance to provide a real bridge, and something that saves all of two lines
+of code isn't it.
+
+If it turns out that this bridge isn't useful or workable, then we shouldn't
+provide one that vaguely looks like it is simply because we can. We did that
+with return-by-reference functions, and it's obvious how well *that* one
+worked out.
+
+Anyway, my abstract view of interfaces is that there basically should be
+just one type. And all instances of that type ought to work the same way.
+Thus, you can create a protected interface simply by adding the word
+'protected' to an existing one, and no code changes will be needed (other
+than to change the name). This is precisely the model that you are always
+talking about to allow interoperation of abstract types and interface types.
+In that case, I think it is rare that it actually will work, but in this
+case, it should always work - unchanged.
+
+In an ideal world, there would be no difference between the kinds of
+interfaces at all. Limited interfaces could have entries, and they'd match
+entries. (Obviously, the concrete type would have to be task or protected in
+that case.) If there are no entries, any of the items could be a concrete
+type (with procedures matching entries as noted above).
+
+Note that by adding prefix calls, we allow these subprograms to be called in
+either notation, so there is no notational reason for treating them
+differently. A protected subprogram call could still look natural.
+
+The compiler would generate a wrapper if needed to make the calls
+consistent. This is very similar to what's done for renames and formal
+subprograms (as you noted).
+
+Of course, the world is not ideal. Entry calls cause problems, because the
+implementation model for tasks and protected objects is probably very
+different. Mandating that there exist some sort of call that works on both
+might be a burden on implementations.
+
+I presumed that (and *only* that) is why you retained task and protected
+interfaces - so you could keep wrapperless entry calls straight. That way,
+entries are only allowed in task and protected interfaces, and you know how
+to call them.
+
+Anyway, it seems to me that there is another solution to the problem of
+entries. Protected types really don't have any interesting operations from
+outside of themselves other than entries. That's different than tasks.
+Moreover, the original problem is the extensibility of protected types. No
+one was asking for extensible tasks.
+
+So adding tasks to the equation makes it much hard to create an integrated
+solution. Thus, I propose dropping task interfaces altogether.
+
+Then, we simply allow limited interfaces to have entries. (In which case,
+the concrete type must be a protected object.) The compiler will need to
+make all of the subprogram calls have the right convention for the
+interface, but that's not difficult (if you can rename a protected procedure
+as a normal one, you can build the appropriate wrapper).
+
+The matching rules for protected objects would be as I stated before:
+subtype conformance with the profile with the PO added as the first
+parameter of the profile (mode in out for procedures, mode in for
+functions). And family-less entries match procedures.
+
+That would mean that if you are programming through interfaces, you do not
+even need to know if the actual implementation is a protected type or just a
+tagged type. You don't have to write any wrapper routines by hand. And if
+you need more powerful wrappers, you can certainly write them.
+
+This is our last chance to integrate protected types into the rest of the
+language. If we adopt separate-but-equal (which is essentially Tucker's
+proposal), we're not going to have enough leaway to ever fix it. (At least
+without major pain.)
+
+Sorry about the stream-of-consciousness, it's too much to go back and
+rewrite it all to match the final conclusion.
+
+****************************************************************
+
+From: Tucker Taft
+Sent: Thursday, January 22, 2004   9:51 PM
+
+I'll have to think a while about Randy's response.
+So don't expect a quick answer...
+
+****************************************************************
+
+From: Randy Brukardt
+Sent: Thursday, January 22, 2004  10:15 PM
+
+I think I'd rather have a *good* response, rather than a *quick* one. I'm
+sure there is something wrong with my idea (there always is :-), and we want
+to know what it is before we adopt it...
+
+****************************************************************
+

Questions? Ask the ACAA Technical Agent