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=ham autolearn_force=no version=3.4.4 X-Google-Language: ENGLISH,ASCII-7-bit Path: g2news2.google.com!news1.google.com!border1.nntp.dca.giganews.com!nntp.giganews.com!newsfeed00.sul.t-online.de!t-online.de!news.tele.dk!feed118.news.tele.dk!news.tele.dk!small.news.tele.dk!bnewspeer01.bru.ops.eu.uu.net!emea.uu.net!fi.sn.net!newsfeed2.tdcnet.fi!news.song.fi!not-for-mail Date: Sat, 15 Aug 2009 22:19:41 +0300 From: Niklas Holsti Organization: Tidorum Ltd User-Agent: Mozilla-Thunderbird 2.0.0.22 (X11/20090706) MIME-Version: 1.0 Newsgroups: comp.lang.ada Subject: Re: Access types as parameters References: <521c4843-d40f-4545-9e80-ca725e847090@h21g2000yqa.googlegroups.com> <8410fc60-9b8a-4f82-92fc-622a6bbe5931@i18g2000pro.googlegroups.com> <8880c3d0-a07f-4d4e-ac87-372014598576@d15g2000prc.googlegroups.com> <4a83d018$0$26303$4f793bc4@news.tdc.fi> <4a847400$0$26304$4f793bc4@news.tdc.fi> <4a852df2$0$26317$4f793bc4@news.tdc.fi> <1jrxo2acn8evc.x3wfcmp4etbo.dlg@40tude.net> <4a858af5$0$24774$4f793bc4@news.tdc.fi> In-Reply-To: Content-Type: text/plain; charset=ISO-8859-1; format=flowed Content-Transfer-Encoding: 7bit Message-ID: <4a870a5b$0$26302$4f793bc4@news.tdc.fi> NNTP-Posting-Host: 81.17.205.61 X-Trace: 1250363996 news.tdc.fi 26302 81.17.205.61:50403 X-Complaints-To: abuse@tdcnet.fi Xref: g2news2.google.com comp.lang.ada:7821 Date: 2009-08-15T22:19:41+03:00 List-Id: Dmitry A. Kazakov wrote: > On Fri, 14 Aug 2009 19:03:52 +0300, Niklas Holsti wrote: > >> Or we don't agree on the formalism, which seems to be the case for you >> and me. > > Once people agreed on a formalism there would be no place for > discussions... (;-)) Perhaps in the ideal, yes. But formalisms in "design" are not yet that powerful or unambiguous :-) >>> Why do you dispatch again, you already checked the tag. >> Because this gives me the functionality I want, of course. > > That is clear, but if you say that the functionality you wanted is > re-dispatch itself, that could not justify it. There must be something else > in it, which raises the question of design. You have implemented something > using re-dispatch. My position is that it is a bad way to implement > anything. You disagree... (:-)) Well, you still haven't shown me any convincing reason for calling it a bad design, because I don't accept your "type error" argument. >>> The type is determined, >> Of course not, only the point (within the class) of the implementation >> of the currently executing, (possibly) inherited operation (the caller) >> is determined. The actual type, as you well know, is any type in >> T'Class, although it is written "T" in the operation profile. > > No, the actual type is T, just because the operation declaration tells so. This seems to be the origin of our disagreement. You want to view the object as of type T, although at run-time it may be a T-view of an object of a derived type S. This means that you cannot redispatch. But this does not entitle you to call redispatching "bad" in general. > If you are in the operation inherited by S from T and you don't want it to > be T there, then why do have you inherited and not overridden it in S? > Something must be wrong in it. Not at all, it works perfectly for S. If I had overridden it for S, it would have exactly the same text as for T (except of course for the change in type name from T to S). If the operation did *not* use redispatching, it would *not* work for T as well as for S, and I would have to override it for S. > Are you saying that the reader must be constantly aware that the type of > the actual parameter could be not the type the procedure deals with? Yes, of course. Because of inheritance. >> I suggest that you show how you would design the Divide-and-Conquer >> example that I described in my reply to Randy earlier today, and then we >> can compare the two designs. > > type Subproblem is abstract ...; > procedure Conquer (X : in out Subproblem) is abstract; > type List_Of_Subproblems is ...; -- Container type of Subproblem'Class > > type Problem is abstract ...; > function Divide (X : Problem) return List_Of_Subproblems is abstract; Ok, good for discussing. This works, but has some drawbacks compared to a redispatching solution. Firstly, it needs new data structures (Subproblem and List_Of_Subproblems) which may require dynamic memory allocation if the Subproblem type is complex. Secondly, it is no longer possible to stop the Divide operation when the first solvable Subproblem is discovered. Thirdly, you now need a class-wide operation that invokes Divide and then Conquer for the given object of type T'Class. Finally, a point that is not a drawback in my view, but should be in your view (as I understand it): there is still no guarantee that the actual invoked Divide operation and the actual invoked Conquer operation are defined for the same type; the actual T'Class object might inherit Divide from type T, and Conquer from type S. Logically (but perhaps exaggerating a bit) you should consider this a type error because it is mixing the semantics of T and S. I'm sure you don't see this as a problem, probably because you assume that the semantics of T and S are sufficiently "compatible", being all in T'Class. But this is just the reason why I don't think that redispatching from Divide (T) to Conquer (S) is a type error. > A better case for re-dispatch is an operation defined in terms > of other operations. Well, I do think that Divide, in my example, is defined in terms of other operations, namely Conquer, because Divide (in my design) finds the subproblems and calls Conquer on each subproblem. Perhaps I should have called the operation Divide_And_Conquer to make this clear in the name, sorry. > For example x*n defined as x+...+x, n times. you want to have a > standard version of *, but in some cases to have an ability to > implement it specifically. I think that is quite similar to the Divide/Conquer example. > I am using a sort of this: > > procedure Add (X : in out Item; Y : Item); > procedure Mul (X : in out Item'Class; N : Positive); > private > function Has_Accelerated_Mul (X : Item) return Boolean; > procedure Accelerated_Mul (X : in out Item; N : Positive); > > procedure Mul (X : in out Item'Class; N : Positive) is > begin > if Has_Accelerated_Mul (X) then > Accelerated_Mult (X, N); Shudder (my turn :-). This is a kind of manually implemented overriding, where Accelerated_Mul sometimes "overrides" Mul. I much prefer to use the language-defined overriding of primitive operations, so I would make Mul primitive on Item, instead of class-wide, and override it with an accelerated version for those Item'Class types that allow it. This is probably faster, too :-) One problem in your design is that someone can now mistakenly call Accelerated_Mul (X) even if Has_Accelerated_Mul (X) is False. Moreover, Has_Accelerated_Mul seems to depend on the particular object (X), not just on the type (Item). Or was this your intention? > My empirical condition for all dispatching operations is that type tag > resolution should move strictly towards the root, and never backwards. I.e. > if you have a tag for T'Class,, you can narrow the set of candidates > ultimately to the single type, but you never widen the set. This rule defines a subset of Ada that you want to use. OK, but so far you have not convinced me that there is any benefit in this subset. We are all struggling to find the small nuggets of correct programs hidden in the vast rockpile of incorrect programs. I can believe that your empirical condition is helpful to you, but I have not found any problems with redispatching. I suggest that we agree to disagree and close here. -- Niklas Holsti Tidorum Ltd niklas holsti tidorum fi . @ .