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 X-Google-Thread: 103376,99ab4bb580fc34cd X-Google-Attributes: gid103376,public From: ok@goanna.cs.rmit.edu.au (Richard A. O'Keefe) Subject: Re: Q: access to subprogram Date: 1996/07/22 Message-ID: <4svdk6$6qu@goanna.cs.rmit.edu.au> X-Deja-AN: 170004750 references: <4rb9dp$qe6@news1.delphi.com> <4re2ng$t7u@wdl1.wdl.loral.com> <4rqbo9$b02@goanna.cs.rmit.edu.au> <4rvnbr$amu@goanna.cs.rmit.edu.au> <4snh9b$2tl@goanna.cs.rmit.edu.au> organization: Comp Sci, RMIT, Melbourne, Australia nntp-posting-user: ok newsgroups: comp.lang.ada Date: 1996-07-22T00:00:00+00:00 List-Id: rogoff@sccm.Stanford.EDU (Brian Rogoff) writes: [about nested procedures as parameters of other procedures] >I guess I would ask Pascal programmers how useful they found this, and whether >any interesting idioms emerged which used this feature. As an old Algol/Pascal programmer, I would answer "very useful". I don't quite grasp the intent of the "any useful idioms" question. Procedures as parameters are an abstraction mechanism. Hen's teeth, even Fortran has them. Why? So you can build abstractions like "integrate over an interval"; "Monte-Carlo integration in N-dimensional space"; "find the minimum of a function over an interval"; "find the mimimum of a function in N-space" (Nelder-Mead, for example); "solve ODE"; ... Now it _is_ possible to do a lot of this using Ada generics. Let's consider one special case, and I'll use Scheme syntax. (define (integrate-1D L1 U1 F) ; compute and return ; integral F(x) dx for x in [L1,U1] <> ) (define (integrate-2D L1 U1 L2 U2 F) ; compute and return ; integral F(x,y) dx dy for x in [L1,U1] y in [L2,U2] (integrate-1D L2 U2 (lambda (y) (integrate-1D L1 U1 (lambda (x) (F x y)) )) )) The Pascal (ISO 10206) equivalent would be function Integrate_1D(L1, U1: Real; function F(x: Real): Real): Real; (* some locals *) begin (* some body *) end; function Integrate_2D(L1, U1, L2, U2: Real; function F(x, y: Real): Real): Real; function Inner(y: Real): Real; function Curried_F(x: Real): Real; begin Curried_F := F(x, y) end; begin Inner := Integrate_1D(L1, U1, Curried_F) end; begin Integrate_2D := Integrate_1D(L2, U2, Inner) end; The Ada equivalent of this would be generic with function F(x: Float) return Float; function Integrate_1D(L1, U1: Float) return Float; function Integrate_1D(L1, U1: Float) return Float is -- some locals begin -- some body end; generic with function F(x, y: Float) return Float; function Integrate_2D(L1, U1, L2, U2: Float) return Float; function Integrate_2D(L1, U1, L2, U2: Float) return Float is function Outer_Integrand(y: Float) return Float is function Inner_Integrand(x: Float) return Float is begin return F(x, y); end Inner_Integrand; function Inner_Integrator is new Integrate_1D(F => Inner_Integrand); begin return Inner_Integrator(L1, U1); end Outer_Integrand; function Outer_Integrator is new Integrate_1D(F => Outer_Integrand); begin return Outer_Integrator(L2, U2); end Integrate_2D; [Layout chosen to give Scheme no unfair advantage with respect to number of lines needed. I would normally lay the Ada version out exactly the way that AQAS guidelines recommend.] In the Scheme version, only the top-level procedures need names. In the Pascal version, the lambda-expressions have to be given names; there is nothing in the Pascal model which actually *requires* this; Pascal *could* be extended with context-sensitive lambda-expressions so as to allow function Integrate_2D(L1, U1, L2, U2: Real; function F(x, y: Real): Real): Real; begin Integrate_2D := Integrate_1D(L2, U2, lambda (y) Integrate_1D(L1, U1, lambda (x) F(x, y))); end; without requiring any backend changes. In the Ada version, not only do the *arguments* of Integrate_1D need names, so also do the *calls*. In the Scheme and Pascal versions, there is one copy of Integrate_1D which is called twice. In the Ada version, at least as compiled by "gcc -S -O" (gcc 2.7.2, gnat 3.04, SPARC Solaris 2.5), there are two copies of Integrate_1D. The optimised SPARCompiler Pascal code for a program using this is in fact about half the size of optimised GNAT Ada code. For this example, it doesn't seem so important, but consider larger examples, and consider caching effects. Most of all, consider the effect on program style and clarity. Many people, of whom I am one, consider passing procedures as parameters a *fundamental* control structure (really fundamental; people in this camp like to use it as a primitive to explain IF), and while it is possible to program without it, so would it be possible to program in a language with no FOR statements or where no expression was allowed to have more than one operator or where there was no automatic storage allocation. All of these are *possible*, but why make programming any harder than it has to be? >Maybe a few more useful examples, and a consensus on the implementation >issues would enable this issue to be reopened in the future. Procedures as parameters have been around since the late 1950s. There are many thousands of commercially important Fortran programs and libraries using them. There are libraries full of books about the functional programming paradigm and the importance of procedures as a (not "the", "a") tool of abstraction. >While I (as a user) think that downward closures and some other language >tidying would have been good, the ability to write OCXes easily in GNAT >would be far more useful :-). One must be practical. I would far rather have Ada 95 *now* the way it is than the way it might have been "some time next year". Any time I want a language with less compromise, I can turn to Clean. -- Fifty years of programming language research, and we end up with C++ ??? Richard A. O'Keefe; http://www.cs.rmit.edu.au/~ok; RMIT Comp.Sci.