Krulwich-Bruce@cs.yale.edu (Bruce Krulwich) (05/01/89)
In article <19890425161147.8.DEATH@MICKEY-MOUSE.LCS.MIT.EDU>, death@ZERMATT (Mark A. Sheldon) writes: >Allowing DEFINEs everywhere gives us a weird sort of dynamic scoping. >Restricting DEFINEs to beginnings of blocks allows us to think of a block >of internal DEFINEs as a LETREC (though CALL/CC may expose implementations >that don't implement these blocks as a LETREC). If Scheme is a statically >scoped language, then this sort of DEFINE is anathema. If Scheme is to >support dynamic scoping, then I think FLUID-LET is cleaner because I don't >have to think about environment mutation. 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 allows top-level definitions to scope over lexically bound variables, which cannot be done in any other way (as of now a single procedure can scope over variables, but such variables cannot be shared by procedures); having non-top-level DEFINEs side effect the local lexical environment is the same as LETREC (perhaps nested) and thus doesn't add any functionality. 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. The ability to have global procedures scope over variables reduces the number of unneeded global variables, allows hiding of internal representations, and (almost definitely) adds alot to the efficiency of accessing these variables. 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?? Bruce Krulwich
mkatz@SESAME.STANFORD.EDU (Morris Katz) (05/01/89)
Date: 30 Apr 89 20:39:25 GMT From: Krulwich-Bruce@yale-zoo.arpa (Bruce Krulwich) In article <19890425161147.8.DEATH@MICKEY-MOUSE.LCS.MIT.EDU>, death@ZERMATT (Mark A. Sheldon) writes: >Allowing DEFINEs everywhere gives us a weird sort of dynamic scoping. >Restricting DEFINEs to beginnings of blocks allows us to think of a block >of internal DEFINEs as a LETREC (though CALL/CC may expose implementations >that don't implement these blocks as a LETREC). If Scheme is a statically >scoped language, then this sort of DEFINE is anathema. If Scheme is to >support dynamic scoping, then I think FLUID-LET is cleaner because I don't >have to think about environment mutation. 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 allows top-level definitions to scope over lexically bound variables, which cannot be done in any other way (as of now a single procedure can scope over variables, but such variables cannot be shared by procedures); having non-top-level DEFINEs side effect the local lexical environment is the same as LETREC (perhaps nested) and thus doesn't add any functionality. 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. The ability to have global procedures scope over variables reduces the number of unneeded global variables, allows hiding of internal representations, and (almost definitely) adds alot to the efficiency of accessing these variables. 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?? Bruce Krulwich Some systems may not have the concept of A top level environment, so this proposal restricts Scheme in an important way that should be recognized. If one has a system that has first-class environments, then a top level environment may not exist or may be a dynamic, rather than a static, property of a piece of executing code. I would far prefer to see local defined removed from Scheme than have them given the proposed semantics. Morry Katz katz@polya.stanford.edu
alms@spt.entity.COM (andrew lm shalit) (05/02/89)
Date: 30 Apr 89 20:39:25 GMT From: Krulwich-Bruce@yale-zoo.arpa (Bruce Krulwich) In article <19890425161147.8.DEATH@MICKEY-MOUSE.LCS.MIT.EDU>, death@ZERMATT (Mark A. Sheldon) writes: . . . 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. . . . 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?? How about a general aversion to the notion of "top-level environment"? When you introduce first-class environments, and programming tools for using them, the notion of "top-level" starts to change. Just how it changes depends on how the environments and programming tools are designed. Do you have a problem with the following, aside from the general aversion to SET! ? (define frob nil) (let ((x (mumble1)) (y (mumble2))) (dset! frob (lambda (z) (list x y z))))
Ram-Ashwin@cs.yale.edu (Ashwin Ram) (05/02/89)
In article <8905011321.AA03861@spt.entity.com>, alms@spt.entity.COM (andrew lm shalit) writes: > Date: 30 Apr 89 20:39:25 GMT > 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. > . . . > 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?? > > How about a general aversion to the notion of "top-level environment"? When > you introduce first-class environments, and programming tools for using > them, the notion of "top-level" starts to change. Just how it changes > depends on how the environments and programming tools are designed. This is a good argument against DEFINE in general, not against DEFINEs within LETs in particular. A top-level DEFINE always assumes that you want to side-effect the "top-level environment". What you're really saying is that DEFINE should take an extra argument, which would explicitly specify the environment in which the definition should take place. T does provide such a function, called *DEFINE. DEFINE is merely a convenient syntax for doing a *DEFINE in the environment that the startup REPL works in, called USER-ENV. The REPL-ENV can be changed as desired. To get back to the original point, it seems perfectly reasonable to allow *DEFINEs to be nested within LETs, which would create a definition in the environment (which is explicitly specified as an argument to the *DEFINE). The definition is evaluated (and therefore closed) within the lexical environment of the LET, as any other form would be. Given that DEFINE is merely a convenient syntax for *DEFINE in the REPL-ENV, it seems consistent to let a DEFINE with a LET be a convenient syntax for a *DEFINE within that LET with the REPL-ENV explicitly specified as the environment for the definition. -- Ashwin.
Pavel.pa@XEROX.COM (05/04/89)
Too many people are referring to DEFINE as a form that ``side-effects the top-level environment'' for me to keep out of the fray. In the current (unreleased) draft of the Scheme specification, R^(3.95)RS, the following definitions appear (my wording): -- A program is a mixed sequence of definitions and expressions. -- The meaning of a program P is the same that that of the following expression: ((lambda (I*) P') <undefined> ...) where I* is the set of variables defined in P (i.e., appearing as the CADR of a DEFINE form), P' is the sequence of expressions obtained by replacing each definition in P with the corresponding assignment, and <undefined> is an expression producing some useless value. More informally, to execute a program, you wrap it in a big LET binding all of the defined variables to useless values, change the DEFINEs to SET!s, and evaluate that expression. Note, please: no mutation of any environment takes place. I believe that too many people base their understanding of the semantics of Scheme on a read-eval-print loop model or on the low-level details of particular (or perhaps all) implementations. It seems to me irrelevant that I might use side-effects to resolve the separate compilation (or just separate loading) of files of code with the semantics given above. The semantics of DEFINE, and therefore the most accurate way to conceptualize its meaning, has no mention of mutation. Of course, this little diatribe has no direct bearing on the meaning of internal DEFINEs. Personally, I'd rather they had the same meaning (as given above) as top-level ones. The meaning in terms of LETREC has always struck me as gratuitously incompatible, and frequently inconvenient as well. If you're going to use the same name, you should give it the same semantics... Pavel
biep@cs.vu.nl (J A Biep Durieux) (05/08/89)
In article <890503-103409-9762@Xerox> Pavel.pa@XEROX.COM writes: >-- A program is a mixed sequence of definitions and expressions. > >-- The meaning of a program P is the same as that of the following expression: > ((lambda (I*) P') <undefined> ...) >where I* is the set of variables defined in P (i.e., appearing as the CADR >of a DEFINE form), P' is the sequence of expressions obtained by replacing >each definition in P with the corresponding assignment, and <undefined> is >an expression producing some useless value. Then why not actually do this? -- The scheme top-level environment has each variable bound to a unique location. Many of these locations will be assigned the value #\undefined. -- The user can assign other values to variable locations using "set!". A "define" on top level will be an error, since the variable is already bound on that level. -- Allow the syntax "(set! (first cell) (car cell))" to mean the analogue of the comparable "define" syntax -- Biep. (biep@cs.vu.nl via mcvax) Who am I to doubt the existence of God? I am only a simple man, I already have trouble enough doubting the existence of my neighbour!
Krulwich-Bruce@cs.yale.edu (Bruce Krulwich) (05/08/89)
In article <890503-103409-9762@Xerox>, Pavel.pa@XEROX writes: >Too many people are referring to DEFINE as a form that ``side-effects the >top-level environment'' for me to keep out of the fray. In the current >(unreleased) draft of the Scheme specification, R^(3.95)RS, the following >definitions appear (my wording): > >-- A program is a mixed sequence of definitions and expressions. > >-- The meaning of a program P is the same that that of the following >expression: > ((lambda (I*) P') <undefined> ...) >where I* is the set of variables defined in P (i.e., appearing as the CADR >of a DEFINE form), P' is the sequence of expressions obtained by replacing >each definition in P with the corresponding assignment, and <undefined> is >an expression producing some useless value. > >More informally, to execute a program, you wrap it in a big LET binding all >of the defined variables to useless values, change the DEFINEs to SET!s, >and evaluate that expression. OK, the above makes sense for "top-level" DEFINE forms. Before recasting my comments from before into the terminology used above, let me define two catagories of DEFINE forms that are not handled under the above specification: (1) non-top-level static definitions: definitions that are made at load/compile/etc time (ie, are static) but are not top-level. An example of this is a DEFINE form inside a "top-level" LET form. (2) dynamic definitions: DEFINE forms that are executed during the execution of another procedure. An example of this is a DEFINE form in the body of another DEFINE form. My claim is that both of these cases fits cleanly into the specification given above (by Pavel). In each case the symbol being DEFINEd would be bound in the global binding countour (shown above as a LAMBDA, essentially a LET) and would be SET! at the point where they were to be DEFINEd. The new specifications (using the terminology from above) would be: A program is a sequence of expressions The meaning of a program P is the same as that of the expression: ((lambda (I*) P') <undefined> ...) where I* is the set of variables defined in P (ie, apearing as the CADR of a DEFINE form anywhere in the tree coresponding to P), P' is the sequence of expressions obtained by replacing all appearences of the symbol DEFINE [perhaps only those where the variable DEFINE isn't rebound] with teh symbol SET!, and <undefined> is an expression producing some useless value. The next question is why I think the specifications should be extended to allow this. My answers are: (1) It adds expressive power to the language. (2) Other proposed specifications for non-top-level DEFINEs do not add expressive power to the language. (3) It is a direct and consistant extension of the semantics being proposed (according to Pavel). Bruce Krulwich
Krulwich-Bruce@cs.yale.edu (Bruce Krulwich) (05/09/89)
In article <2454@ski.cs.vu.nl>, biep@cs (J A Biep Durieux) writes: >In article <890503-103409-9762@Xerox> Pavel.pa@XEROX.COM writes: > >>-- A program is a mixed sequence of definitions and expressions. >> >>--The meaning of a program P is the same as that of the expression: >> ((lambda (I*) P') <undefined> ...) ... >Then why not actually do this? > >-- The scheme top-level environment has each variable bound to a unique > location. Many of these locations will be assigned the value > #\undefined. > >-- The user can assign other values to variable locations using "set!". > A "define" on top level will be an error, since the variable is > already bound on that level. First, the system has to know what symbols to bind in the global LET contour. It would be consistant to have DEFINE always specify that a symbol be included in this binding. Secondly, having internal DEFINEs do local definitions is even more redundant, since it is 100% translatable into a LETREC while DEFINE is needed to specify what variables should be bound. Bruce Krulwich
Krulwich-Bruce@cs.yale.edu (Bruce Krulwich) (05/09/89)
In article <59918@yale-celray.yale.UUCP>, I wrote: >In article <2454@ski.cs.vu.nl>, biep@cs (J A Biep Durieux) writes: >>In article <890503-103409-9762@Xerox> Pavel.pa@XEROX.COM writes: >> >>>-- A program is a mixed sequence of definitions and expressions. >>> >>>--The meaning of a program P is the same as that of the expression: >>> ((lambda (I*) P') <undefined> ...) >... >>Then why not actually do this? >> >>-- The scheme top-level environment has each variable bound to a unique >> location. Many of these locations will be assigned the value >> #\undefined. >> >>-- The user can assign other values to variable locations using "set!". >> A "define" on top level will be an error, since the variable is >> already bound on that level. > >First, the system has to know what symbols to bind in the global LET >contour. It would be consistant to have DEFINE always specify that a >symbol be included in this binding. > >Secondly, having internal DEFINEs do local definitions is even more >redundant, since it is 100% translatable into a LETREC while DEFINE is >needed to specify what variables should be bound. Gee that last paragraph reads like gibberish. What I meant was: Secondly, having internal DEFINEs do local definitions is even more reduntant since this is 100% translatable into LETREC. On the other hand, top-level DEFINEs are not 100% translatable since some specification is needed of which variables should be in the top-level contour. Bruce
jar@ZOHAR.AI.MIT.EDU (Jonathan Rees) (05/27/89)
Date: 30 Apr 89 20:39:25 GMT 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"). I would prefer to see the desired functionality achieved not by a mechanism like T's but by either (a) a new construct implemented as a macro or (b) some kind of module system. I think a number of people are working on this design problem now.
aarons@syma.sussex.ac.uk (Aaron Sloman) (05/28/89)
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
shaff@Sesame.Stanford.EDU (Mike Shaff) (05/30/89)
ciao, In article <1030@syma.sussex.ac.uk>, aarons@syma (Aaron Sloman) writes: >jar@ZOHAR.AI.MIT.EDU (Jonathan Rees) writes: >> >> 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-: > Though Scheme has no standard for speaking about interrupt handlers and presuming that the code that was handling the interrupt dispatch did so using the current environment (an admittedly complex issue with interrupts), I see no reason why the standard LET & LETREC could not supply the functionality you are seeking. As I understand your comment you wish to have local definitions, as Scheme believes in first class functions, functions can be introduced any place that a Scheme object can be introduced. What is at issue here is the meaning of the define "command." > 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 (define foo (lambda (...) (let ((interrupt (lambda () (newline) (display Message about being in foo) (break) ;!Not standard! push a break loop (interrupt)))) ;This reference to interrupt refers to ;the binding of interrupt outside this ;environment body of foo))) Please note that I do not agree that this is the best way to shadow an interrupt handler, however I am showing how to transliterate the above code segment. > >(b) Define a local procedure that is required ONLY within the >nesting procedure. Again, the functionality of LET & LETREC allow this. (e.g., ;; BLORF alpha beta [FUNCTION] ;; ;; PURPOSE: Stupid example of how to get notification in a given context. ;; (define blorf (lambda (alpha beta) (let ((cons ;Define a new cons to give notice (lambda (obj-1 obj-2) (newline) (display "Cons was called from blorf") (cons obj-1 obj-2)))) ;Refer to the cons outside (if (eq? alpha beta) (cons alpha beta) (vector alpha beta))))) > >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. > Having the ability to nest functions is, of course, a wonderful thing, but I do not believe that anyone in the Scheme community contests that. The questions related to first class incremental environments, WELL there you will get discussion. >(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; (define foo (lambda (...) (let ((foo-interrupt (lambda () ...))) (set! interrupt foo-interrupt) ;Sets the value outside foo. ...))) > >or to ensure that it is set in all current contexts > > set_global_valof(foo_interrupt, "interrupt"); > I have no idea what this would mean in Scheme, so... > >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. > I could not agree more, however Scheme *DOES* allow nested procedure definitions, that properly capture the environment in which it was created, passed around, etc. (peace chance) mas --