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 Path: border1.nntp.dca3.giganews.com!backlog3.nntp.dca3.giganews.com!border2.nntp.dca.giganews.com!nntp.giganews.com!newspeer1.nac.net!newsfeed.xs4all.nl!newsfeed4a.news.xs4all.nl!xs4all!news.stack.nl!aioe.org!.POSTED!not-for-mail From: "Dmitry A. Kazakov" Newsgroups: comp.lang.ada Subject: Re: Your wish list for Ada 202X Date: Tue, 8 Apr 2014 11:15:59 +0200 Organization: cbb software GmbH Message-ID: References: <7f1c01c5-3563-4b94-9831-152dbbf2ecdc@googlegroups.com> <8bhozh836pyt$.1qctlysud0s2q$.dlg@40tude.net> <1cdsyxjzsfgzm.1synpaujysv21$.dlg@40tude.net> <1aa804jg9qq4o$.wdiq33yo621l.dlg@40tude.net> Reply-To: mailbox@dmitry-kazakov.de NNTP-Posting-Host: AuYlnUSfTZrfhAkRjyySpQ.user.speranza.aioe.org Mime-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit X-Complaints-To: abuse@aioe.org User-Agent: 40tude_Dialog/2.0.15.1 X-Notice: Filtered by postfilter v. 0.8.2 X-Original-Bytes: 13618 Xref: number.nntp.dca.giganews.com comp.lang.ada:185625 Date: 2014-04-08T11:15:59+02:00 List-Id: On Mon, 7 Apr 2014 20:15:10 -0500, Randy Brukardt wrote: > "Dmitry A. Kazakov" wrote in message > news:1aa804jg9qq4o$.wdiq33yo621l.dlg@40tude.net... >> On Wed, 2 Apr 2014 17:39:06 -0500, Randy Brukardt wrote: >> >>> "Dmitry A. Kazakov" wrote in message >>> news:1cdsyxjzsfgzm.1synpaujysv21$.dlg@40tude.net... >>> ... >>>>>> Of course The former >>>>>> would be greatly preferable. This requires yet another feature Ada >>>>>> lacks - >>>>>> interface inheritance. Unbounded_String must drop parent's >>>>>> implementation/representation and inherit only the interface. >>>>> >>>>> That's not the problem at all. (The parent here would have no >>>>> representation, so there is nothing to drop.) >>>> >>>> Unbounded_String will be derived from String, or String derived from >>>> Unbounded_String. The point is to keep it a hierarchy. >>> >>> Why? There is no interesting relationship between String and >>> Unbounded_String other than the interface (which is inherited from the >>> root >>> type). >> >> The relationship is that you can mix Unbounded_String and String in >> operations like "&". > > Yeah, just like mixing apples and oranges. Like mixing McIntosh and Jonathan apples. > Mixed operations are not the Ada way of typing. It is Ada's way of SUBtyping. Obviously Unbounded_String and String are related subtypes, promoted to types due to implementation reasons only. >>> There is no opportunity to share implementations, which is the other >>> reason to inherit one from another. >> >> Most of implementations of String can be shared by Unbounded_String. This >> is what people do manually all the time converting Unbounded_String to >> String and back. > > No, not really. Most implementations of Root_String'Class could be shared > amongst string types. That would be very inconvenient. Certainly, less class-wide operations are provided better is the design. > But specific implementations are tied to particular > representations; that's exactly what we want to get rid off. How a specific implementation could be tied to a class-wide type? That does not make sense. The model where operations are primitive has the decisive advantage that the operation can be inherited or overridden in order to provide a more efficient version. > Moreover, there cannot be automatic sharing of operations; that weakens the > type system. One only wants to share operations that are explicitly intended > to be shared; that's the point of declaring packages operating on > Root_String'Class rather than some specific type. I don't understand the term "shared operation." If you don't want to provide an implementation you declare the operation abstract. If you don't want an operation in the class, you move it into the body. Where is a problem? >>> Just inheriting things because something *thinks* they should be related >>> is silly. There is no advantage to using String'Class (if such a thing existed) >>> rather than Root_String'Class, since all the former does is restrict what >>> your subprogram can do; it doesn't add any capability. >> >> You have just said that constrained types are useless. What is the >> advantage of using String (1..80). It does not add any capability... > > Constrained types *are* useless -- Ada has no such thing. It only has > unconstrained types (remember that names only apply to subtypes). And in > particular, a constrained class-wide type is an evil thing, because it's > mixing things that should not be mixed: relaxing and tightening constraints > at the same time. OK, constraining is useless, discriminants are evil, specific instances of classes are wrong. We have to agree to disagree. >>>> And the real problem >>>> with all that is that there is more than one vector of inheritance: >>>> 1. memory management (fixed, bounded, unbounded) >>>> 2. range (character, wide_character, wide_wide_character) >>>> 3. encoding (UCS-4, UCS-2, UTF-8, ASCII) >>> >>> 1 and 3 are irrelevant, because they shouldn't have any effect on the >>> interface -- they're things that should be totally hidden outside of >>> streaming for 3. Perhaps 2 should have some affect on the interfaces, but >>> that's easily handled with a second level of abtsract types defining >>> those >>> interfaces. >> >> Presently 1-3 are exposed, and I don't understand how you are you going to >> hide them: >> >> type A (Length : Positive) is record >> X : String (1..Length); >> end record; >> >> type B is record >> X : Unbounded_String; >> end record; >> >> Looks a quite visible difference to me. > > Object declarations of course use concrete, specific types. So what's your > point? The point is that all aspects I mentioned cannot be hidden = have crucial effect on the interface and is in part the interface. > The majority of operations would take Root_String'Class and thus work > on any object. Ones that don't would have been restricted for some good > reason, and would work as now. What's so hard about that? It does not make any sense to me. Consider the operation Append. Which class does it belong to? Does it act on String? Is the argument UCS-2? >>> Besides, 1 can totally be defined after the fact with packages. That's >>> the model that I'm suggesting. >> >> This is equivalent to deriving from fixed string, which is the common >> denominator. > > No, I'm deriving from an abstract type with no representation. There's a > huge difference. No difference whatsoever. We are talking about interfaces. The interface of Unbounded_String is not the interface of String. When you force a set of types to one class you must drop parts of the interfaces uncommon to all members. This is why single parent will never work for any more or less non-trivial set of types. String types and numeric types are such sets. It is not even worth trying. The result is doomed. >>> "Unbounded_String" is a container for a >>> Root_String'Class type; it adds unbounded memory management to whatever >>> the string type does. There is no reason that one has to do that as part of >>> the string abstraction. >> >> But then you necessarily loose all operations String cannot have, e.g. >> Append, Replace, etc. The idea is of course that Unbounded_String, >> Unbounded_Wide_Wide_String, Unbounded_UTF8_String share this interface. > > Ada.Strings.Fixed has all of those operations. Append (S, SUNDANESE_LETTER_KHA); >>> You would have >>> to qualify all string literals in an MD world, and that's something >>> programmers would never tolerate. >> >> Ambiguity of literals can be resolved using preference rules like ones >> invented for Universal_Integer. We could introduce Universal_String etc. > > That doesn't work at all; universal integer only has a preference against > itself. All other types are equal. They will have no literals of their own, in this model. >> Another method could be making Latin-1 literals contravariant String, i.e. >> >> package Dont_Make_Me_Primitive is >> function "A" return String; >> function "AB" return String; >> function "ABC" return String; >> ... >> end Dont_Make_Me_Primitive; >> >> not inherited by Unbounded_String, Wide_String etc. That will introduce >> desired preference. > > Then Unbounded_String will not have any literals, Yes, it will inherit to String and thus be able to mix String in its operations, that will be equivalent to having literals/ > and you'd still have > problems with Wide_Wide_String vs. String. No, because wider literals will go to Wide_String and most wide literal to Wide_Wide_String. The idea is to dissect literals according to the code point planes. >> And, yes, you will be able to assign "ABC" to Unbounded_String because >> ":=" is a MD operation with one body dealing with that: >> >> procedure ":=" (Left : in out Unbounded_String; Right : String); > > But that's unimplementable. Assignment is an operation as any other. >> Otherwise, yes, preference rules are necessary to resolve >> >> A & B & C >> >> Between >> >> String'(A & B) & C >> >> and >> >> Unbounded_String'(A & B) & C > > Right, and such preferences are always trouble in a programming language. It is the nature of things. When you define an n-ary operation on a type hierarchy you have the problem. You can say that is why you don't want n-ary operation and/or type hierarchies and go straight back to FORTRAN-IV. But large scale and safe software design is unthinkable without this stuff. So either you force the programmer to do all the work manually, see string types and operations in Ada for the fruits of this approach, or else you provide a reasonable support in the language for dealing with real-world problems, making programmer's life safer and easier. > They are a prime source of beaujolais effects, which Ada considers > completely unacceptable. (Adding or removing a declaration should never > change a legal program into a different legal program with a different > meaning.) Hierarchies of types are not insulated against each other. You cannot simply add or remove declaration of an operation in the hierarchy. That will lead to changes in the dispatching tables, need to define/override something, if the operation is abstract, which is why freezing rules are here etc. It is the nature of a constraint which is far more important than any "beaujolais effects" - that no dispatch can fail at run-time. All slots of any dispatching table must be statically checked for being either properly inherited or overridden. That *necessarily* requires making something illegal which otherwise would be legal. "No-beaujolais" is a nicety for separate compilation, another is strong typing imperative. I gladly trade former for the latter if necessary. >>>>> We can't get rid of these problematical operations -- it would be way >>>>> too incompatible. So new packages is the only way to go. >>>> >>>> You can, and everything will stay compatible if the type system is fixed >>>> first (MD, MI etc). Within present type system it is indeed unfixable >>>> and moreover, new packages will likely become as messy as the old ones. >>> >>> As I said before, I am skeptical that an MD system (even ignoring MI) >>> could be implemented efficiently enough to be used in critical operations like >>> strings. As soon as you go to MD, the linear tag model has to ba abandoned, >>> and some sort of search mechanism used for finding bodies to dispatch to. >> >> An MD operation needs an N-D array indexed by tags of controlling >> arguments. Dispatching is indexing the array. > > Sure, but you can't represent the array that way, because you don't know N > at compile-time (someone can define new types and link them in after the > fact). I don't need to know it at compile time. I only need to know its subarray that includes the types visible in the context being compiled. That subarray is sufficient to get through. N must be known at bind/link-time. > And even if you did, it would be an impracticaly large a data > structure in most cases. (Consider that there are something like 20 string > types in a Root_String'Class model, N is not the total number of related types, N is the depth of the given hierarchy. You will have to map Tag to some dense index in the operation's inheritance path and then go into the array with this index. Raw tag is a poor index. >> (However there are serious problems with implementation of MD which I have >> no idea how to resolve. MI is much simpler) > > I suppose MI is simpler, but it has a similar problem with tag construction. > (At least there is only one dimension for interfaces; full MI would turn > things into a much worse situation, as hidden types would have to be > allowed - and then its possible for a type to have multiple copies of the > same interface.) That is a highly desired effect: to support additive model of inheritance. E.g. additive MI from doubly-linked list element. -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de