Path: utzoo!attcan!uunet!mcvax!ukc!icdoc!syma!aarons From: aarons@syma.sussex.ac.uk (Aaron Sloman) Newsgroups: comp.lang.scheme Subject: Re: semantics of DEFINE (and nesting) Summary: nested LOCAL procedure definitions are useful and important Message-ID: <1030@syma.sussex.ac.uk> Date: 28 May 89 09:54:28 GMT References: <59021@yale-celray.yale.UUCP> <8905262124.AA04820@zohar> Organization: School of Cognitive & Computing Sciences, Sussex Univ. UK Lines: 135 jar@ZOHAR.AI.MIT.EDU (Jonathan Rees) writes: > From: Krulwich-Bruce@yale-zoo.arpa (Bruce Krulwich) > > A while ago I posted the suggestion that non-top-level DEFINEs do > the same thing as top-level DEFINEs, ie, side effect the top level. > ... This interpretation is the one adopted by the current version > of T (although it's not an explicit decision on the part of the T > designers) and I believe is the interpretation used by most LISP > dialects. ... > > Most of the responses that I got said either like "well, Abelson > and Sussman used the 'local' interpretation, so we really should > stick to it" or "well, local non-top-level DEFINEs add fewer > parentheses than nesting LETRECs." Does anyone have other > (theoretical or functional) reasons for this decision?? > > It *was* an explicit decision on the part of the designers, made in > 1981. It was an attempt to have something similar to the MIT Scheme > define but without coupling it to the syntax or semantics of lambda. > We never properly implemented the feature (LOCALE, for those of you > who have seen the T manual) that would have made it coherent, however. > Personally, I now would like to see T changed either to implement R3RS > define or to allow defines only at "top level" (given a suitable > non-global definition of "top level"). > In Pop-11 you can use define locally and it is frequently and very effectively used for two main purposes, and less importantly for a third: (a) Temporarily alter the error handler, standard character output consumer, interrupt handler, or other procedures that define the current environment, need to be changed in a particular procedure, and need to be re-set when that procedure is exitted whether normally or abnormally (e.g. via exitto, or by temporarily suspending a lightweight process using that procedure - in the latter case the temporary value is re-set if the procedure is resumed.) Typical example, redefining -interrupt locally-: define foo(...,....); lvars oldinterrupt=interrupt; ;;; save previous value in local var. define interrupt(); pr('Message about being in foo'); popready(); ;;; interactive break oldinterrupt(); ;;; if it exits normally do previous interrupt enddefine; ...body of foo... enddefine The interrupt procedure (whatever its current value) is called when the user hits the interrupt key, or after the error handler has printed its message, or if some procedure explicitly calls it. The use described in (a) depends on the procedure name being a dynamically scoped identifier. (b) Define a local procedure that is required ONLY within the nesting procedure. Sometimes this can be done by having a procedure defined at top level with either a unique name, or via file-local lexically scoped name, or in a section (Pop-11 sections are a bit like Packages in Common Lisp, but can be nested). But the local NESTED procedure definition is most useful when it needs to access one of the lexically scoped locals of the enclosing procedure. E.g. define foo(x, y, z); lvars x, y, z; define baz(w); lvars w; if w == x then .... elseif w == y then .... elseif w == z then ... .... enddefine; ...body of foo may either call baz or hand it as an argument ... to some other procedure or else may return baz as a new ... lexical closure enddefine; (Some of these cases can be handled slightly more efficiently, and slightly more messily using 'partial application', in Pop-11). My suspicion is that people who don't appreciate the usefulness of such nested procedure definitions must be people who have not been using a language that allows these constructs. Here at Sussex there was a gradual conversion among programmers using Pop-11, especially after we introduced lexical scoping as an option. (c) If you simply want to change the global value of a procedure identifier you can do things like: define foo ....; define foo_interrupt; .... enddefine; foo_interrupt -> interrupt; ;;; sets the value outside foo. enddefine; or to ensure that it is set in all current contexts set_global_valof(foo_interrupt, "interrupt"); -------------------------------- I would say that a lisp-like language that doesn't allow nested procedure definitions with these capabilities was seriously impoverished. One can of course achieve similar effects by other means, but they are bound to be more clumsy and also by not using the syntactic nesting you risk hiding an important relationship, and thereby confusing people responsible for maintaining code they did not write. The impoverishment in case (a) is reduced if your language does not provide a "process" mechanism, which not all Lisps do. Aaron Sloman, School of Cognitive and Computing Sciences, Univ of Sussex, Brighton, BN1 9QN, England INTERNET: aarons%uk.ac.sussex.cogs@nsfnet-relay.ac.uk aarons%uk.ac.sussex.cogs%nsfnet-relay.ac.uk@relay.cs.net JANET aarons@cogs.sussex.ac.uk BITNET: aarons%uk.ac.sussex.cogs@uk.ac or aarons%uk.ac.sussex.cogs%ukacrl.bitnet@cunyvm.cuny.edu UUCP: ...mcvax!ukc!cogs!aarons or aarons@cogs.uucp