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!feeder.eternal-september.org!aioe.org!.POSTED!not-for-mail From: "Dmitry A. Kazakov" Newsgroups: comp.lang.ada Subject: Re: Studying and Maintaining GNAT, Is There Any Interest in a New Group? Date: Fri, 31 Aug 2018 10:48:25 +0200 Organization: Aioe.org NNTP Server Message-ID: References: <309225242.556906218.575482.laguest-archeia.com@nntp.aioe.org> <2145221813.556924687.162377.laguest-archeia.com@nntp.aioe.org> <3892c779-2924-405c-b88d-19389fc5ba3e@googlegroups.com> <1ceec6d8-c5c4-49b1-9808-a3580bba3f8e@googlegroups.com> NNTP-Posting-Host: MyFhHs417jM9AgzRpXn7yg.user.gioia.aioe.org Mime-Version: 1.0 Content-Type: text/plain; charset=utf-8; format=flowed Content-Transfer-Encoding: 7bit X-Complaints-To: abuse@aioe.org User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:52.0) Gecko/20100101 Thunderbird/52.9.1 Content-Language: en-US X-Notice: Filtered by postfilter v. 0.8.3 Xref: reader02.eternal-september.org comp.lang.ada:54302 Date: 2018-08-31T10:48:25+02:00 List-Id: On 2018-08-31 01:25, Randy Brukardt wrote: > They are primitive. That's what forces writing the traversal code into every > operation. Consider the following node: > type Root_Node is abstract tagged null record; > type Expr_Tree_Access is access Root_Node'Class; > > procedure Fold (Expr : in out Root_Node); -- Fold the tree. > > type Binop is new Root_Node with record > Kind : Op; > Left, Right : Expr_Tree_Access; > end record; > > and the implementation of the Folding operation: > overriding > procedure Fold (Expr : in out Binop); > > procedure Fold (Expr : in out Binop) is > begin > Fold (Expr.Left.all); > Fold (Expr.Right.all); > -- If Expr.Left and Expr.Right are constants, calculate the > folded value. > end Fold; > > This operation has to do recursive (dispatching calls) on the children. And > so does every other such operation that gets defined (dozens). Why isn't Fold class-wide: procedure Fold (Expr : in out Binop'Class) is begin Fold (Expr.Left.all); Fold (Expr.Right.all); Expr.Evaluate; -- This dispatches to the Op's implementation end Fold; BTW, I prefer to fold constants before generating the tree. I.e. when Evaluate is called it returns either a value ("constant" node) or a new "dynamic" node. >> As a general observation, anything you can do with subtypes of a single >> type you can also do with a class of a types hierarchy. [There could be >> some obstacles regarding indefinite vs definite types, but they usually >> apply to both designs.] > > This is true, you just have to write 3 times as much code to make the OO > design work. And you lose the ability to do agile programming, because you > have to implement so much new code to add a new node or operation (probably > on the order of a week for either in an Ada compiler perspective -- dozens > of operations and dozens of node types are needed there). You would possibly save a comparable number of source lines in clients. >>> Umm, we're talking about a compiler, and there never is a "final" version >>> of a compiler. >> >> Final = deployed, of course. > > Which is relevant how? It's just a snapshot of a continual evolution. Each new release is another program. You might kill the whole package next time, the code is not stable. With OO I can have stable pieces of code frozen in separate packages which never change and keep adding volatile code without rewriting everything each time. > ... >>>> Note that inheritance would handle this safely. It will be: >>>> >>>> Current.Token.Do_Something; -- A dispatching call override >>>> >>>> Do_Something would be abstract must-be-overridden for Float_Token. >>> >>> Sure, but that's what's maddening about using OOP in this way. You have >>> to >>> define and implement *every* such operation that way for a new node >>> before >>> anything can be done (even compiling the changed program). >> >> Exactly. This is called being typed. Operations do not exist without >> types. The design with case-statements spread all over the client code is >> untyped because chunks of client code under "whens" are not attributed to >> any type, have no contract etc. OO allows us >> >> 1. To give a contract to this code >> 2. To refactor and reuse this code >> 3. To place it in separate compilation units *related* to the types >> involved. > > (1) There is no useful contracts for individual nodes -- all of the > contracts with any meaning are on the tree as a whole. And it is the state > of the nodes, rather than their kinds, which figure mainly into those > contracts. (The state being part of the data, not part of the types.) But when you fold constants you do that according to the types of the nodes. The question is if you try to map these types onto Ada types or not. > (2) Refactoring isn't any harder with a variant design than it is with a OO > design -- I do those sorts of things all of the time. I find using free subprograms more difficult. They have no natural place to keep in. So I always forget which package holds this or that refactored subprogram and end up with several instances doing basically same thing. > You would need so many getter/setter routines that you > would be spending 50% of your time adding/fixing them rather than doing > anything useful. In my experience getter/setter tend to be stable. My problem is that they pollute package specification. This is one of the reasons why I'd like to have record interfaces: type Getters_Setters is interface record Field_1 : Integer; -- "Virtual" record field ... end record; type T is ... and Getters_Setters with ... The type T need not to contain Field_1 physically. If it does not one should override getter and setter (in the private part). > We've gotten a lot of mileage out of using aggregate > assignments to ensure completeness rather than trying to make everything > private. It was wasting time, IMO. User-defined aggregates would be much simpler to add without burdening the language with dubious rules and counterintuitive constructs. > Which does not answer the underlying question: how do you do agile > development if adding an operation or new kind of node requires 4 days of > coding? I don't buy agile if that means generating false code. When I add an abstract operation I want the compiler to generate error messages the same way it does when a new case choice is added. BTW, I feel uncomfortable about Ada 2005(?) change that a primitive function returning the type need not to be always overridden. > For Ada at least, the only meaningful data structure is the expression tree. > We don't care about the individual nodes outside of a handful of tree > walking operations. Most everything talks specifically about the tree. I'd like to have it reverse, hiding walking and exposing semantics of the nodes. > In any case, I don't expect ever to agree with you on these points. I think > a lot of people take strong typing too far -- but of course the sweet spot > is hard to find. There may be an argument for going further than I do, but > my experience doesn't show much value to that. True, I want to push typing and static verification in general as far as possible. Nothing is too far to me. (:-)) -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de