Path: utzoo!attcan!uunet!elroy.jpl.nasa.gov!decwrl!pa.dec.com!src.dec.com!Norman Ramsey From: nr@Princeton.EDU (Norman Ramsey) Newsgroups: comp.lang.modula3 Subject: Re: Closures in m3 Message-ID: <9102191802.AA29918@cs.Princeton.EDU> Date: 19 Feb 91 18:02:53 GMT Lines: 57 To: Mike_Spreitzer.PARC@xerox.com, nr@elan.Princeton.EDU Cc: m3 > An even simpler approach would be to use procedures without the REFANY > argument, relying on the ability to make local procedures as the mechanism for > binding to other data. The only problem I have with this proposal is that Modula-3 is a functional language, so these local procedures can't escape (be assigned or returned). If I were to use local procedures for enumerations, and if I wanted to perform the same enumeration in two different places, I would have to duplicate the implementation of the local procedure (or write two different local procedures that call a third, fetching the arguments from the closure). Returning an enumeration procedure would be impossible. The advantage of using local procedures is that the closure is allocated on the stack, not the heap, and the language mechanisms do it automatically. Using my proposal, the programmer has to declare the type of the closure and allocate it himself from the heap. > ...[but the > local procedure scheme needs an additional change: the ability to assign local > procedures to procedure variables; this is currently forbidden, but no more > unsafe than dereferencing UNTRACED references --- so I don't see why it > shouldn't be equally possible.] Because the local procedures are allocate on the stack, so their closures are allocated on the stack. Assignment to global variables is like returning procedure values: the function ``escapes,'' which means that the closure (free variables) must be allocated on the heap, because a stack-allocated closure would refer to stack locations that have been reused for something else. Imagine > TYPE > WalkProc = PROCEDURE (element: REFANY) RAISES {}; > (* The type of procedures for walking over lists. *) > > PROCEDURE Walk( l: T; p: WalkProc ); PROCEDURE NewSum(start:REFANY; add:PROCEDURE(x,y:REFANY):REFANY): WalkProc = VAR sum := start; PROCEDURE Walk(element:REFANY) RAISES {} = BEGIN sum := add(sum,element); END Walk; BEGIN RETURN Walk; (* or ``walker := walk; RETURN; '' *) END NewSum; Now `Walk' has free variables `sum' and add, but these were allocated to a stack frame that is reclaimed when NewSum returns. > PROCEDURE SumIt( it: List.T ) : INTEGER = > VAR sum: INTEGER; > BEGIN > sum := 0; > List.Walk(it, LAMBDA (element: REFANY) = BEGIN (* add element *) END); > RETURN sum; > END SumIt; This example is OK because the local procedure is passed down the stack.