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.7 required=5.0 tests=BAYES_00,INVALID_DATE, MSGID_SHORT,REPLYTO_WITHOUT_TO_CC autolearn=no autolearn_force=no version=3.4.4 Path: utzoo!utgpu!watmath!clyde!att!ucbvax!decwrl!purdue!rjh From: rjh@cs.purdue.EDU (Bob Hathaway) Newsgroups: comp.lang.ada Subject: Re: Procedure types and dynamic binding Message-ID: <5804@medusa.cs.purdue.edu> Date: 9 Jan 89 20:19:55 GMT References: <5796@medusa.cs.purdue.edu> <4046@hubcap.UUCP> Sender: news@cs.purdue.EDU Reply-To: rjh@cs.purdue.edu (Bob Hathaway) Organization: Department of Computer Science, Purdue University List-Id: > In more complicated examples parameterizable operations for each Adt > instance could simplify the program considerably, such as by avoiding > nested case statements. > Bill Wolfe responds: > But you've designed your ADT to handle only two types of printing > situations: SHORT_PRINT and LONG_PRINT (or at least this would seem > to be the case to anyone who takes your generic parameter structure > at face value). What we REALLY want is for ANY type of printing > operation to occur, depending on the user's needs, which we cannot > forecast in advance. But now look at the trap we've walked into: > we must provide MAKE_*_PRINT, where * represents an indefinite set > of cases. We now see that our old friend, the case statement, is > not gone; it has simply taken on a new form. > This is not entirely correct, since I commented another more general solution. It is possible to parameterize the Adt with a generic MAKE_PRINT_PROCEDURE as described in the comment which I'll repeat below. This is accomplished with a nested generic subprogram declaration in the visible part of the package specification allowing any visible type compatible procedure to be passed to a "MAKE_PRINT_PROCEDURE". This satisfies the constraint above by dynamically assigning the generic parameter to the internal procedural variable, if desired. -- -- MAKE_SHORT_PRINT -- -- Set procedural instance variable PRINT_ELEMENT to parameter -- SHORT_PRINT so subsequent calls to PRINT by this instance ** Please Note *** -- will invoke SHORT_PRINT. Could also use a generic -- subprogram parameter to supply a new print routine, or ... *** *** -- Parameters SHORT_PRINT and LONG_PRINT will have to be assignment -- compatible with type PRINT_PROCEDURE_TYPE; -- Also, it appears unrestricted pointers to subprograms, if thats what you propose, would provide dynamically checked operations at runtime. This mechanism is more powerful than statically checked procedural variables. Analogous to the use of generics and arbitrary polymorphism where generics should be used whenever applicable and arbitrary polymorphism with care, procedural variables should be used whenever applicable and unrestricted pointers with care. Since the previous example used generics, corresponding procedural variables are applicable and provide an elegant general solution. I'll let Harland, if we're lucky enough to have him read this, provide an example with arbitrary polymorphism and unrestricted pointers:-) Mats Weber writes: >I think there is one good reason why procedure types should not be included in >the Ada language: A secure implementation of procedure types must restrict >their use to global procedures (that is, procedures that are not nested in >other procedures or tasks), as is the case in Modula-2. > >Such a rule would violate the uniformity of the language (in Ada 83, anything >can be declared in any declarative region, no matter how deeply nested the >region is). I'm aware of this argument but after several years of Modula-2 programming this simply doesn't cause concern. This is like the "else" construct argument where disambiguating rules must be applied to allow if-then-else constructs. It is not consequential that rules must be applied to grammars, as with the else construct, or that semantic rules must be applied to objects, as with procedural variables. As a programmer, the only procedures I would assign to a procedural variable are top-level procedures and since the nested case doesn't occur, I don't see a problem. I agree constraints restricting orthogonality should only be applied when necessary but procedural variables present such a case in a stack based-model since assignment could allow access to inactive environments global to a procedure and the assignment restriction to top-level procedures is safe, appropriate, and general. Also, the rule for disallowing procedural variable assignment to nested subprograms which are passed as generic subprogram parameters must still be statically checked. Bill Wolfe writes: > We now see that our entire approach to the problem is wrong. We must > provide a method whereby our user can access his/her stacked objects, > and allow the user to implement any arbitrary print procedure. But, > you object, the user must be given some sort of standard print procedure > which can be used as a default! And so he/she must; we achieve this > by supplying in a utilities package the following: > But this is the old scheme again since the Adt cannot be parameterized at any point in the program and allow a single subsequent operation to invoke the desired subprogram. This sheme requires the work of writing an output procedure inline. Yes, the user can call MY_PRINT (STACK .POP ...) for maximum flexibility as they can with the presented example but this was not desired. The output example was kept simple to abstract the desired method and real applications are easy to construct, as shown below. > Still waiting for a situation in which procedural variables should > be used in order to formulate an intuitively natural solution... Graphical environments provide one example where dynamically parameterizable objects can be of great assistance. One could parameterize graphical objects with operations for displaying output, windows, etc., with each instance using the correct subprogram to implement its display operations. Bob Hathaway rjh@purdue.edu