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=ham autolearn_force=no version=3.4.4 X-Google-Thread: a07f3367d7,73cb216d191f0fef X-Google-Attributes: gida07f3367d7,public,usenet X-Google-NewGroupId: yes X-Google-Language: ENGLISH,ASCII-7-bit X-Received: by 10.180.24.132 with SMTP id u4mr2777474wif.6.1365451620284; Mon, 08 Apr 2013 13:07:00 -0700 (PDT) MIME-Version: 1.0 Path: p18ni45336wiv.0!nntp.google.com!feeder1.cambriumusenet.nl!feed.tweaknews.nl!193.141.40.65.MISMATCH!npeer.de.kpn-eurorings.net!npeer-ng0.de.kpn-eurorings.net!xlned.com!feeder7.xlned.com!newsfeed.xs4all.nl!newsfeed2.news.xs4all.nl!xs4all!border4.nntp.ams.giganews.com!border2.nntp.ams.giganews.com!border3.nntp.ams.giganews.com!border1.nntp.ams.giganews.com!nntp.giganews.com!news.thorslund.org!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: Wed, 3 Apr 2013 19:04:24 -0500 Organization: Jacob Sparre Andersen Research & Innovation Message-ID: References: <1raubw1sk48ca$.69rdgczvnnf.dlg@40tude.net> <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> NNTP-Posting-Host: static-69-95-181-76.mad.choiceone.net X-Trace: munin.nbi.dk 1365033876 24335 69.95.181.76 (4 Apr 2013 00:04:36 GMT) X-Complaints-To: news@jacob-sparre.dk NNTP-Posting-Date: Thu, 4 Apr 2013 00:04:36 +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 Date: 2013-04-03T19:04:24-05:00 List-Id: "Dmitry A. Kazakov" wrote in message news:1gkxiwepaxvtt$.u3ly33rbwthf.dlg@40tude.net... > On Tue, 2 Apr 2013 16:54:01 -0500, Randy Brukardt wrote: > >> "Dmitry A. Kazakov" wrote in message >> news:3gv2jwc95otm.pl2aahsh9ox8.dlg@40tude.net... >>> On Mon, 1 Apr 2013 19:40:42 -0500, Randy Brukardt wrote: >>> >>>> "Dmitry A. Kazakov" wrote in message >>>> news:1gnmajx2fdjju.1bo28xwmzt1nr.dlg@40tude.net... >>>> .... >>>>> The point is that whether you create a totally new string hierarchy or >>>>> create one for already existing string types, you will have the issue >>>>> with >>>>> "&" (and any other operation with more than one argument/result of >>>>> string >>>>> type). That is *independent* on whatever Ada did or not before. It is >>>>> in >>>>> the nature of a multi-method, which "&" is. If you want a hierarchy of >>>>> strings you *must* face this problem. >>>> >>>> No you don't. There are no language-defined multi-methods. String & >>>> String = String is not a multi-method. >>> >>> Once String becomes member of a class it is. For the operation "&" there >>> exist only two possibilities, Either it is a primitive operation of the >>> class and so a multi-method, or else it is declared individually on each >>> member of the class in separate packages (somewhere after the freezing >>> point). The later variant is so silly that I don't even consider it. >> >> I agree that latter variant is silly, but the notion that somehow "&" >> becomes a multi-method simply because it exists makes no sense. "=" is >> not a >> multi-method in Ada today for tagged types; > > Assignment should certainly be a multi-method, because in some cases you > want to be able to assign one type of the hierarchy to another. Assignment > is a bit special case because the target's tag cannot be changed anyway. > That reduces the number of use-cases for mixed types. Others could be > further reduced using constrained subtypes, but they still exist. > > As a multi-method use case consider a tagged class with the operations "=" > and "<" defined. These must support mixed types almost always. > >> "&" would work the same way - it >> does not take items of different types unless you explicitly convert >> them. >> Maybe you have a different idea of a multimethod than I do?? > > 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). So here again we must agree to disagree. >>>> You, the user, can define such operations, of >>>> course, but then you have the deal with the consequences. Generally, >>>> it's >>>> best if you don't do that. >>> >>> What is the use of a string type without operations? >> >> Huh? There are predefined single-type operations. If you want mixed type >> operations, you have to define them, > > It would be a combinatorial explosion of variants and a huge issue with > visibility. The language should support this use case, because it is a > pattern for all dyadic operations, assignment, comparisons. 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. >> but it's usually a bad idea to do so. >> You're almost always better off with 'Class operands if you need type >> mixing -- for type resolution reasons (not even mentioning runtime >> issues). > > That does not solve the problem, you will get Constraint_Error. You don't get Constraint_Error when one of the arguments is class-wide: function Is_Same (Left : A_Type; Right : Root_Type'Class) return Boolean; There is no automatic reason for this to raise Constraint_Error. You do have to implement this, of course, and whether that can easily be done depends on the operations of your class-wide type -- but that's all under the control of the programmer. > 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. > 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. 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. Based on our experience with much easier changes (like resolving routines inherited from interfaces), the likelihood of getting that right is about 0% - it would take years of detailed work to create a model that both worked and did not introduce incompatibilities. It would require there to be critical bugs in Ada (or critical needs) for the (current) ARG to take on such changes. You aren't even willing to write up real use-cases where the existing language has problems (because I've asked that you do that in the past) -- without those there is no chance that any significant changes will be entertained here. ... >> In any case, that >> extends the problem of creating extensions -- it would be so diifficult >> and >> time-consuming that it almost never would be done. > > It is not an extension, it meant to be the same interface and a completely > different representation. Extension is the same interface and extended > representation. Interfaces alone are worthless. The only thing that makes inheritance worth the effort is the ability to share implementations. > 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 suspect that this would just be too complex for the average Ada user to understand. It took me a long time to understand inheritance at all -- I never did understand it in Ada 83 and it took a huge amount of effort (and creating several large libraries using it) before I got any feeling for it at all. >>>> 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. You'd have to forcibly specify one or the other for all types, which is certain to be incompatible with existing implementations. 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). That would mean that the behavior of such a conversion would be very different for an untagged type and for a tagged type. Not sure if that is a real problem, but it certainly would be the sort of thing we would not want to be true. (Essentially this would mean that re-dispatching is not possible for untagged objects. I agree that re-dispatching is usually a mistake, but sometimes you need it -- we did use it in Claw in a few instances for message delivery, for example.) >>>> That >>>> forces the representation to be compatible. You could change that for >>>> by-copy types (although that would have run-time compatibility issues >>>> for >>>> controlled objects), >>> >>> Controlled objects are tagged, hence no change here. >> >> A component is an object; I'm talking about untagged objects with >> controlled >> components here. > > 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). It also could cause problems if such a call occurred inside of an Adjust or Finalize, you could unintentionally cause an infinite regress. >>>> but you can't change that for types that have no copy >>>> operation (like tasks and protected objects). So how to do you >>>> implement >>>> them? Array of task is an untagged type, too, and it certainly should >>>> be >>>> able to use 'Class. >>> >>> Sure. T'Class will be a referential object when T is untagged >>> by-reference >>> type. It will consist of the type tag and a *pointer* to the specific >>> object (not much different from 4.1.5), and from view conversions ether, >>> though formally a new object. >>> >>> When T is by-copy, T'Class is the type tag + a copy of T. >> >> Wow! And this is supposed to be a *simplification*??? :-) > > Yes. It is much simpler to view tagged types as a special case [definite > representation constraint] of a more general model that encompasses all > types. A more general model that is many times more complex than the existing model. What exactly is being simplified? It's like the 1986 tax simplification in the US; they eventually renamed it to "tax reform" because in the end nothing was simplified. >>>>>> It really kills the entire idea. >>>>> >>>>> No, IF String inherited the representation of Wide_Wide_String, THAT >>>>> would kill the idea. >>>>> >>>>> 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. >> I'm interested in what we can do with tagged types. Forget the silly >> interfaces, or call it an interface if that makes people feel better >> (that's >> what we did with iterators, but it doesn't actually buy you anything). >> It's >> an Ada 95 abstract type. > > If so, allow to inherit from more than one. Allow to inherit only > interface > from such a type. That's why we'd probably define it as an interface, so that misguided people like you will feel better. :-) I find no value whatsoever to interfaces without a skelton shared implementation, so I want components and concrete operations in any type. And implementing full MI is prohibitively expensive, on top of which it's usually not the best solution anyway. > You have the problem of "&" at hand. It cannot be solved > without the above mechanisms. And multi-method and MI is only a start. The > real problem is *full* MD as "&" is defined on String and Character. We can agree to disagree on the above. You are just babbling in my view -- I see no problem at all with "&". Combining different types in an ad-hoc way is the same as not having typing at all in terms of error detection. You need more than just a definition of what happens when type A is converted to type B, you also have to require almost all conversions to be explicit. And once you do that, multi-methods are virtually never needed. ... >>> The proper pattern would be: >>> >>> type Measurement is digits 5 range -1.0E+10 .. 1.0E+10; >>> ... >>> type Velocity is new Measurement; >>> type Acceleration is new Measurement; >>> ... >> >> This is a terrible design pattern because it puts unrelated types >> together >> (and, allows them to be converted to a common ancestor, which almost >> always >> is a bug - especially if we had Measurement'Class). > > They are related, Acceleration is computed from Velocity and Duration. On > top of that come a huge pile of generics (can't rid of them) instantiated > with Measurement, e.g. I/O, GUI stuff. In real-life case you can't do that > with individual types. You need a class and this is what Measurement is > for. Your real-life is obviously completely different than mine. There are almost no generics in any of our code (especially in the compiler). And even if there were, I'd be happy to have as many instantiations as needed -- code shared generic instances are pretty cheap to create. ... >>>> Either you have a conversion to the original >>>> type, in which case they belong to the same hierarchy. >>> >>> This is a possible way to implement cloning. This is not the semantics >>> of. >>> The semantics is that there is a new set of types, values and operations >>> with the properties of the original. Nothing more. >> >> If they allow conversion (including implicitly to the class-wide root of >> the >> class) and instantiation, they're members of the same hierarchy. > > The semantics for tagged types (if they were supported) should be creating > a new type for each parent type. > > type T is new Ada,Finalization.Controlled with ...; > type Q is new T; > > X : Q; > Y : Ada.Finalization.Controlled'Class := X; -- Type error > Z : Ada.Finalization.Controlled'Class := T (X); -- OK > > Q is not an extension of Ada,Finalization.Controlled. It is of some > anonymous type cloned from Ada,Finalization.Controlled: > > type is new Ada,Finalization.Controlled; Sure, but what does this mean? If there is no conversion or instantiation associated with this new type, it's useless. (You'd need to duplicate all of the code associated with it.) And if there is, it's not a separate hierarchy. Anyway, this is getting pointless (especially this last part). I think this discussion is pretty much done, so I'm going to try to refrain from answering any more on this. Randy.