!standard 13.13.1(3/2) 10-02-13 AC95-00190/01 !class Amendment 10-02-13 !status received no action 10-02-13 !status received 10-01-15 !subject Change type of Root_Stream_Type to interface !summary !appendix From: Vadim Godunko Date: Friday, January 15, 2010 1:04 PM It would be nice to declare Ada.Streams.Root_Stream_Type as type Root_Stream_Type is limited interface; to allows to support stream operations by objects with others root type. **************************************************************** From: Dmitry A. Kazakov Date: Friday, January 15, 2010 2:55 PM Also type Root_Storage_Pool is limited interface; type Controlled is interface; type Limited_Controlled is limited interface; **************************************************************** From: Pascal Leroy Date: Sunday, January 17, 2010 4:51 AM This was considered during the Ada 2005 design, but it's not possible because it would introduce horrendous incompatibilities due to the "no hidden interfaces" rule (RM95 7.3(7.3/2)). **************************************************************** From: Bob Duff Date: Sunday, January 17, 2010 9:00 AM Right. It is quite common to inherit privately from [Limited_]Controlled. Root_Storage_Pool and Root_Stream_Type, not so much, but it's still an incompatibility. Also, for [Limited_]Controlled, there would be an implementation burden, because some implementations declare these types to have private components (e.g. links to chain controlled objects together). GNAT currently does that, although the plan is to eliminate that someday. And Root_Storage_Pool is derived from Limited_Controlled. Root_Stream_Type is likely just a "tagged limited null record". **************************************************************** From: Vadim Godunko Date: Sunday, January 17, 2010 11:11 AM > This was considered during the Ada 2005 design, but it's not possible > because it would introduce horrendous incompatibilities due to the "no > hidden interfaces" rule (RM95 7.3(7.3/2)). Can you please provide such an example in context of Root_Stream_Type? **************************************************************** From: Vadim Godunko Date: Sunday, January 17, 2010 11:23 AM So, it is good candidate to be declared as interface. In real life it definitely simplify things, for example: package IO_Devices is type IO_Device is abstract limited new Root_Stream_Type with private; private type IO_Device is abstract new Limited_Controlled and Root_Stream_Type with SOME record; end IO_Devices; package Files is type File is new IO_Device with private; end Files; package Sockets is type Socket is new IO_Device with private; end Sockets; Of course, the same effect can be achieved by adding operation to IO_Device which returns access to Root_Stream_Type, but from the current state of OOP art it looks not very good (and don't forgot about potential dangling pointer). **************************************************************** From: Dmitry A. Kazakov Date: Sunday, January 17, 2010 1:26 PM ... > package IO_Devices is > > type IO_Device is > abstract limited new Root_Stream_Type with private; Device is almost always will be a descendant of Limited_Controlled. At least it is so in our design. > Of course, the same effect can be achieved by adding operation to > IO_Device which returns access to Root_Stream_Type, Yes, this is how we resolve it, because controlledness is more important than stream interface. Similarly it goes for the task each device normally encapsulates (to which I/O requests get queued). Unfortunately the language makes it is impossible to have a device, which is a task, a stream and controlled at the same time. > but from the current > state of OOP art it looks not very good (and don't forgot about > potential dangling pointer). In our case the result is an anonymous not-null-access type, which reduces potential damage to some extent. **************************************************************** From: Bob Duff Date: Sunday, January 17, 2010 11:30 AM The following is legal Ada. It would be illegal if Root_Stream_Type were an interface. I do not claim that this sort of thing is likely to be common in real code; usually if you want to derive from Root_Stream_Type, you want to do so visibly. But who knows? Maybe the stream-ness of type T only needs to be visible in children of P. Is the benefit of making Root_Stream_Type an interface worth the possible (unknown) cost of incompatibility? I don't know. with Ada.Streams; use Ada.Streams; package P is type T (<>) is limited private; function F return T; private type T is new Root_Stream_Type with null record; procedure Read (Stream : in out T; Item : out Stream_Element_Array; Last : out Stream_Element_Offset); procedure Write (Stream : in out T; Item : Stream_Element_Array); end P; package body P is function F return T is begin return (Root_Stream_Type with null record); end F; procedure Read (Stream : in out T; Item : out Stream_Element_Array; Last : out Stream_Element_Offset) is begin null; end Read; procedure Write (Stream : in out T; Item : Stream_Element_Array) is begin null; end Write; end P; **************************************************************** From: Bob Duff Date: Sunday, January 17, 2010 4:00 PM > So, it is good candidate to be declared as interface. In real life it > definitely simplify things,... I agree it would be better as an interface. I guess that's true in general -- any type that is "null record" and has only 'abstract' and 'null' procedures, would be more powerful as an interface. If Ada 95 had interfaces, I suppose we would have done it that way. The only question is, "Is it worth the incompatibility?" ... > Of course, the same effect can be achieved by adding operation to > IO_Device which returns access to Root_Stream_Type, but from the > current state of OOP art it looks not very good (and don't forgot > about potential dangling pointer). Or, in this example, you could add a controlled component to File and Socket. You might not want the expense of controlled types for ALL IO_Devices -- for example the null device (like /dev/null or "the bit bucket") doesn't need any finalization. But I agree there's some benefit to making Root_Stream_Type an interface. And some unknown but small-ish cost. For [Limited_]Controlled, no way! **************************************************************** From: Dmitry A. Kazakov Date: Sunday, January 17, 2010 3:14 PM > Also, for [Limited_]Controlled, there would be an implementation > burden, because some implementations declare these types to have > private components (e.g. links to chain controlled objects together). You can add them at the point where a type starts to implement the "interface". It is clear that neither of them are interfaces, they are abstract types with certain implementation wired up by the compiler. The motivation to make them "interfaces" is to achieve MI, where it is otherwise forbidden. **************************************************************** From: Cyrille Comar Date: Sunday, January 17, 2010 2:50 PM > The following is legal Ada. It would be illegal if Root_Stream_Type > were an interface. rather than transforming Root_Stream_Type into an interface, can't we add a new predefined interface called "Streams" (since it is not really a Root nor a type) that would have the expected similar semantics? That is not pretty in term of language design but it provides the needed feature and does not create incompatibility... (same thing for Controlled and Limited_Controlled) **************************************************************** From: Bob Duff Date: Monday, January 18, 2010 2:36 PM > rather than transforming Root_Stream_Type into an interface, can't we > add a new predefined interface called "Streams" (since it is not > really a Root nor a type) that would have the expected similar semantics? > That is not pretty in term of language design but it provides the needed > feature and does not create incompatibility... Yes, I think that would work. So Root_Stream_Type would be derived from this new interface. > (same thing for Controlled and Limited_Controlled) That would work too, from a compatibility point of view, but I still think there's an implementation issue. Certainly it's possible, as Dmitry Kazakov said: > > Also, for [Limited_]Controlled, there would be an implementation > > burden, because some implementations declare these types to have > > private components (e.g. links to chain controlled objects together). > > You can add them at the point where a type starts to implement the > "interface". But not trivial. I wouldn't be surprised if this cost many person-months, for some implementations. **************************************************************** From: Vadim Godunko Date: Monday, January 18, 2010 2:41 PM > rather than transforming Root_Stream_Type into an interface, can't we > add a new predefined interface called "Streams" (since it is not > really a Root nor a type) that would have the expected similar semantics? > That is not pretty in term of language design but it provides the > needed feature and does not create incompatibility... Doesn't help as pointed by Pascal **************************************************************** From: Bob Duff Date: Monday, January 18, 2010 4:12 PM > Doesn't help as pointed by Pascal He did? The only message I got from Pascal on this subject was the following: > This was considered during the Ada 2005 design, but it's not possible > because it would introduce horrendous incompatibilities due to the "no > hidden interfaces" rule (RM95 7.3(7.3/2)). Did I miss something? Above, Pascal is talking about this: type Limited_Controlled is limited interface; which would be severely incompatible. Cyrille is talking about something different: type Limited_Controlled_Interface is limited interface; type Limited_Controlled is new Limited_Controlled_Interface with private; This would be compatible, except for the minor issue of polluting the namespace with "Limited_Controlled_Interface", and even that could be eliminated by putting it in a different package. **************************************************************** From: Randy Brukardt Date: Monday, January 18, 2010 5:39 PM ... > Cyrille is talking about something different: > > type Limited_Controlled_Interface is limited interface; > type Limited_Controlled is new > Limited_Controlled_Interface with private; > > This would be compatible, except for the minor issue of polluting the > namespace with "Limited_Controlled_Interface", and even that could be > eliminated by putting it in a different package. I think Vadim is correct; the above has the same incompatibility problem as the original idea (you'd still be hiding the Limited_Controlled_Interface, and that is illegal whether or not it is directly declared). After all, the partial view would still fail the test (it would not be a descendent of Limited_Controlled_Interface, as required by 7.3(7.3/2)). The only way to make it OK would be for them to be totally unrelated: type Limited_Controlled_Interface is limited interface; type Limited_Controlled is abstract tagged limited private; Which is pretty weird. Moreover, I would still object: implementing hidden components for this one case of interfaces would be as costly as adding them for all types. If we are willing to do that, there would have been no reason to not implement full multiple inheritance, and we would not need interfaces at all. (After all, the major difference between abstract types and interfaces is the ability to have components.) There are still many of us that are unconvinced that multiple inheritance is really worth the extreme implementation costs (interfaces is one feature that you don't have to worry about appearing in Janus/Ada anytime soon), so I think there would be a lot of opposition to extending it even further. Anyway, I wouldn't be as adverse to making this change for streams or storage pools, but the incompatibility is definitely scary. Moreover, in the case of streams, it doesn't help that much. The problem is that you still can only have one kind of stream, but it makes sense to have multiple kinds (binary, XML, portable binary, etc.). The *real* problem isn't streams, but rather the fact that you can't get the composition benefits of streams in code that you write yourself. I'm not sure that there is a solution (we've tried to look for such solutions in the past, but it still is annoying that there is compiler magic here (and also for equality) that you can't use in your own abstractions. **************************************************************** From: Bob Duff Date: Monday, January 18, 2010 6:29 PM > I think Vadim is correct; the above has the same incompatibility > problem as the original idea (you'd still be hiding the > Limited_Controlled_Interface, and that is illegal whether or not it is > directly declared). After all, the partial view would still fail the > test (it would not be a descendent of Limited_Controlled_Interface, as > required by 7.3(7.3/2)). OK, I see. **************************************************************** Editor's note, February 13, 2010. In addition to the concerns above, there is also the problem that implementations supporting multiple Ada versions (Ada 95, Ada 2005, Ada 2012) via a compiler switch (which is most of them), changing the kind of a predefined type is likely to be a lot harder than simply changing the values of entities or simply ignoring some declarations. That clearly would increase the implementation burden. ****************************************************************