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!mx05.eternal-september.org!feeder.eternal-september.org!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: Fri, 5 Apr 2013 20:20:58 -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> <1bj564vat3q1j$.1s4d00rlzx4ux$.dlg@40tude.net> NNTP-Posting-Host: static-69-95-181-76.mad.choiceone.net X-Trace: munin.nbi.dk 1365211262 586 69.95.181.76 (6 Apr 2013 01:21:02 GMT) X-Complaints-To: news@jacob-sparre.dk NNTP-Posting-Date: Sat, 6 Apr 2013 01:21:02 +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:14883 Date: 2013-04-05T20:20:58-05:00 List-Id: "Dmitry A. Kazakov" wrote in message news:1bj564vat3q1j$.1s4d00rlzx4ux$.dlg@40tude.net... > On Thu, 4 Apr 2013 15:31:23 -0500, Randy Brukardt wrote: > >> "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. > > It is not same. R and Z are different mathematical constructs. Unicode > strings, all of them, are semantically exactly same except for encoding > [representation] and constraints put on the code point set. We're not talking about "Unicode strings", whatever that is. We're talking about an abstract string type, with different sets of characters, not necessarily overlapping. Automatically converting Wide_String into String is unsafe as it loses information, and that's even more true once you start thinking about other character sets. I'm sure that there are cases where you can get away with automatic conversions, but you don't need that and you certainly can't do it all the time. So why support it at all? >> 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. > > If that were so, you should not have them in the same hierarchy. You want > to have one class in order to share operations like Put_Line. These > operations are far more questionable than comparison. If you challenge > comparison, you should not do Put_Line either. To some extent, you are right. If you can't convert the value into some universal representation, then you can't implement class-wide operations, either. And that suggests that the entire idea is bad. Indeed, that indicates that all OOP is bad because it destroys type-checking. Perhaps I should forget that I ever heard of it?? But in any case, there are two important differences here: single operand operations vs. multiiple operand operations, and the declaration (by using 'Class) that you are explicitly not caring about type. Perhaps it is orthodoxy, but I don't believe that you should ever mix types without explicit conversions, meaning that only single operand operations can have 'Class operands. (I can't think off hand of any cases where we ever had more than one operand of a class in a Claw operation, but I may have forgotten something.) > You design decisions are motivated by compiler implementation issues not > by > the actual properties of strings. If you can't implement it, no mattter how wonderful some idea may be, it's still irrelevant. ... > Now this: > > function Get_Text (Control : Edit_Box) return Root_String_Type; First, this is illegal because Root_String_Type is abstract, and you can't have a function returning an abstract type. Second, you can't implement that in Claw (or Ada), because the Edit_Box is an "in" parameter and you need to modify some of its components in order to deal with the state changes of the edit box. So you have have to have something like: function Get_Text (Control : in out Edit_Box) return Root_String_Type'Class; But this is very different than the procedure form. In the procedure case, the object passed in determines the type and representation of the result. In the function case, the function returns whatever type it feels like, and just about the only thing you can do with it is assign it to an object or pass it to a class-wide parameter. So this is trivial to implement: return any old new object that you like. Probably here again you want to totally redesign Ada, but that isn't very likely. >>>>> 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. > > No more than 20-30% actually. OK, it's 95% of the cases that occur in actual practice, with the possible exception with people that overuse OOP constructs and make their systems more complex than needed. :-) ... >> Mixed operations are almost always a non-starter for any type. > > Mixed operations are a must for all algebraic types and all string types. > Ada 83 had it mixed from the start, e.g. Universal_Integer vs. Integer. > Ada > 95 added mixed operations for access types. Actually, those aren't really mixed, as Universal has the same relationship to other types as 'Class does. In some ways, these act as separate types, but given the automatic conversions they really aren't that - they're a set of types that includes the associated specific types. ... >> 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. > > I don't know where you get that idea. I never had any problem with > abstract > factories. Well, I have. As I previously explained, the abstract routines and functions in the Claw Builder root types make it so time-consuming to create an extension (the last one I did took around 20 hours to write, and even then I never finished it) that it's impractical to do so. If you have only one such routine, there is no significant problem, but you have to greatly limit the number or extensions are impractical. The only practical extensions inherit virtually all of their implementation (at least initially) from their parent. [This is how Claw itself works, it's a lot easy to deal with than the builder is.] It's a major reason why I don't believe in interfaces, as the effort to create implementations for every included operation is just too much of a "Big Bang" for my programming style. (If I can't have something compilable and testable in a few hours, I get very uncomfortable, because that is the only way to detect gross mistakes -- otherwise, one can waste days building something that doesn't work.) > Actual problem is fighting senseless limitations imposed by the > language, which makes design very hard as you never know if the derived > type would be possible to construct. Limited aggregates/functions, broken > initialization/finalization, leaking separation of specification and > implementation, missing initialization/finalization of class-wides etc. > That makes it difficult. Not sure why most of those things would cause any real issues, as most of them give more, not less, capability. But it isn't worth discussing that. >>> 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. > > When the type neither specified as either by-reference nor as by-value, it > shall be exactly this way. I don't understand why this is relevant. > >> It would make performance wildly variable and possibly cause bugs (if the >> program unintentionally depended on one or the other. > > Yes, the language permits compilers implemented poorly, and? This has nothing to do with compilers. It has to do with *poorly designed programs*. It's certainly true that a program that depends on by-copy or by-reference passing is wrong; the compiler should be free to choose. But this is an undetectable error. And bringing Adjust into it makes it much worse, because it's easy to write code that doesn't work if Adjust is called (you probably can get infinite recursion that way). That sort of thing can't be implementation-defined. ... >>>> 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. Here you are saying that you can't redispatch because you insist on a model that doesn't allow it. I don't think that is a very good case. :-) You have to justify eliminating re-dispatch, then, and only then, can you chose a model that doesn't allow it. >>>> 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; > > No. E.g. task and protected types lack type identity. Only tagged types > have type identity. > > Note that type identity /= object identity (e.g. machine address where the > object resides). I don't think that they ought to be separated. If you have object identity, and every object has a nominal type, it does not make sense to lose that information. There are no values of a limited type, only objects. I see your point for non-limited values, as the type of a value is not necessarily determined. But that's not true for objects. Here again, you are justifying your model by simply eliminating a property that all objects have and saying that I can't fit that into my model so I won't. >>>> 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?? > > I don't introduce anything Ada 83 or 95 do not have already. I want to > lift > meaningless language limitations which lead to silly design decisions like > inability to concatenate or compare two strings. That's a feature, not a bug. :-) And it's one I would be extremely surprised to see changed in Ada (or any other strongly typed language, for that matter). >>> 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. > > Encoding! You have to reimplement *every* operation for a new representation (in your model as well as in existing Ada). So you are gaining absolutely nothing over using interfaces for any added operations. You need to have a more complete example than that if you want to convince me of anything. Randy.