* Class-wide types algebra @ 2016-09-12 20:26 Dmitry A. Kazakov 2016-09-12 21:12 ` rieachus 0 siblings, 1 reply; 14+ messages in thread From: Dmitry A. Kazakov @ 2016-09-12 20:26 UTC (permalink / raw) Presently class-wide types are singletons. I would propose to extend that to call-wide expressions, namely conjunction in all places where T'Class is expected. e.g. procedure Foo (X : in out T'Class and R'Class); -- Not Ada task type Worker (X : not null access T'Class and R'Class) is if X is in (T'Class and R'Class) then Object : T'Class and R'Class renames (T'Class and R'Class) (X); It should not be difficult, at first glance. Rationale. Presently it is impossible to declare an operation requiring an instance derived from the type T that also implements interface R. We cannot do that without having a explicit root type type T_And_R is new T and R with ...; But that will exclude existing types already implementing T. If we already have: type S is new T with ...; A type derived from S with R added would not fall into T_And_R. Generics do not help much because descendants of T may have discriminants which are all unknown in advance, so there is no chance to create instances derived types. -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: Class-wide types algebra 2016-09-12 20:26 Class-wide types algebra Dmitry A. Kazakov @ 2016-09-12 21:12 ` rieachus 2016-09-12 21:39 ` Dmitry A. Kazakov 0 siblings, 1 reply; 14+ messages in thread From: rieachus @ 2016-09-12 21:12 UTC (permalink / raw) On Monday, September 12, 2016 at 4:26:24 PM UTC-4, Dmitry A. Kazakov wrote: > Presently class-wide types are singletons. I would propose to extend > that to call-wide expressions, namely conjunction in all places where > T'Class is expected. e.g. > The problem may be worth solving, but I dread the number of cases that would have to be considered to come up with workable rules. A more restricted case, which should deal with the issue would be to allow "T'Class is in R'Class;" where R is an interface, or an ancestor of T'Class. Why the second case? The real issue seems to be visibility. You have an interface, and a type which could/should be a member, but the declaration is more deeply nested. (There would still need to be some magic so that the lifetime of T'Class is the same as R'Class. Or better, just a rule that requires them to have the same lifetime from other rules.) ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: Class-wide types algebra 2016-09-12 21:12 ` rieachus @ 2016-09-12 21:39 ` Dmitry A. Kazakov 2016-09-13 11:46 ` rieachus 0 siblings, 1 reply; 14+ messages in thread From: Dmitry A. Kazakov @ 2016-09-12 21:39 UTC (permalink / raw) On 2016-09-12 23:12, rieachus@comcast.net wrote: > On Monday, September 12, 2016 at 4:26:24 PM UTC-4, Dmitry A. Kazakov wrote: >> Presently class-wide types are singletons. I would propose to extend >> that to call-wide expressions, namely conjunction in all places where >> T'Class is expected. e.g. >> > A more > restricted case, which should deal with the issue would be to allow > "T'Class is in R'Class;" where R is an interface, or an ancestor of T'Class. I am not sure what you mean. Ada's "in" has the mathematical meaning "member of" (∈). If you meant "subset of"/"subsumption" (⊆) then it is just same as T in R'Class It is already there. It would be nice to have comparisons like: T'Class <= R'Class <=> T in R'Class But that is not the problem. > Why the second case? The real issue seems to be visibility. You have > an interface, and a type which could/should be a member, but the > declaration is more deeply nested. (There would still need to be some > magic so that the lifetime of T'Class is the same as R'Class. Or better, > just a rule that requires them to have the same lifetime from other rules.) I don't see any problems with that. All types in question are statically known. Type tests are expressible already X in (T'Class and R'Class) <=> (X in T'Class) and (X in R'Class) The real problem is declarations not tests. -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: Class-wide types algebra 2016-09-12 21:39 ` Dmitry A. Kazakov @ 2016-09-13 11:46 ` rieachus 2016-09-13 12:26 ` Dmitry A. Kazakov 0 siblings, 1 reply; 14+ messages in thread From: rieachus @ 2016-09-13 11:46 UTC (permalink / raw) On Monday, September 12, 2016 at 5:39:32 PM UTC-4, Dmitry A. Kazakov wrote: > On 2016-09-12 23:12, rieachus@comcast.net wrote: > > I am not sure what you mean. Ada's "in" has the mathematical meaning > "member of" (∈). If you meant "subset of"/"subsumption" (⊆) then it is > just same as > > T in R'Class I was using "in" as syntactic sugar, extending R'Class without creating a new (named) subclass. As unrelated to the Boolean version as to its use in subprogram declarations. > > Why the second case? The real issue seems to be visibility. You have > > an interface, and a type which could/should be a member, but the > > declaration is more deeply nested. (There would still need to be some > > magic so that the lifetime of T'Class is the same as R'Class. Or better, > > just a rule that requires them to have the same lifetime from other rules.) > > I don't see any problems with that. All types in question are statically > known. Type tests are expressible already > > X in (T'Class and R'Class) <=> (X in T'Class) and (X in R'Class) > > The real problem is declarations not tests. IMHO the biggest problem in Ada is the namespace pollution of types. Dotted notation is acceptable when package names hide each other, but I hate having to write "subtype Foo is Bar.Foo." Subprograms can be overloaded, but adding a (new) type name during maintenance can break something down a chain of derivations. A long time ago, Lori Clarke and others wrote: "Nesting in Ada is for the Birds." http://dl.acm.org/citation.cfm?id=948651 There are a few missing pieces when you go to mix class wide types into Ada. I've started numbering the often never mentioned again types that appear when using interfaces to do mix-ins. ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: Class-wide types algebra 2016-09-13 11:46 ` rieachus @ 2016-09-13 12:26 ` Dmitry A. Kazakov 2016-09-28 0:05 ` Randy Brukardt 0 siblings, 1 reply; 14+ messages in thread From: Dmitry A. Kazakov @ 2016-09-13 12:26 UTC (permalink / raw) On 13/09/2016 13:46, rieachus@comcast.net wrote: > IMHO the biggest problem in Ada is the namespace pollution of types. That is not a problem either because resulted types are anonymous. > Dotted notation is acceptable when package names hide each other, > but I hate having to write "subtype Foo is Bar.Foo." Well that is another language problem that types cannot orderly be renamed. But I don't see how it is related to class-wide types. And type renaming is only a problem in generics which are beyond any hope anyway. > Subprograms can be > overloaded, but adding a (new) type name during maintenance can break > something down a chain of derivations. No, it cannot break anything, it is just like "access T", T'Base etc. > A long time ago, Lori Clarke and others wrote: "Nesting in Ada is > for the Birds." http://dl.acm.org/citation.cfm?id=948651 There are a few > missing pieces when you go to mix class wide types into Ada. I've > started numbering the often never mentioned again types that appear when > using interfaces to do mix-ins. Yes, I have long proposed to introduce T'Interface to denote the type interface stripped of implementation. That will spare you many explicit interface declarations and fix language library problems. E.g. type Root_Stream_Type is ... OK, that was a mistake, it should have been type Root_Stream_Interface is limited interface; type Root_Stream_Type is abstract new Root_Stream_Interface with private; With T'Interface you could still work it around: type DB_Stream is new Data_Base_Connector -- I need my base type here and Root_Stream_Type'Interfrace; -- But it is a stream still [Of course T'Interface will override abstract all non-abstract not null primitive operations inherited by T] Of course: T'Immediate_Parent to denote the parent type. T'Element to denote array element type T'Index to denote array index type T'Member(Name/Position) to denote record member type T'Constraints to denote a null record type with constraints of T T'Target to denote the pointer's target type Nothing of this would be any difficult, but it would make usage of generics a lot easier. -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: Class-wide types algebra 2016-09-13 12:26 ` Dmitry A. Kazakov @ 2016-09-28 0:05 ` Randy Brukardt 2016-09-28 7:31 ` Dmitry A. Kazakov 0 siblings, 1 reply; 14+ messages in thread From: Randy Brukardt @ 2016-09-28 0:05 UTC (permalink / raw) "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message news:nr8rah$14gp$1@gioia.aioe.org... > ... E.g. > > type Root_Stream_Type is ... > > OK, that was a mistake, it should have been > > type Root_Stream_Interface is limited interface; > type Root_Stream_Type is abstract new Root_Stream_Interface > with private; We would have made the above change years ago if it wasn't severely incompatible. > With T'Interface you could still work it around: But you'd still have the problem that interfaces can't be hidden (which is the source of the incompatibility). And lifting that restriction would be a nightmare (we've tried on several occasions), as you quickly get scenarios where you have to have two different copies of the same interface in order to keep sanity about the operations. (Else one has to totally abandon privacy, allowing hidden operations to be overridden. That leads to usage madness...) The problem with all forms of MI is diamond inheritance, which get much worse in Ada because of our strict privacy rules. I don't think that could ever be solved for Ada (perhaps a new language could solve it, but not Ada). Randy. ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: Class-wide types algebra 2016-09-28 0:05 ` Randy Brukardt @ 2016-09-28 7:31 ` Dmitry A. Kazakov 2016-09-28 20:17 ` Randy Brukardt 0 siblings, 1 reply; 14+ messages in thread From: Dmitry A. Kazakov @ 2016-09-28 7:31 UTC (permalink / raw) On 28/09/2016 02:05, Randy Brukardt wrote: > "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message > news:nr8rah$14gp$1@gioia.aioe.org... >> ... E.g. >> >> type Root_Stream_Type is ... >> >> OK, that was a mistake, it should have been >> >> type Root_Stream_Interface is limited interface; >> type Root_Stream_Type is abstract new Root_Stream_Interface >> with private; > > We would have made the above change years ago if it wasn't severely > incompatible. > >> With T'Interface you could still work it around: > > But you'd still have the problem that interfaces can't be hidden (which is > the source of the incompatibility). And lifting that restriction would be a > nightmare (we've tried on several occasions), as you quickly get scenarios > where you have to have two different copies of the same interface in order > to keep sanity about the operations. (Else one has to totally abandon > privacy, allowing hidden operations to be overridden. That leads to usage > madness...) That is largely because interfaces were introduced as named types, which was an error as well as introducing them at all. T'Interface would be an anonymous type like T'Class is. There is no danger of having its copies because it is a by-structure thing and all copies are same. > The problem with all forms of MI is diamond inheritance, which get much > worse in Ada because of our strict privacy rules. There is no any problems with diamond inheritance and visibility. > I don't think that could > ever be solved for Ada (perhaps a new language could solve it, but not Ada). It can easily be solved once understood that inheritance may produce conflicts to be resolved by the programmer, not by the language, which is indeed impossible. Use-clauses, nesting, generic formals have *exactly* same issues there is absolutely nothing special in inheritance. -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: Class-wide types algebra 2016-09-28 7:31 ` Dmitry A. Kazakov @ 2016-09-28 20:17 ` Randy Brukardt 2016-09-29 8:06 ` Dmitry A. Kazakov 0 siblings, 1 reply; 14+ messages in thread From: Randy Brukardt @ 2016-09-28 20:17 UTC (permalink / raw) "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message news:nsfrn6$ab7$1@gioia.aioe.org... > On 28/09/2016 02:05, Randy Brukardt wrote: ... >> The problem with all forms of MI is diamond inheritance, which get much >> worse in Ada because of our strict privacy rules. > > There is no any problems with diamond inheritance and visibility. Certainly are. If one has a hidden interface with hidden overridden operations, and a later extension tries to add the same interface with different overridden operations, one of two things has to happen, both bad: (1) The interfaces are the same, so the hidden operations completely disappear. (The new extension cannot know that they ever existed, so they cannot be called from it.) Since the parent operation presumably depends on those operations, trouble is certain to ensue. (When the operations aren't overridden, then they magically become visible, which is also a problem.) (2) The interfaces are different, so one has problems in dispatching calls of figuring out which one to call. That way leads to madness. The problem here is that only (2) respects privacy at all; (1) eliminates it no matter how the conflict is resolved (the fact that the parent has a hidden interface changes the semantics of the extension even though the extension is not supposed to know or care about the interface). >> I don't think that could >> ever be solved for Ada (perhaps a new language could solve it, but not >> Ada). > > It can easily be solved once understood that inheritance may produce > conflicts to be resolved by the programmer, not by the language, which is > indeed impossible. The programmer can't resolve the conflicts unless privacy is ignored, because the programmer has no knowledge that the hidden interface is used. So why shouldn't they be able to use the same interface?? Any answer breaks privacy badly. Remember, the case in question is an extension from a private view, for which no knowledge about the implementation should escape. > Use-clauses, nesting, generic formals have *exactly* same issues there is > absolutely nothing special in inheritance. And all of those are also evil. :-) They introduce significant maintenance problems all of the time. And they don't have the problem of requiring visibility on private things to resolve the conflicts. (For all of the above, private things stay private.) Randy. ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: Class-wide types algebra 2016-09-28 20:17 ` Randy Brukardt @ 2016-09-29 8:06 ` Dmitry A. Kazakov 2016-09-29 18:44 ` Randy Brukardt 0 siblings, 1 reply; 14+ messages in thread From: Dmitry A. Kazakov @ 2016-09-29 8:06 UTC (permalink / raw) On 28/09/2016 22:17, Randy Brukardt wrote: > "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message > news:nsfrn6$ab7$1@gioia.aioe.org... >> On 28/09/2016 02:05, Randy Brukardt wrote: > ... >>> The problem with all forms of MI is diamond inheritance, which get much >>> worse in Ada because of our strict privacy rules. >> >> There is no any problems with diamond inheritance and visibility. > > Certainly are. If one has a hidden interface with hidden overridden > operations, and a later extension tries to add the same interface with > different overridden operations, one of two things has to happen, both bad: > (1) The interfaces are the same, so the hidden operations completely > disappear. (The new extension cannot know that they ever existed, so they > cannot be called from it.) Since the parent operation presumably depends on > those operations, trouble is certain to ensue. (When the operations aren't > overridden, then they magically become visible, which is also a problem.) > (2) The interfaces are different, so one has problems in dispatching calls > of figuring out which one to call. That way leads to madness. It is clearly #2. What you cannot see does not exist, the old good solipsism. Ada sometimes violates this principle, always for worse. There is no problem with dispatching calls so long visibility is respected: 1. Where type's primitive operations are not visible you cannot dispatch to them. 2. Where type's primitive operations are visible you cannot derive without resolving the conflict between hierarchies. From this follows that you cannot use the type in the context where hidden operations are visible! The type with an unresolved conflict (and its class-type) is invalid. Example: package P1 is type I is interface; procedure Foo (X : I) is abstract; type T1 is new T with ...; private type T1 is new T and I with ...; -- Private interface end P1; package P2 is -- We don't see private I type T2 is new T1 and I with null record; -- This is OK! end P2; package P1.Child_1 is -- We see private I type T2 is new T1 and I with null record; -- This is NOT OK end P1.Child_1; procedure P1.Child_2 is X : P2.T2; -- This is NOT OK, we see two instances of I in X But of course it is also possible to allow X as we do with other cases when operations get hidden by each other. In that scenario X.Foo will be rejected as ambiguous. Yes, when X were passed as a class-wide of another root type dispatching to Foo would take the hierarchy of I that this root type uses. It is all consistent. Y : T1'Class := ...; -- This OK, we see only private I begin Y.Foo; -- This dispatches on I inherited in P1, WYSIWYG procedure Public is X : P2.T2; -- This is OK, we respect privateness begin X.Foo; -- This dispatches on I inherited in P2 The important point is that inheritance can be additive as in the case when I is inherited once privately and once publicly. If succeeds then the outcome is *two* hierarchies. You cannot forbid that altogether, which is why you see it as a problem. It is not a problem, just let it be. We would also allow P1.Child_1 when the programmer states that I is inherited additively. The use case for additive inheritance is multiple linked-lists: type Node is limited interface; function Next (X : Node) return Node'Class; ... type IO_Queue is new Node and Node with ...; Each item participates in two lists, the list of requests and the list of requests from the same task (i.e. to abort them when the task completes). > The problem here is that only (2) respects privacy at all; Right. The user must resolve conflict when it appears. E.g. type IO_Queue is new Node as Request_List and Node as Task_List with ...; function Next_IO_Request ... renames Request_List.Next; function Next_Task_Request ... renames Task_List.Next; I leave syntax to imagination. >> Use-clauses, nesting, generic formals have *exactly* same issues there is >> absolutely nothing special in inheritance. > > And all of those are also evil. :-) Yes, the world is a sad place. (:-)) > They introduce significant maintenance > problems all of the time. And they don't have the problem of requiring > visibility on private things to resolve the conflicts. (For all of the > above, private things stay private.) True, and we could do same for inheritance. -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: Class-wide types algebra 2016-09-29 8:06 ` Dmitry A. Kazakov @ 2016-09-29 18:44 ` Randy Brukardt 2016-09-29 19:55 ` Dmitry A. Kazakov 0 siblings, 1 reply; 14+ messages in thread From: Randy Brukardt @ 2016-09-29 18:44 UTC (permalink / raw) "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message news:nsii37$fl3$1@gioia.aioe.org... > On 28/09/2016 22:17, Randy Brukardt wrote: >> "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message >> news:nsfrn6$ab7$1@gioia.aioe.org... >>> On 28/09/2016 02:05, Randy Brukardt wrote: >> ... >>>> The problem with all forms of MI is diamond inheritance, which get much >>>> worse in Ada because of our strict privacy rules. >>> >>> There is no any problems with diamond inheritance and visibility. >> >> Certainly are. If one has a hidden interface with hidden overridden >> operations, and a later extension tries to add the same interface with >> different overridden operations, one of two things has to happen, both >> bad: >> (1) The interfaces are the same, so the hidden operations completely >> disappear. (The new extension cannot know that they ever existed, so they >> cannot be called from it.) Since the parent operation presumably depends >> on >> those operations, trouble is certain to ensue. (When the operations >> aren't >> overridden, then they magically become visible, which is also a problem.) >> (2) The interfaces are different, so one has problems in dispatching >> calls >> of figuring out which one to call. That way leads to madness. > > It is clearly #2. What you cannot see does not exist, the old good > solipsism. Ada sometimes violates this principle, always for worse. > > There is no problem with dispatching calls so long visibility is > respected: > > 1. Where type's primitive operations are not visible you cannot dispatch > to them. ??? You can always convert an object of the type to the class-wide interface (which is legal in this scenario, even if not in the private part), and make a dispatching call. (That's the case that's a problem.). Which overriding operation do you get? If it depends on visibility, you have madness (what happens depends on where it is written - yuck). If it doesn't depend on visibility, the implementation of the original private type is going to end up with the new operations, even though those were not overridden (which is likely to break something - essentially you'd be getting the wrong copy of the interface). That of course could occur implicitly inside of a class-wide interface operation called by the private implementation (which is the reason for using interfaces in the first place), so it's a real issue. > 2. Where type's primitive operations are visible you cannot derive without > resolving the conflict between hierarchies. Sure. But that doesn't fix anything. > From this follows that you cannot use the type in the context where hidden > operations are visible! No problem, but the problem is with the type with two copies of the interface. How does the conversion to I'Class work (that conversion is implicit in calls to class-wide subprograms)? How does it chose which interface to use? The correct answer depends on what the programmer intended, and that something a compiler (or programming language) can't know. ... > Yes, when X were passed as a class-wide of another root type dispatching > to Foo would take the hierarchy of I that this root type uses. It is all > consistent. > > Y : T1'Class := ...; -- This OK, we see only private I > begin > Y.Foo; -- This dispatches on I inherited in P1, WYSIWYG > > procedure Public is > X : P2.T2; -- This is OK, we respect privateness > begin > X.Foo; -- This dispatches on I inherited in P2 Actually, this is madness. Nothing "consistent" about it. Why? Because if you move the call to some outside routine (say some other dispatching operation), the meaning changes. Which means that one can't use abstractions. Very, very bad. > The important point is that inheritance can be additive as in the case > when I is inherited once privately and once publicly. If succeeds then the > outcome is *two* hierarchies. > > You cannot forbid that altogether, which is why you see it as a problem. Well, actually we do: we forbid the private inheritance completely, because there is no consistent and sensible set of rules for what it would mean. > It is not a problem, just let it be. The implementation of two copies of the same interface would break any of the existing algorithms for implementing interfaces. (I have absolutely no idea how that could be accomplished; it would depend on how the dispatching rules were eventually worked out.) ... >> They introduce significant maintenance >> problems all of the time. And they don't have the problem of requiring >> visibility on private things to resolve the conflicts. (For all of the >> above, private things stay private.) > > True, and we could do same for inheritance. Doesn't help, as noted above. Randy. ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: Class-wide types algebra 2016-09-29 18:44 ` Randy Brukardt @ 2016-09-29 19:55 ` Dmitry A. Kazakov 2016-10-01 5:47 ` Randy Brukardt 0 siblings, 1 reply; 14+ messages in thread From: Dmitry A. Kazakov @ 2016-09-29 19:55 UTC (permalink / raw) On 2016-09-29 20:44, Randy Brukardt wrote: > "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message > news:nsii37$fl3$1@gioia.aioe.org... >> On 28/09/2016 22:17, Randy Brukardt wrote: >>> "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message >>> news:nsfrn6$ab7$1@gioia.aioe.org... >>>> On 28/09/2016 02:05, Randy Brukardt wrote: >>> ... >>>>> The problem with all forms of MI is diamond inheritance, which get much >>>>> worse in Ada because of our strict privacy rules. >>>> >>>> There is no any problems with diamond inheritance and visibility. >>> >>> Certainly are. If one has a hidden interface with hidden overridden >>> operations, and a later extension tries to add the same interface with >>> different overridden operations, one of two things has to happen, both >>> bad: >>> (1) The interfaces are the same, so the hidden operations completely >>> disappear. (The new extension cannot know that they ever existed, so they >>> cannot be called from it.) Since the parent operation presumably depends >>> on >>> those operations, trouble is certain to ensue. (When the operations >>> aren't >>> overridden, then they magically become visible, which is also a problem.) >>> (2) The interfaces are different, so one has problems in dispatching >>> calls >>> of figuring out which one to call. That way leads to madness. >> >> It is clearly #2. What you cannot see does not exist, the old good >> solipsism. Ada sometimes violates this principle, always for worse. >> >> There is no problem with dispatching calls so long visibility is >> respected: >> >> 1. Where type's primitive operations are not visible you cannot dispatch >> to them. > > ??? You can always convert an object of the type to the class-wide interface > (which is legal in this scenario, even if not in the private part), and make > a dispatching call. (That's the case that's a problem.). No, you cannot, you don't see if the type inherits the interface, so you cannot convert to its class. > Which overriding operation do you get? None, that is illegal, the type does not implement the interface inherited privately. There is no overriding, there is no class in any public scope. > If it depends on visibility, you have madness (what > happens depends on where it is written - yuck). Nope, that is what visibility all about. You don't see inheritance, you don't have it. Period. >> 2. Where type's primitive operations are visible you cannot derive without >> resolving the conflict between hierarchies. > > Sure. But that doesn't fix anything. > >> From this follows that you cannot use the type in the context where hidden >> operations are visible! > > No problem, but the problem is with the type with two copies of the > interface. How does the conversion to I'Class work (that conversion is > implicit in calls to class-wide subprograms)? It will work just fine. In any context only one inheritance is visible or else conversion is ambiguous and thus illegal (unless resolved). > How does it chose which interface to use? The one visible in this context. There is no unresolved conflicts, in any given context it is unambiguous. > ... >> Yes, when X were passed as a class-wide of another root type dispatching >> to Foo would take the hierarchy of I that this root type uses. It is all >> consistent. >> >> Y : T1'Class := ...; -- This OK, we see only private I >> begin >> Y.Foo; -- This dispatches on I inherited in P1, WYSIWYG >> >> procedure Public is >> X : P2.T2; -- This is OK, we respect privateness >> begin >> X.Foo; -- This dispatches on I inherited in P2 > > Actually, this is madness. Nothing "consistent" about it. Why? Because if > you move the call to some outside routine (say some other dispatching > operation), the meaning changes. Which means that one can't use > abstractions. Very, very bad. Nothing mad here. You change the type of X that changes the operation. Why X / Y must mean same for Integer X, Y and Float X, Y? How Foo is different from "/"? >> The important point is that inheritance can be additive as in the case >> when I is inherited once privately and once publicly. If succeeds then the >> outcome is *two* hierarchies. >> >> You cannot forbid that altogether, which is why you see it as a problem. > > Well, actually we do: we forbid the private inheritance completely, because > there is no consistent and sensible set of rules for what it would mean. By violating privacy, by introducing ugly artifacts, by piling up nonsense rules... >> It is not a problem, just let it be. > > The implementation of two copies of the same interface would break any of > the existing algorithms for implementing interfaces. (I have absolutely no > idea how that could be accomplished; it would depend on how the dispatching > rules were eventually worked out.) It breaks nothing. You get two hierarchies independent on each other. You already can do just this with ease: generic package P is type I is interface; procedure Foo (X : I) is abstract; end P; package P1 is new P; package P2 is new P; type T is new P1.I and P2.I with null record; overriding procedure Foo (X : T); How is it different from: type T is new I and I with null record; Or this: generic type S is (<>); package P is type I is interface; procedure Foo (X : I; Y : S) is abstract; end P; type T is new P1.I and P2.I with null record; overriding procedure Foo (X : T; Y : Integer); overriding procedure Foo (X : T; Y : Boolean); You cannot prevent things like this once you introduced MI. There is only one way to do MI right and it is not the one Ada presently goes. -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: Class-wide types algebra 2016-09-29 19:55 ` Dmitry A. Kazakov @ 2016-10-01 5:47 ` Randy Brukardt 2016-10-01 8:35 ` Dmitry A. Kazakov 0 siblings, 1 reply; 14+ messages in thread From: Randy Brukardt @ 2016-10-01 5:47 UTC (permalink / raw) "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message news:nsjrjo$lg8$1@gioia.aioe.org... > On 2016-09-29 20:44, Randy Brukardt wrote: >> "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message >> news:nsii37$fl3$1@gioia.aioe.org... >>> On 28/09/2016 22:17, Randy Brukardt wrote: >>>> "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message >>>> news:nsfrn6$ab7$1@gioia.aioe.org... >>>>> On 28/09/2016 02:05, Randy Brukardt wrote: >>>> ... >>>>>> The problem with all forms of MI is diamond inheritance, which get >>>>>> much >>>>>> worse in Ada because of our strict privacy rules. >>>>> >>>>> There is no any problems with diamond inheritance and visibility. >>>> >>>> Certainly are. If one has a hidden interface with hidden overridden >>>> operations, and a later extension tries to add the same interface with >>>> different overridden operations, one of two things has to happen, both >>>> bad: >>>> (1) The interfaces are the same, so the hidden operations completely >>>> disappear. (The new extension cannot know that they ever existed, so >>>> they >>>> cannot be called from it.) Since the parent operation presumably >>>> depends >>>> on >>>> those operations, trouble is certain to ensue. (When the operations >>>> aren't >>>> overridden, then they magically become visible, which is also a >>>> problem.) >>>> (2) The interfaces are different, so one has problems in dispatching >>>> calls >>>> of figuring out which one to call. That way leads to madness. >>> >>> It is clearly #2. What you cannot see does not exist, the old good >>> solipsism. Ada sometimes violates this principle, always for worse. >>> >>> There is no problem with dispatching calls so long visibility is >>> respected: >>> >>> 1. Where type's primitive operations are not visible you cannot dispatch >>> to them. >> >> ??? You can always convert an object of the type to the class-wide >> interface >> (which is legal in this scenario, even if not in the private part), and >> make >> a dispatching call. (That's the case that's a problem.). > > No, you cannot, you don't see if the type inherits the interface, so you > cannot convert to its class. We're talking about the type that both publically and privately implements the same interface. (The private one inherited from the parent, the public one directly added.) And we're talking about a conversion that occurs where the private interface is visible (so *both* copies of the interface are visible). As I said: >> Which overriding operation do you get? > > None, that is illegal, the type does not implement the interface inherited > privately. There is no overriding, there is no class in any public scope. You're not thinking broadly enough about all of the possible combinations of public and private inheritance. It just doesn't work. Yes, the "normal" cases work, but we have to allow all possibilities (or we're breaking privacy). In particular, when you are in the private scope, you can see both interfaces. Whichever one you chose will be wrong about half of the time. Such a capability isn't going to help users much, as there will be innumable problems caused by it. ... >> How does it chose which interface to use? > > The one visible in this context. There is no unresolved conflicts, in any > given context it is unambiguous. ??? Both interfaces are visible in the private context. How could they not be both visible (they're distinct)? >> ... >>> Yes, when X were passed as a class-wide of another root type dispatching >>> to Foo would take the hierarchy of I that this root type uses. It is all >>> consistent. >>> >>> Y : T1'Class := ...; -- This OK, we see only private I >>> begin >>> Y.Foo; -- This dispatches on I inherited in P1, WYSIWYG >>> >>> procedure Public is >>> X : P2.T2; -- This is OK, we respect privateness >>> begin >>> X.Foo; -- This dispatches on I inherited in P2 >> >> Actually, this is madness. Nothing "consistent" about it. Why? Because if >> you move the call to some outside routine (say some other dispatching >> operation), the meaning changes. Which means that one can't use >> abstractions. Very, very bad. > > Nothing mad here. You change the type of X that changes the operation. Why > X / Y must mean same for Integer X, Y and Float X, Y? How Foo is different > from "/"? > >>> The important point is that inheritance can be additive as in the case >>> when I is inherited once privately and once publicly. If succeeds then >>> the >>> outcome is *two* hierarchies. >>> >>> You cannot forbid that altogether, which is why you see it as a problem. >> >> Well, actually we do: we forbid the private inheritance completely, >> because >> there is no consistent and sensible set of rules for what it would mean. > > By violating privacy, by introducing ugly artifacts, by piling up nonsense > rules... There's no violation of privacy. It's illegal to add an interface in a private part, but you can of course add the interface visibly. It was the best idea that we could come up with, and it had the advantage that if we ever could figure out some meaningful semantics that it would be compatible to allow private interfaces in the future. The worst possible thing is to allow something but get it wrong, because then one have to break all the existing usage to fix it (or worse, not fix it). >>> It is not a problem, just let it be. >> >> The implementation of two copies of the same interface would break any of >> the existing algorithms for implementing interfaces. (I have absolutely >> no >> idea how that could be accomplished; it would depend on how the >> dispatching >> rules were eventually worked out.) > > It breaks nothing. You get two hierarchies independent on each other. Sorry, but you don't have any idea how interface dispatching works. It's typically done with some sort of lookup table (since the typical solution of ensuring that all of the slots are the same in all of descendants does not work when multiple inheritance is allowed). That works because there is only one copy of each interface, so there is no need to worry about where they come from - one just needs a unique Id where they are declared. If there can be multiple copies, that doesn't work. And since these are runtime operations, there doesn't seem to be any way to choose between them -- runtime knows nothing of visibility. > You already can do just this with ease: > > generic > package P is > type I is interface; > procedure Foo (X : I) is abstract; > end P; > > package P1 is new P; > package P2 is new P; > > type T is new P1.I and P2.I with null record; > overriding procedure Foo (X : T); P1.I and P2.I are two different types. In the privacy case, you are getting two copies of the same type. Not remotely the same thing. (You can tell the difference by trying to call a global class-wide operation that has a parameter of the interface. For this example, that won't work for both interfaces -- one or the other isn't going to be allowed. For the private example, one could call that routine with either interface (because they have the same name) -- that's the crux of the problem. > How is it different from: > > type T is new I and I with null record; There is only one copy of the interface in this type. It doesn't matter how many times you mention it in the declaration or inherit it. But if that was applied to private types, it would be what I called case (1), and it would badly break privacy. Randy. ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: Class-wide types algebra 2016-10-01 5:47 ` Randy Brukardt @ 2016-10-01 8:35 ` Dmitry A. Kazakov 2016-10-05 20:42 ` Randy Brukardt 0 siblings, 1 reply; 14+ messages in thread From: Dmitry A. Kazakov @ 2016-10-01 8:35 UTC (permalink / raw) On 2016-10-01 07:47, Randy Brukardt wrote: > "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message > news:nsjrjo$lg8$1@gioia.aioe.org... >> On 2016-09-29 20:44, Randy Brukardt wrote: >>> "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message >>> news:nsii37$fl3$1@gioia.aioe.org... >>>> On 28/09/2016 22:17, Randy Brukardt wrote: >>>>> "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message >>>>> news:nsfrn6$ab7$1@gioia.aioe.org... >>>>>> On 28/09/2016 02:05, Randy Brukardt wrote: >>>>> ... >>>>>>> The problem with all forms of MI is diamond inheritance, which get >>>>>>> much >>>>>>> worse in Ada because of our strict privacy rules. >>>>>> >>>>>> There is no any problems with diamond inheritance and visibility. >>>>> >>>>> Certainly are. If one has a hidden interface with hidden overridden >>>>> operations, and a later extension tries to add the same interface with >>>>> different overridden operations, one of two things has to happen, both >>>>> bad: >>>>> (1) The interfaces are the same, so the hidden operations completely >>>>> disappear. (The new extension cannot know that they ever existed, so >>>>> they >>>>> cannot be called from it.) Since the parent operation presumably >>>>> depends >>>>> on >>>>> those operations, trouble is certain to ensue. (When the operations >>>>> aren't >>>>> overridden, then they magically become visible, which is also a >>>>> problem.) >>>>> (2) The interfaces are different, so one has problems in dispatching >>>>> calls >>>>> of figuring out which one to call. That way leads to madness. >>>> >>>> It is clearly #2. What you cannot see does not exist, the old good >>>> solipsism. Ada sometimes violates this principle, always for worse. >>>> >>>> There is no problem with dispatching calls so long visibility is >>>> respected: >>>> >>>> 1. Where type's primitive operations are not visible you cannot dispatch >>>> to them. >>> >>> ??? You can always convert an object of the type to the class-wide >>> interface >>> (which is legal in this scenario, even if not in the private part), and >>> make a dispatching call. (That's the case that's a problem.). >> >> No, you cannot, you don't see if the type inherits the interface, so you >> cannot convert to its class. > > We're talking about the type that both publically and privately implements > the same interface. In the example provided it is P1.Child_1.T2. > (The private one inherited from the parent, the public > one directly added.) And we're talking about a conversion that occurs where > the private interface is visible (so *both* copies of the interface are > visible). What converted to the type? But it is irrelevant anyway. The interface instances were already visible in the private part of the package where the offending type was declared. The conflict was *already* resolved there, or the type declaration rejected. >>> Which overriding operation do you get? >> >> None, that is illegal, the type does not implement the interface inherited >> privately. There is no overriding, there is no class in any public scope. > > You're not thinking broadly enough about all of the possible combinations of > public and private inheritance. It just doesn't work. Yes, the "normal" > cases work, but we have to allow all possibilities (or we're breaking > privacy). In particular, when you are in the private scope, you can see both > interfaces. I don't understand why you think so. Can you provide an example with the type declarations I gave? >>> How does it chose which interface to use? >> >> The one visible in this context. There is no unresolved conflicts, in any >> given context it is unambiguous. > > ??? Both interfaces are visible in the private context. How could they not > be both visible (they're distinct)? Because declaration of P1.Child_1.T2 is illegal. To make it legal at least one of the interfaces must be renamed. >> By violating privacy, by introducing ugly artifacts, by piling up nonsense >> rules... > > There's no violation of privacy. It's illegal to add an interface in a > private part, That constitutes violation of privacy. Usually it is more that you can do privately than publicly (:-)) >>>> It is not a problem, just let it be. >>> >>> The implementation of two copies of the same interface would break any of >>> the existing algorithms for implementing interfaces. (I have absolutely >>> no >>> idea how that could be accomplished; it would depend on how the >>> dispatching >>> rules were eventually worked out.) >> >> It breaks nothing. You get two hierarchies independent on each other. > > Sorry, but you don't have any idea how interface dispatching works. It's > typically done with some sort of lookup table (since the typical solution of > ensuring that all of the slots are the same in all of descendants does not > work when multiple inheritance is allowed). The slots in the dispatching table are different because interfaces are different. > That works because there is only > one copy of each interface, so there is no need to worry about where they > come from - one just needs a unique Id where they are declared. If there can > be multiple copies, that doesn't work. The copies get different IDs in the combined dispatching table, that is the purpose of renaming/conflict resolution. > And since these are runtime > operations It is no different from how dispatching works under MI. You must slide the table when moving toward the roots. In most general case you have Tag x Operation -> Body So it is T x I(1) and T x I(2), both different. You might have difficulties with interpretation re-dispatch if a body is inherited as-as, but that is only for full MI, we don't have still. Clearly I'Class in an inherited body must mean "the class of my I". >> You already can do just this with ease: >> >> generic >> package P is >> type I is interface; >> procedure Foo (X : I) is abstract; >> end P; >> >> package P1 is new P; >> package P2 is new P; >> >> type T is new P1.I and P2.I with null record; >> overriding procedure Foo (X : T); > > P1.I and P2.I are two different types. BINGO! > In the privacy case, you are getting > two copies of the same type. No, you get two different types (interfaces) if the conflict resolved. > Not remotely the same thing. (You can tell the > difference by trying to call a global class-wide operation that has a > parameter of the interface. For this example, that won't work for both > interfaces -- one or the other isn't going to be allowed. For the private > example, one could call that routine with either interface (because they > have the same name) -- that's the crux of the problem. Exactly, it is ambiguous just same as if in the body of P there were Bar (X : I'Class); overloaded from instances of P1 and P2. >> How is it different from: >> >> type T is new I and I with null record; > > There is only one copy of the interface in this type. No, there are two. And calling to Bar is ambiguous. The conflict must be resolved by naming a distinct I's in T. Once they named T gets independent sets of slots for each of the primitive operations of I. To be able to call Bar where you see both I's you will have to explicitly convert T to either of two I'Class. That will slide to the corresponding section of the dispatching table corresponding to the chosen I. -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: Class-wide types algebra 2016-10-01 8:35 ` Dmitry A. Kazakov @ 2016-10-05 20:42 ` Randy Brukardt 0 siblings, 0 replies; 14+ messages in thread From: Randy Brukardt @ 2016-10-05 20:42 UTC (permalink / raw) "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message news:nsnsho$6om$1@gioia.aioe.org... > On 2016-10-01 07:47, Randy Brukardt wrote: ... >> We're talking about the type that both publically and privately >> implements >> the same interface. > > In the example provided it is P1.Child_1.T2. > >> (The private one inherited from the parent, the public >> one directly added.) And we're talking about a conversion that occurs >> where >> the private interface is visible (so *both* copies of the interface are >> visible). > > What converted to the type? But it is irrelevant anyway. The interface > instances were already visible in the private part of the package where > the offending type was declared. The conflict was *already* resolved > there, or the type declaration rejected. I need to write out the entire example to get a sensible answer from you. I unfortunately don't have time, as I have to get the agenda ready for the ARG meeting that starts on Saturday (and of course I'll be traveling all day Friday). So I'm going to have to drop this discussion. (So I didn't read your reply, sorry.) The problem eventually becomes a run-time problem, since type conversions are primarily runtime artifacts. That's very ugly. And you can't reject the declaration that has two distinct copies of the same interface, because that would clearly violate privacy. So you get into a situation where you have to chose between two copies of an interface, with identical names, at runtime, presumably based on visibility. That's not going to work. Randy. P.S. Dmitry, Don't bother answering, I'm not going to read it or answer. Just can't afford the time. Sorry. ^ permalink raw reply [flat|nested] 14+ messages in thread
end of thread, other threads:[~2016-10-05 20:42 UTC | newest] Thread overview: 14+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2016-09-12 20:26 Class-wide types algebra Dmitry A. Kazakov 2016-09-12 21:12 ` rieachus 2016-09-12 21:39 ` Dmitry A. Kazakov 2016-09-13 11:46 ` rieachus 2016-09-13 12:26 ` Dmitry A. Kazakov 2016-09-28 0:05 ` Randy Brukardt 2016-09-28 7:31 ` Dmitry A. Kazakov 2016-09-28 20:17 ` Randy Brukardt 2016-09-29 8:06 ` Dmitry A. Kazakov 2016-09-29 18:44 ` Randy Brukardt 2016-09-29 19:55 ` Dmitry A. Kazakov 2016-10-01 5:47 ` Randy Brukardt 2016-10-01 8:35 ` Dmitry A. Kazakov 2016-10-05 20:42 ` Randy Brukardt
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox