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 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.106.161 with SMTP id gv1mr3499547wib.4.1364940230413; Tue, 02 Apr 2013 15:03:50 -0700 (PDT) Path: ex12ni19365wid.1!nntp.google.com!feeder1.cambriumusenet.nl!feed.tweaknews.nl!194.109.133.87.MISMATCH!newsfeed.xs4all.nl!newsfeed1.news.xs4all.nl!xs4all!newspeer1.nac.net!border4.nntp.dca.giganews.com!border2.nntp.dca.giganews.com!nntp.giganews.com!news.bbs-scene.org!eternal-september.org!feeder.eternal-september.org!aioe.org!.POSTED!not-for-mail From: "Dmitry A. Kazakov" Newsgroups: comp.lang.ada Subject: Re: Is this expected behavior or not Date: Sat, 30 Mar 2013 10:20:37 +0100 Organization: cbb software GmbH Message-ID: <1gnmajx2fdjju.1bo28xwmzt1nr.dlg@40tude.net> References: <1hvv2kd9smnfx.6spgz9thd1mh$.dlg@40tude.net> <1raubw1sk48ca$.69rdgczvnnf.dlg@40tude.net> <2qwq2cdeuvhu$.qtnb8zyhuob9$.dlg@40tude.net> <1u72u7h5j4jg3$.wlxmaltyzqik.dlg@40tude.net> Reply-To: mailbox@dmitry-kazakov.de NNTP-Posting-Host: XRUMb5xlbonTNodERpEXEw.user.speranza.aioe.org Mime-Version: 1.0 X-Complaints-To: abuse@aioe.org User-Agent: 40tude_Dialog/2.0.15.1 X-Notice: Filtered by postfilter v. 0.8.2 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Date: 2013-03-30T10:20:37+01:00 List-Id: On Fri, 29 Mar 2013 19:49:25 -0500, Randy Brukardt wrote: > "Dmitry A. Kazakov" wrote in message > news:byacn0wck9qt$.norrlukaw80l$.dlg@40tude.net... >> On Thu, 28 Mar 2013 16:55:34 -0500, Randy Brukardt wrote: >> >>> "Dmitry A. Kazakov" wrote in message >>> news:cqf0yy6q930$.1sdzw45bc0c1w.dlg@40tude.net... >> >>>> These are two data types, string and slice. Are you treating slice as an >>>> Ada-subtype of string? I consider slice a member of same class as string, >>>> yet a distinct type (in Ada sense). This is like 4.1.5, but in the same >>>> type hierarchy. >>> >>> I thought you might do that, but then you're going to have problems of >>> compatibility (Ada currently considers them the same type). An operation >>> like "&" cannot not allow mixed string operands (if you do allow that, >>> you make string literals ambiguous). >> >> Right, if strings form a hierarchy, then "&" must be primitive and a >> multi-method. There is no alternative to that. Even if you are going to >> add a completely new set of string types without slices you still will have >> this problem: >> >> declare >> X : New_String_Type; >> Y : A_Descendant_Of; >> begin >> ... X & Y ...; -- Is this legal? Does it raise Constraint_Error? >> >> The answers must be yes, no. And for this "&" must be a multi-method with >> all combinations of argument and the result types defined. Making some of >> them class-wide could reduce the number of combinations. And it will be >> ambiguous with literals and other overloaded operations returning string >> objects. > > Well, actually, the answer is "no, irrelevant" as it is for the existing Ada > types. You can always use an explicit type conversion if you need these to > match. If each type effectively has its own literals, then this works > perfectly. Mixing types is precisely the sort of thing you don't want to do > in a strong typing environment. > > I think this problem illustrates why the schemes you are thinking of will > never be used -- they just add far too much complexity to the language. 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. >>>>> Nobody wants "type cloning". >>>> >>>> It is a very useful building block to me. Anyway it is Ada 83 legacy. >>> >>> Ada 83 derived types are virtually useless, and no one understood how >>> they worked. >> >> What would you do if you need a new type with same properties. Copy-paste? > > Make a new declaration, using constants for the properties. Properties are operations here. > But the real > answer is that this never happens, so it's a bogus concern. If the > properties are truly the same, it's hardly likely that the type is the same. type Velocity is new Float; >> Example: >> >> type Position is ; >> type Key is some ; >> type Container is ...; >> >> How do you prevent Position and Key mixed in Container operations if they >> belong to same hierarchy? > > If the container operations take parameters of type Key, then you can only > pass a Position to such a parameter if you use an explicit conversion. Position and Key are just same type according to you. You said you don't need independent hierarchies of any types, which should include numbers. >> The model of Ada 95 tagged types provides a class with shared >> representations. This need to be augmented for the case when >> representations has to be different. Note that this was partially done when >> Java interfaces were added in Ada 2005. Java interface is a type without >> any representation, so there is nothing to inherit from, except for a slot >> for the type tag. Drop that slot, and you will have a workable model for >> building classes of scalar types and arrays. > > I don't know how one could implement "interfaces" without a tag. You have the tag in the representation of untagged'Class. You don't have it in the representation of untagged. >>>> We also need "extension" that drops parent's representation altogether. >>>> E.g. something like: >>>> >>>> type Character is new Unsigned_8 -- or enumeration, whatever >>>> and Wide_Wide_Character'Interface; >>>> >>>> Character is member of Wide_Wide_Character'Class with a representation >>>> different from Wide_Wide_Character. >>> >>> I don't think that would work semantically, at least in the 'Class case. >>> Unless you expect every operation down to assigning individual components >>> to be dispatching, even in primitive routines defined for a specific type. >> >> Exactly! I want all that mess to become orderly defined primitive >> operations. This also includes attributes and aspects. There should be no >> special cases like T'Read, ":=" etc. There should be no magic and strange >> stuff like attributes, slices, ranges, aspects infesting the language, >> only objects and operations. > > You miss my point: the effect is to make *every* operation in *every* > subprogram -- including specific ones -- dispatching -- which would be way > too expensive in practice (no optimization would be possible). Then it is wrong what you wrote. There will be no dispatch on specific types. (I don't understand why you think that dispatching on specific types is necessary) > You are still misunderstanding. You'd *have* to have redispatch because the > only way to implement inheritance would be to make all operations (even > those very low-level ones) dispatching in *all* instances. There could be no > statically bound operations. Yes, I still do not understand what has re-dispatch to do with inheritance (of what?) The representation is NOT inherited Primitive operations (AKA, interface) are inherited in the way standard to tagged types. That is, a NEW operation is declared with the same name and the profile where the parent type is replaced by the descendant type. The way its body is generated out of old body with parent type converted to the descendant type in- and/or out. How this requires re-dispatch? >> For copyable objects implementation inheritance is much less interesting >> than for by-reference types. Operations (especially cross operations) need >> to be re-implemented anyway, at least for performance reasons. Few other >> operations are [conversion ->] inherited body [-> conversion]. > > Not all objects that have different representations are copyable. (Consider > an array of some_task'class). Whatever solution one has for untagged types > has to be able to deal with uncopyable objects with different > representations. > > But banning implementation inheritance makes the whole thing useless IMHO, > and supporting it is too expensive. I don't ban inheritance of the representation. I want to be able not to do it when I don't need it, e.g. for character and string types. > 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! >>>> Making it regular is a simplification. >>> >>> This isn't regular. All numeric types are currently members of a single >>> hierarchy, >> >> It is not a single hierarchy. It is a forest of similar hierarchies. They >> are similar just because they were constructed by the compiler rather than >> by the programmer. It is nominal vs. structured. Same structure does not >> imply equality, at least, it is meant to be so in Ada. > > No, you need to read the RM again. All integer types are effectively derived > from Universal_Integer, and all of the rest are derived from Universal_Real. > There are only two hierachies. OK, this again becomes a scholastic discussion we need not to go into. The bottom line is, I regard Ada 83 construct "type S is new T;" as very useful, and wished it be valid with all specific types without silly exceptions Ada 95 made for tagged types. >>> and in practice, the vast majority of tagged types are members of >>> two hierarchies (controlled and limited controlled). >> >> So what? This is equivalent to say that a type can be either copyable or >> else not. Yes, "copyable" is an interface. There is a countable infinity of >> other interfaces a type may implement. Which is why multiple inheritance >> is so important. > > You mean why it is so useless. If you break down every possible property of > a type as an interface, you'll end up with a very expensive complex > structure. It is how it is. A number is a field, additive group, weak ordered and so on... A good language shall support programmer in expressing concepts of the problem domain. > The only way I know of to implement multiple inheritance is with > a linked-list of tags, which isn't too bad when only a few interfaces are > involved. But once you get the dozens you're talking about, it's going to > cause a very significant overhead. There is no overhead on any existing Ada program. >> But even if two types implement the same interface, e.g. can be copied, >> that by no means should automatically imply that these types must belong >> to the same hierarchy. This decision is up to the programmer. > > I suspect that *all* types should belong to the same hierarchy, if you are > going this OOP route. To make yet another bogus OOPL out of Ada? > Because you need operations that work on every object, > copyable or not. Remember, there are no type conversions between types in > different hierarchies. And, this is what different hierarchies are for. >> Type cloning is a valuable mechanism to insulate type hierarchies against >> each other. >> >> Consider a container type defined for Copyable'Class. How do you prevent >> mixing apples and oranges in there? Creating two new container types for >> copyable apples and copyable oranges? > > Of course you have separate containers for each kind of thing that they > contain. When you define a container for Copyable'Class, you are allowing > anything non-limited in that container -- you've specifically decided not to > enforce any sort of checking on the contents. And cloning is there to enforce checking. >> But they are still the same >> hierarchy. You need some fundamental operation which tells that a type >> breaks off the hierarchy. > > Yes, of course all containers ought to be members of the same hierarchy. How > else would you create generic algorithms? Easily. Cloning borrows all operations. Define algorithm on the original type. Then clone it. Done! >>> As soon as user-defined things are involved, preference rules simply >>> don't work. >> >> Some rules are clearly needed to choose between: >> >> function "&" (Left : String; Right : Wide_String) return Wide_String; >> function "&" (Left : String; Right : Wide_String) return String; > > There are no such operations predefined in Ada, and that's because you > *can't* chose between them. You can of course declare them yourself (that's > essentially what happens in Ada.Strings.Unbounded), but the net effect is > that you then cannot use literals with such a type. Ada does not attempt to > prevent you from shooting yourself in the head. :-) Are you seriously going to propose a string class without "&"? >> My take on this that the language should prevent situations when you have >> an object with some of its operations invisible, especially, when these >> operations override the same primitive operation. I think this should be >> possible to do while keeping it backward compatible, because standard >> types are always visible anyway. > > Huh? This is very common with private types. With interfaces, we enforced > such a rule and it is rather limiting. > > Claw uses hidden operations on window objects to keep the mechanisms of the > message loop hidden away. I didn't meant that. Whether public or private an operation on the object should be visible if you would see it otherwise, e.g. through use-clause. >>> My contention remains: if you are going to allow user >>> "Handle.all", then you need to expose the type of Handle.all. If you don't >>> need to allow that, then you are not going to use 4.1.5 at all. So I fail >>> to see your concern. >> >> I don't need Handle.all, but I do need Handle.Foo meaning Handle.all.Foo. >> Publicly it is an opaque by-copy type, privately it is an access type. > > In this case, you're probably not using 4.1.5, because it is only about .all > (and writing into .all specifically). Yes, 4.1.5 is a hack for one specific case. >>> Unless you want to drive >>> yourself nuts with OOP baloney - and it's never going to be possible to >>> do these sorts of checks with type checking. >> >> I don't understand why it has to be impossible to have: >> >> type File is ...; >> >> type Read_File is new File ...; >> prcodure Read (X : in out File; Data : out Stream_Element_Array); >> >> type Write_File is new File ...; >> procedure Write (X : in out File; Data : Stream_Element_Array); >> >> type Readwrite_File is new Read_File and Write_File ...; >> >> with concrete types, but well possible with interfaces. >> >> The nature of the check that the operation Write is not applied to >> Read_File is exactly same in both cases. > > You can do this sort of thing in exceedingly simple cases. But it simply > gets too complex the more properties that you try to decompose on. And every > such property is making all of your dispatching calls slower and slower. I would gladly buy that in exchange for how it is now: 100+ generic packages, 1000+ instances, 4 hours compilation time, no way to compile on a 32-bit host. > Moreover, most interesting properties are not binary like the file mode. > They tend to depend on the *values* of parameters, and often on combinations > of values. It just is too complex to do. It is becomes increasingly expensive not to do it. There is a language damage as well inflicted by all hacks of Ada 2005-2012. Further hacks are in the pipe line, I guess. >> It is not about checking, especially because the above construct is still >> possible to get through generics. It is just a stupid limitation motivated >> by urban legends mindlessly repeated through (using you wording) >> OO-baloney >> texts. > > Implementability is hardly an "urban legend". If the resulting code is too > slow to use I saw no significant overhead in C++ programs, which has full MI from the start. -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de