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=-1.9 required=5.0 tests=BAYES_00 autolearn=unavailable autolearn_force=no version=3.4.4 Path: border1.nntp.ams3.giganews.com!border1.nntp.ams2.giganews.com!border3.nntp.ams.giganews.com!border1.nntp.ams.giganews.com!nntp.giganews.com!newsfeed.fsmpi.rwth-aachen.de!nuzba.szn.dk!news.jacob-sparre.dk!munin.jacob-sparre.dk!pnx.dk!.POSTED!not-for-mail From: "Randy Brukardt" Newsgroups: comp.lang.ada Subject: Re: Is this expected behavior or not Date: Thu, 4 Apr 2013 15:31:23 -0500 Organization: Jacob Sparre Andersen Research & Innovation Message-ID: References: <2qwq2cdeuvhu$.qtnb8zyhuob9$.dlg@40tude.net> <1u72u7h5j4jg3$.wlxmaltyzqik.dlg@40tude.net> <1gnmajx2fdjju.1bo28xwmzt1nr.dlg@40tude.net> <3gv2jwc95otm.pl2aahsh9ox8.dlg@40tude.net> <1gkxiwepaxvtt$.u3ly33rbwthf.dlg@40tude.net> <1fmcdkj58brky.bjedt0pr39cd$.dlg@40tude.net> NNTP-Posting-Host: static-69-95-181-76.mad.choiceone.net X-Trace: munin.nbi.dk 1365107487 16776 69.95.181.76 (4 Apr 2013 20:31:27 GMT) X-Complaints-To: news@jacob-sparre.dk NNTP-Posting-Date: Thu, 4 Apr 2013 20:31:27 +0000 (UTC) X-Priority: 3 X-MSMail-Priority: Normal X-Newsreader: Microsoft Outlook Express 6.00.2900.5931 X-RFC2646: Format=Flowed; Original X-MimeOLE: Produced By Microsoft MimeOLE V6.00.2900.6157 X-Original-Bytes: 13323 Xref: number.nntp.dca.giganews.com comp.lang.ada:180925 Date: 2013-04-04T15:31:23-05:00 List-Id: "Dmitry A. Kazakov" wrote in message news:1fmcdkj58brky.bjedt0pr39cd$.dlg@40tude.net... > On Wed, 3 Apr 2013 19:04:24 -0500, Randy Brukardt wrote: > >> "Dmitry A. Kazakov" wrote in message >> news:1gkxiwepaxvtt$.u3ly33rbwthf.dlg@40tude.net... > >>> If you have more than one controlled argument/result of the same type, >>> that makes it a multi-method. The natural consequence of this is that >>> all >>> possible combinations of types from the hierarchy are specific bodies to >>> inherit/override. In Ada 95 only the bodies when all types are same can >>> be >>> overridden. Statically the compiler prevents calls to mixed types >>> bodies, >>> but they nevertheless exist and can be reached through dispatch. In that >>> case it works as if the body were "rase Constraint_Error." This hack >>> won't >>> work for &, =, <, <= etc. Those require almost all combinations to work. >> >> I see where you are coming from, but I don't agree with any of it on a >> fundamental level: mixing types in operations is virtually always an >> error >> (either a real mistake or a design error). > > How is it an error to compare String and Wide_String? The same way it is an error to compare a float and an integer. You could make a rule with somewhat sensible results, but it's a bad idea because it hides more errors than it simplifies correct code. >> I don't believe that these use cases are at all common. I don't recall >> any >> such cases in Claw, for example, which uses Ada 95 inheritance >> extensively. > > That is because GUI does not require dyadic operations at all. There are > few cases for MD though. E.g. > > procedure Render (Surface : in out Device_Type; Shape : Geometric_Type); > > And of course: > > procedure Set_Text (Control : Edit_Box; Text : Root_String_Type); This is clearly class-wide: procedure Set_Text (Control : Edit_Box; Text : Root_String_Type'Class); The implementation would use the constructor for Text to create the value, so it's straightforward to implement. (The Edit_Box has to have some particular representation for text, even if the result does not.) >>> The only pattern that sometimes works is recursive cascaded dispatch. >>> That >>> is when you make one argument controlled and another class-wide. >> >> Gosh, that's what I was talking from the beginning. > > It works only with dyadic operations, procedures or else functions > returning an alien type. Which is 95% of the cases that you would want. >>> I used it for "=" and "<" multi-method operations of the target type >>> hierarchy of >>> smart pointers. I needed this to be able to have ordered collections of >>> smart pointers to such objects when pointers are ordered by the objects >>> they point to. It is extremely tedious, involves recursive calls and >>> explicit tag comparisons for ancestry. It won't work with for operations >>> like &, which has two arguments and the result. >> >> You don't want mixed operations in cases like "&", ever. > > Non-starter for strings. Mixed operations are almost always a non-starter for any type. >> It simply doesn't >> make any sense from a resolution prespective. You want to fix resolution >> by >> introducing a set of very complex preference rules. > > Actually I want these rules defined by the programmer. And resolution is > only the issue with the operations returning the type, e.g. literals. One > cannot have both covariant result factories and multi-method operations. > If > I ran the circus I would try to define rules that would prohibit > declaration of potentially conflicting operations, like: > > type T is ...; -- Untagged class > > function Generator return T; > function "=" (L, R : T) return Boolean; > > This should be an error unless preference rules defined by the programmer > for T. I have no idea how that could work. Resolution is a very complex business both to define and to implement. It's hopeless for ordinary users to be able to make sense of it, and the implementation seems like it would be impossible. Moreover, unbounded preference rules also means unbounded maintenance headaches (preference rules almost always causing bad behavior). Generally, it's the lack of overloading, not the provision for more, that causes the trouble. ... >> Interfaces alone are worthless. The only thing that makes inheritance >> worth >> the effort is the ability to share implementations. > > Implementation /= representations. You can share class-wide operations and > inherit primitive operations without sharing the representation. You can, but it's virtually worthless to do so. It's simply too much work for any supposed benefit. (Not that I expect you to believe this.) >>> Anyway, the point is, that the compiler shall inherit a body IF AND ONLY >>> IF >>> it knows that this would be safe to do. That is the following cases: >>> >>> 1. Any operation when the representation is same >>> >>> 2. In-operation when the representation is extension or constraint >>> >>> 3. Out-operation when the representation is generalization (lifted >>> constraint) [Ada does not have this, as an example consider extension of >>> an enumeration type] >>> >>> 4. User-defined conversion defined, programmer's responsibility [Ada >>> does >>> not have this] >>> >>> On top of that come pre- and post-conditions which may in their turn >>> disallow inheritance of the body. >> >> Besides being incompatible, > > ? I merely explained why Ada 95 design choice was right [#2]. > >> I suspect that this would just be too complex >> for the average Ada user to understand. > > It need not to be understood. The programmer will be asked to override > operation or else to make type abstract, just as Ada 95 did. You have to avoid this sort of thing, the Ada 95 effects make it absolutely necessary to avoid functions returning a controlled type, else creating extensions is so hard that no one will do it. And to avoid them you have to understand it. ... >>>>>> The only way to implement those is >>>>>> via some sort of dispatching, and the need poisons the possibility of >>>>>> omitting tags, even for "specific" operations. >>>>> >>>>> I don't understand where you get this. The model is exactly the model >>>>> of >>>>> tagged types, but without specific objects required to keep the tag. >>>>> The >>>>> only thing it influences is view conversions between T and T'Class. >>>>> Nothing else is changed. >>>> >>>> When you dispatch to an inherited routine, you directly call that >>>> routine >>>> with the operands view converted (which cannot make copies in the >>>> general >>>> case). >>> >>> For by-copy types dispatching call will be copy-in / copy-out. The >>> representation of T'Class is tag + value. Tag is stripped, value passed >>> through. >>> >>> For by-reference untagged types dispatching call will pass the >>> reference. >>> The representation of T'Class is tag + pointer. Tag is stripped, >>> reference >>> passed through. >> >> What about the majority of types which are neither By-Copy nor >> By-Reference? >> Such types allow the compiler to chose how they're passed, and that would >> seem to be a huge problem for this model. > > Not at all, the compiler is free to choose, so it must. [I don't consider > schizophrenic compilers choosing one way and doing in another. That does > not work with any model.] It doesn't work for portability reasons for some compilers to call Adjust during the evaluation of parameters and other compilers to not call Adjust. It would make performance wildly variable and possibly cause bugs (if the program unintentionally depended on one or the other. >> You'd have to forcibly specify one >> or the other for all types, which is certain to be incompatible with >> existing implementations. > > The compiler vendor simply follows the choice he made before. Thus making most Ada code non-portable. That's a great idea. :-( >> It strikes me that the model of stripping tags means that type conversion >> of >> an object to a class-wide type would effectively change the tag (if the >> routine was inherited or called by a conversion of an operand). > > Yes, re-dispatch is a broken thing. Don't do it, because it is ambiguous > in > general case when objects do not have an identity. By-copy scalar types > are > such things. There is nothing which could distinguish Integer 13 from > Long_Integer 13 except the context. Once you dispatched that information > is > lost. There is no way back. > >> That would >> mean that the behavior of such a conversion would be very different for >> an >> untagged type and for a tagged type. > > Not different. It is just so that tagged types per design have type > identity. Other types simply don't have it. Objects of limited types are always assumed to have an identity; that's why they can't be copied. ... >>> No change either. If a by-copy object passed by value has a controlled >>> component that component is copied-in and out using Adjust and >>> Finalization. >> >> That's the problem. If that is done in existing code, you have an >> incompatibility (because the same code does something different now). > > Why? If that type is by-copy then this is how it must work already. If it > is by-reference then nothing changes either. Because currently, you don't call Adjust/Finalize for a type conversion. And it would have to be specified whether a type is by-copy or by-reference (it could not be left to the compiler with these rules, as I previously mentioned) -- that means compilers would have to change in some cases. ... >> A more general model that is many times more complex than the existing >> model. What exactly is being simplified? > > You will have all strings in one hierarchy. All characters in another. All > integer types in third. Tagged types described using a footnote. That's already true, you just don't have inheritance of operations for these hierarchies. So exactly what you are simplifying but introducing this whole new notion of type cloning, this new notion of when you must override routines, and so on?? >>>>>>> The whole idea is not to inherit irrelevant representations! >>>>>> >>>>>> I don't see the problem here; if Root_String is an abstract tagged >>>>>> type, >>>>> >>>>> Java interfaces will not work for characters/strings. No need even to >>>>> try it. >>>> >>>> Java interfaces will not work for anything. No need to even try. :-) >>>> And >>>> who's talking about Java, anyway? >>> >>> You want strings hierarchy of one level depth. That won't work. >> >> I don't even know what you mean by "one level depth", but I've already >> prototyped such a string type and it works fine -- better than the >> existing >> types. > > You want to derive each string type straight from Root_String. Yes, or from related ones. There is no significant value to doing anything else - as you point out, if you can inherit the operations, the representations have to be compatible. So there is nothing useful you can do with multiple levels. And if you want pure inheritance of (non-string) interfaces, then mix in some interfaces with your types (forget normal derivation in that case). Randy.