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: border2.nntp.dca.giganews.com!nntp.giganews.com!newspeer1.nac.net!feeder.erje.net!eu.feeder.erje.net!news.albasani.net!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: Wed, 9 Apr 2014 22:28:36 -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> <1w6eh0aiksmdh$.1h16p7y0b8c6h.dlg@40tude.net> NNTP-Posting-Host: static-69-95-181-76.mad.choiceone.net X-Trace: loke.gir.dk 1397100516 12558 69.95.181.76 (10 Apr 2014 03:28:36 GMT) X-Complaints-To: news@jacob-sparre.dk NNTP-Posting-Date: Thu, 10 Apr 2014 03:28: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: number.nntp.dca.giganews.com comp.lang.ada:185646 Date: 2014-04-09T22:28:36-05:00 List-Id: "Dmitry A. Kazakov" wrote in message news:1w6eh0aiksmdh$.1h16p7y0b8c6h.dlg@40tude.net... > On Tue, 8 Apr 2014 18:37:35 -0500, Randy Brukardt wrote: > >> "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. > > I don't understand what you are trying to say. Is it that no program shall > use both Unbounded_String and String or that there must be no String > (Unbounded_String) at all? There should be no reason for a single subsystem to use more than one string type. The fact that that is not true in Ada today is the cause of most of the problems. As a correllary, as many operations as possible should work with all possible string types (Root_String'Class in my formulation) -- most of the time when you need to use multiple string types, its because you have libraries that only work with one type (String, Unbounded_String, whatever) and you need to use some other type. So you end up converting back and forth. If most the libraries simply take Root_String'Class, that doesn't happen. ... >>> That would be very inconvenient. Certainly, less class-wide operations >>> are >>> provided better is the design. >> >> "In"convinient? Why? > > Class-wide operations are inefficient, ambiguous, require type casts (and > thus unsafe). The first I rather understand, but it's the entire point of the change (to get as many operations as possible to work with any string type). If that proves too inefficient, then there is no solution. But I do think it has to be tried in order to determine if its truly too inefficient (worrying about that is often premature optimization). [This is the reason that I don't dismiss out-of-hand your ideas on MI and MD -- because while I think they're likely to be too inefficient, I really don't know that. Of course, since I think as much as possible needs to be class-wide, dispatching performance is very important, and MD in particular would have a substantial cost there.] Ambiguous? That's the idea -- most consumers of strings don't care anything about the properties of the string, just the sequence of code points that it represents. Require type casts? No idea why you think that - that only happens in bad abstractions where you find yourself upcasting to get at components that "you know are there". Such things are just plain bad, and surely aren't going to happen here as nothing of the implementation is going to be exposed. (I.e. either use Root_String'Class, or use a specific type - and under no circumstances should you change mid-stream.) >> 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). > > And the whole point of keeping operation primitive is to have its > interface > generic and the implementation type-specific. We don't want "type-specific" implementations of most things -- that's just adding a maintenance headache where one isn't needed. Why would anyone want a "type-specific" implementation of file open? Or exception messages? That's precisely the cause of the problem that we currently have. ... >> 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. > > There will be no dispatching because types are known statically for most > applications. Secondly, even if dispatching is present it is manyfold more > efficient that dispatching within the class-wide bodies per character > basis, which where your proposal leads. Well, my proposal supports slices as a primitive operation, so there's no reason for dispatching on single characters unless you really need to access single characters. But you're saying that we shouldn't solve the problem at all, since the entire problem is forcing the use of specific types in language-defined libraries (and similarly in user-defined libraries). By definition, most of the operations would be on class-wide types and thus dispatching. You seem so focused on virtually useless operations like Append -- they have no real value in an Ada application, as the primitives (slicing and &) do the job better anyway. One is forced to use them for unbounded string, mainly because you don't have very good access to the primitives for those types -- that's the whole idea of this change. ... >>> 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? > > Append is a procedure. Modifiers of Unbounded_String are in-place > operations, that is the major use case for having Unbounded_String at all. OK, then it takes an in out parameter of Root_String'Class, and an in parameter of Root_String'Class. What's the problem? In any case, Append is a pretty useless operation; the majority of the operations in Ada.Strings are that. It takes longer to figure out how they work and find the right one than it does to just write the silly operation explicitly. It might make sense to make the most general Index primitive (as it's pretty much the only operation that really could benefit from a custom implementation), but that's about it. It's very important that the root interface be kept as small as possible. >>>> 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). > > They should not and cannot. The type String cannot have procedure Append > because String size is contracted to be constant. Immutable bounds is the > major use case of String. Mutable bounds is of Unbounded_String. You > cannot > have both "same." Of course you can. Consider Bounded string, which has a bound, but it's partly mutable. Or better, consider the containers -- they have bounded and unbounded versions. Semantically, they all have bounds, it's just that the unbounded version increases the bound automatically when needed. There would be no problem having a fixed size container (other than such a thing would be redunant with the existing arrays). Append is defined in Ada.Strings.Fixed, after all, so of course it works for a fixed length 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. >> >> Possibly, but then programming language design in general is doomed. The >> only solutions that would work are crazily inefficient. > > Everything should work exactly so it does now, but without explicit type > conversions. Yeah, and every girl should want to go out with you, without an explicit invitation. :-) >>>>>> "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. > > Yes! You want weak typing all the way! Yes, that's exactly the idea of class-wide operations -- one weakens the typing (by allowing classes of operations) in order to get more generality. There is always a trade-off between generality and the strength of typing -- and I surely agree with you that generics don't help in any significant way with that. > In my system the above will be compile error because > > 1. String does not have Append; String *does* have Append (in Ada.Strings.Fixed). Why do you think those operations are part of Unbounded_String but not part of String simply because someone stuck the latter ones in a library? ... >> 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). > > I can, this what a proper types system is for. If you don't change the > type > system you end up with a weakly typed language, which is where Ada sails > to > since 2005. You're delusional on this point. Indeed, you refuse to pay any attention when I point out why your system won't work. You just keep relying on some sort of magic to make it work - and then go on to laughably claim that it would be compatible without a shred of evidence that that could possibly be so. I should know better than to engage with you on anything that involves your crazy type system, because the useful ideas that I hope to find out are completely lost in this impractical, magic reimagining of the type system. I'm hoping to come up with a solutions within the existing type system, because there is absolutely no evidence that there is a problem it. Indeed, the fact that I can come up with a viable all-Ada 2012 solution to the string problem shows that. I really don't care whether or not some other type system that's never going to happen would work differently. And that's the last word on that subject. ... >>>> 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". > > No, it is strengthening of the contracts. You removed beaujolais effects > by > weakening the contracts to the point where separately compiled units could > retain their contracts independently. Strengthening may lead to loosing > this property. In order to "program in the large", separate units have to be able to "retain their contracts independently". Otherwise, it's impossible for separate development teams to work on the same program -- it's those independent contracts that make it safe for one team to add new items and not have it cause the program to change behavior without warning. This is a property of Ada which every Ada programmer depends upon, but since it's not an obvious property, most people don't realize how critical it is. > 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). > > The effect is exactly known - the clients become illegal. That is the idea > of strong contracts. Instead of keeping clients legal but risking > unanticipated behavioral changes, strong contracts make clients illegal > when there is a possibility of changed behavior. If you can statically > prove that the behavior does not change, there is no need to introduce the > "effect." If the clients become illegal, then you don't have Beaujolias, and all is well. But that's not the system you described. You descibed one with preference rules, and when those exist, an addition or deletion potentially causes one legal program to become a different legal program - because the preference rule causes a different routine to be called rather than making the call illegal. That's not acceptable for Ada (and it shouldn't be acceptable for any programming language). >> 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. > > Ada has no support for dynamic linking anyway, so it is how it is right > now. Introducing MD won't make bad any worse. MD isn't the problem; preference rules are the problem. And what does dynamic linking have to do with anything? You should be able to get the new version of GTK or Claw and recompile -- if legal, the program should work the same way. Period. That's not the case in a system with preference rules for user-defined operations. ... >> 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. > > I can build a perfect hash at bind/link time when all involved tags become > known. "can" means "possible", not "practical". That might be possible if the number of tags is small, but that's not the case in a lot of OO systems. (A Claw program starts with somewhere around 80.) Finding a perfect hash gets very time consuming if the number of items is large - I don't think people will be very happy with binding that takes 12 hours. Randy.