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: eternal-september.org!reader01.eternal-september.org!reader02.eternal-september.org!news.eternal-september.org!news.eternal-september.org!news.eternal-september.org!feeder.eternal-september.org!news.stack.nl!reality.xs3.de!news.jacob-sparre.dk!loke.jacob-sparre.dk!pnx.dk!.POSTED!not-for-mail From: "Randy Brukardt" Newsgroups: comp.lang.ada Subject: Re: Your wish list for Ada 202X Date: Tue, 8 Apr 2014 18:37:35 -0500 Organization: Jacob Sparre Andersen Research & Innovation 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> NNTP-Posting-Host: static-69-95-181-76.mad.choiceone.net X-Trace: loke.gir.dk 1397000256 31732 69.95.181.76 (8 Apr 2014 23:37:36 GMT) X-Complaints-To: news@jacob-sparre.dk NNTP-Posting-Date: Tue, 8 Apr 2014 23:37: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 Xref: news.eternal-september.org comp.lang.ada:19196 Date: 2014-04-08T18:37:35-05:00 List-Id: "Dmitry A. Kazakov" wrote in message news:yl8s1sbhw0lw.1bhypcrvreord.dlg@40tude.net... > On Mon, 7 Apr 2014 20:15:10 -0500, Randy Brukardt wrote: ... >> 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. Obvious to you only. Any single program/subsystem should stick to a single string type. The fact that you can't do that in Ada (unless that type is String) is the bug that needs to be fixed. >>>> 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. "In"convinient? Why? Use all and prefix notation lets one use class-wide operations in many circumstances. And in any case, the whole point is to eliminate the requirement to use a single, specific string type for operations that should be generic (generic in the English, not Ada, sense). >> 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. Very, very little of string operations should be tied at all to any particular implementation. In particular, nothing in Ada.Strings.* is primitive to string types per-se; they're all higher level operations created out of lower-level primitives. They should therefore be class-wide so there only needs to be one implementation. > 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. That wouldn't improve the performance enough to matter. If there is a problem with this scheme, it's that it could be very inefficient. But that's mostly because of the use of dispatching, and that's required for the goal to be reached (for most of the existing predefined operations to have versions that take/return Root_String'Class rather than String). If we can't reach that goal, there is no point to doing this at all. My experience is that tagged types with lots of primitives are virtually un-extendable -- it just takes too much work to create an extension. The vast majority of those primitives have to be overridden (unless you are using redispatching, which I agree is a bad idea in general), and it just slows the process down to the point where nothing gets accomplished. (I essentially abandoned work on the Claw Builder for this reason.) >> 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? As above, it's too much work to extend a type that requires overridding more than a handful of primitives. It takes 3 days of work to get a compilable, testable extension of the window type in the Claw Builder -- anything over 4 hours or so is unacceptable. ... >> 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. Umm, no, the "interface" doesn't have anything to do with concrete implementations. And since you didn't mention any aspects at all, I'm even sure what you're talking about. All you did is declare some components of specific types. Maybe you're talking about the constraints, but we already agreed to disagree on that. I don't think they have anything whatsoever to do with the interface, and in fact explicitly should *not* be part of 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? Append takes two operands of Root_String'Class, and returns an Root_String'Class with the type of its first argument. What's the problem? ... >> 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. That's true, but that's a bug. They *should* be the same; only the runtime behavior should differ (when exceptions are raised and the like). > 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. Possibly, but then programming language design in general is doomed. The only solutions that would work are crazily inefficient. But the real truth is that you're confusing the totality of the operations of a type with the small targeted part of those operations that form a valuable abstraction. Interfaces that are useful are small and targeted. The main advantage of Ada's interface types is to mix and match those small interfaces on a variety of types. But we don't need to do that here. The only interface that matters is the one that allows useful operations to be constructed on Root_String'Class. Other interfaces are irrelevant, because there is nothing that we might want to do with them. (And of course, if you want to define other interfaces, go ahead - that's the purpose of MI.) >>>> "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); Yes, that's good. If the underlying type cannot store the letter, Constraint_Error is raised. As with all tagged types, you trade-off compile-time checking for generality. You can't have both (I know people try, but they are mad - they'll have just as much luck creating a perpetual motion machine). >>>> 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. They have no literals now, in this model. You're just describing the way things are, and then expect it to work differently. ... >> 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. So the behavior of the program changes when the contents of a string literal changes? No thanks. ... >> 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. In other words, you are willing to weaken Ada's contracts in order to get your supposed "strong typing imperative". Essentially, in your world, you cannot change a package specification because you cannot know what effect such changes have on clients (clients that you don't know much about). That means that there is no hope for reusable software - the originator of the software has to be able to modify it in order to improve or correct it. If that breaks all clients, then either one can do nothing at all, or people just have to stick to their existing (buggy) versions. Neither is acceptable. >>>>>> 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. But it's all we have. Sure, you can map a tag (this requires a loop, either a brute force lookup or some fancier but much more code-intensive version), but that's going to make dispatching far more expensive. You can't do this mapping at compile-time because what mapping you need depends on the interfaces in use. >>> (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. It doesn't work, though, because you have to know *which* copy of the interface you are dispatching through. The operations can be different for each such interface (if they can't, you've completely given up on privacy). You could of course just abandon privacy and maintainability for your type-system, but the result would bear no resemblance to Ada. Randy.