From mboxrd@z Thu Jan 1 00:00:00 1970 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on polar.synack.me X-Spam-Level: X-Spam-Status: No, score=-0.3 required=5.0 tests=BAYES_00, REPLYTO_WITHOUT_TO_CC autolearn=no autolearn_force=no version=3.4.4 X-Google-Thread: 103376,81bb2ce65a3240c3 X-Google-NewGroupId: yes X-Google-Attributes: gida07f3367d7,domainid0,public,usenet X-Google-Language: ENGLISH,ASCII-7-bit Received: by 10.68.230.98 with SMTP id sx2mr2167655pbc.1.1336550606904; Wed, 09 May 2012 01:03:26 -0700 (PDT) Path: pr3ni5396pbb.0!nntp.google.com!news2.google.com!goblin2!goblin.stu.neva.ru!aioe.org!.POSTED!not-for-mail From: "Dmitry A. Kazakov" Newsgroups: comp.lang.ada Subject: Re: What would you like in Ada202X? Date: Wed, 9 May 2012 10:02:50 +0200 Organization: cbb software GmbH Message-ID: References: <3637793.35.1335340026327.JavaMail.geo-discussion-forums@ynfi5> <172r5u18q2247.ze0a856uvhqt.dlg@40tude.net> <1nmd7pjdxlzs1.cwtljox0244r.dlg@40tude.net> Reply-To: mailbox@dmitry-kazakov.de NNTP-Posting-Host: FbOMkhMtVLVmu7IwBnt1tw.user.speranza.aioe.org Mime-Version: 1.0 X-Complaints-To: abuse@aioe.org User-Agent: 40tude_Dialog/2.0.15.1 X-Notice: Filtered by postfilter v. 0.8.2 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Date: 2012-05-09T10:02:50+02:00 List-Id: On Tue, 08 May 2012 22:20:21 +0300, Niklas Holsti wrote: > On 12-05-08 12:03 , Dmitry A. Kazakov wrote: >> On Tue, 08 May 2012 02:18:28 +0300, Niklas Holsti wrote: >> >>> On 12-05-07 10:49 , Dmitry A. Kazakov wrote: >>>> On Mon, 07 May 2012 00:26:52 +0300, Niklas Holsti wrote: >>>> >>>>> On 12-05-06 22:28 , Dmitry A. Kazakov wrote: >>>>>> >>>>>> Yes, for tagged types operations which are unsafe to inherit are inherited >>>>>> abstract, e.g. functions returning covariant value. >>>>> >>>>> The same could be done also for extension by deepening, but it is not >>>>> needed, as long as we don't introduce tags and class-wide programming >>>>> for these types. Inheriting these operations as abstract could even be >>>>> inconvenient, since it would force the program to implement (override) >>>>> the operations even when they are not needed. >>>> >>>> What are you going to do with user-defined operations? >>> >>> As distinct from what? Predefined operations? Why should there be any >>> difference? >> >> Because if you inherit them, you get all substitutability problems you >> wished to avoid: >> >> procedure Foo (X : out Message_Kind1) is >> begin >> X := Request; >> end Foo; >> >> But you already answered below than you want to disallow all out and in-out >> operations. > > Yes, so Foo would not be inherited under that strict rule. > > Perhaps the derived "deepened" type could inherit operations with "out" > and "in out" parameters of the parent type if these parameters were kept > as parent type, and not mapped to the derived type No, they must be inherited exactly same way The new operation has the parameter of derived type when inherited, that is covariance. When an in operation is inherited it too has the parameter of the derived type, which is then converted and passed to the inherited body. For out-operations you call the inherited body and then convert the result. For in-outs, you convert-in, call, convert-out. You might mean that composition with conversion could be inappropriate here. That is a separate issue. If you want to generate conversions, yes you are in trouble. If you let the programmer to supply a conversion which cannot be generated, that becomes his problem. Note also, that if exceptions were contracted, he would not be able to use a conversion raising Constraint_Error for missing values. Which would add necessary safety to the mechanism. >>> My suggestion for derived enumeration types that extend the parent type >>> (by "deepening") would create derived types (in the Ada sense), not >>> subtypes (in the Ada sense). Thus any user-defined (primitive) >>> operations for the parent type (except those with "out" parameters of >>> that type, as already discussed) would be inherited (redeclared) for the >>> derived type and implemented with automatic conversion of "in" parameters. >> >> This would exclude all functions returning values of the type. In >> particular T'Val, T'Succ, T'Pred. Are you going to define some of them new? > > Of course the derived "deepened" type must have these operations, since > it is an enumeration type, (See how quickly multiple inheritance slips in? (:-)) > but I would not consider them as "inherited" > (except in the sense that the parent type also has operations with the > corresponding names and uses). The probe question is: do they overload or do they override. covariant <=> overrides <=> inherited <=> method <=> primitive operation contravariant <=> overloads <=> not-inherited <=> free function <=> non-primitive or class-wide >>>> they >>>> get automatically inherited in any context where visible. [This in effect >>>> makes you exposed to "class-wide" programming with subtypes even if you >>>> don't want to. All subtypes of a type form a class with ad-hoc operations >>>> gathered from all members, when visible.] >>> >>> Could you give an example, preferably using the types Message_Kind1 and >>> Detailed_Message_Kind? >> >> So far it is clear, except for: >> >> 1. Distinction between primitive and non-primitive operations. Are all >> in-operations primitive (inherited). E.g. >> >> package P >> type Detailed_Message_Kind is new Message_Kind1 ... >> end P; >> > > I assume Q (below) has "with P; use P;". > >> package Q is >> procedure Baz (X : Message_Kind1); >> ... >> end Q; >> >> with P, Q; use P, Q; >> package R is > > I assume you meant "procedure R is" here. > >> X : Detailed_Message_Kind; >> begin >> procedure Baz (X); -- Is this legal? > > I would say illegal. The potentially inherited operations should be the > same as for a derived enumeration type in current Ada, as if the > deepened type were defined just as > > type Detailed_Message_Kind is new Message_Kind1; > > and the only difference should be the treatment of "out" and "in out" > parameters and function return values. OK, that is consistent. This is exactly the model we have for tagged types - embedded tags. This is why it makes no sense to implement it for specifically enumeration types. The disadvantages it may have (rather imaginary ones, IMO) will all be yours. So why not to face it, and just do it for all types. Sorry for advocating this for years, but there is no other way. >> 2. Double-dispatch / multi-methods. What happens with operations which have >> several parameters of the parent type: >> >> procedure Set (X : in out Message_Kind1; Y : Message_Kind1); >> >> This is inherited in Y, but not inherited in X. Will it become >> contravariant in X? E.g. >> >> procedure Set (X : in out Message_Kind1; Y : Detailed_Message_Kind); > > Yes, that could be a way to (weakly) inherit operations with "out" or > "in out" parameters and function returns. But the consequences of such a > rule should be thought out, since it differs from the current Ada > inheritance style, as I understand it. You will have to solve MD problematic as well. E.g. type T1 is (...); type T2 is (...); procedure D (X : T1; Y : T2); type S1 is new T1 with ...; type S2 is new T2 with ...; X1 : T1; X2 : S1; Y1 : T2; Y2 : S2; begin D (X2, Y1); -- Illegal? D (X1, Y2); -- Legal but Constraint_Error (Ada 95 hack) > I'm sure there are complications to sort out. I haven't thought very > long or deep about this. If people are interested in this idea, it would > motivate some effort on it. Second to this! Instead of perpetual hand waving about compatibility and imaginary evils of MI, we, Ada community, should consider how to sort the mess out. > I would like to make the "deepening" derivation as similar as possible > to the derivation of enumeration types in With the problems solved you will get enumeration extension for granted. Your proposal rounds about how to generate conversion functions. You can generate in-conversion, but cannot out-conversions. The case of plain extension has the opposite problem: in-conversions are impossible. But the mechanics of all this is same: interface inheritance, delegation of some inherited operations to the parent's body composed with a conversion. Semantically, there always will be some issue with conversions, because any non-trivial modification of a type, must break something. If it did not, you would use the old type instead. It is not the language business, it is up to the programmer to resolve. The language should simply refuse to automate suspicious cases forcing the programmer to intervene. -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de