zimmerma@lan.informatik.tu-muenchen.dbp.de (Kai Werner Zimmermann) (07/17/90)
Hello Net, we have some problems using macros in MIT Scheme. Could someone help us? 1. We're using MIT Scheme Release 7.0.0 (beta), Microcode 10.86 The documentation is DRAFT: June 28, 1989. 2. The documentation we have says nothing about macros, but a special form "macro" with lambda-like syntax exists. Amazingly it seems to be really identical to lambda, e.g. it evaluates its parameters and simply returns the result of its computations. Questions: 1. What's MIT Schemes latest release and where can we get it? Is better Documentation available? 2. Has MIT Scheme macros? How can we use them? Any help will be appreciated, --Kai -- ================================================================================ | Kai Zimmermann zimmerma@lan.informatik.tu-muenchen.dbp.de | | Hold fast to your dreams, for if dreams die, | ----------------- life is a broken winged bird that cannot fly. ----------------
markf@ZURICH.AI.MIT.EDU (07/18/90)
1. You have the latest release. 2. You should have a file called documentation/macros.txt which explains some of the macro stuff. I will include that file at the end of this message along with some other miscellaneous macro related information. -Mark ------------------ macros.txt ---------------------- Macros in MIT Scheme The following description matches release 6.2.2. There probably won't be many user visible changes for release 7. The interpreter interprets a language called "Scode". The syntaxer (invoked with the procedure SYNTAX) translates list structure representing Scheme expressions to Scode. The syntaxer is guided in this process by a structure called a "syntax table", which defines the transformations between various special forms and Scode. A list whose first element is not a keyword defined in the syntax table is assumed to be a procedure application, and is translated accordingly. Individual symbols are assumed to represent variable references. The basic operations on syntax tables are: (MAKE-SYNTAX-TABLE #!optional parent-syntax-table) (SYNTAX-TABLE-REF syntax-table keyword) (SYNTAX-TABLE-DEFINE syntax-table keyword expander) If no PARENT-SYNTAX-TABLE argument is given, then the value of MAKE-SYNTAX-TABLE is a new, empty syntax table. The usual argument to this procedure is the variable SYSTEM-GLOBAL-SYNTAX-TABLE, which contains all of the standard syntactic keywords: LAMBDA, IF, COND, BEGIN, etc. The expander must be a procedure taking S-expressions as input and producing Scode. For convenience there is a macro called MACRO similar to LAMBDA which wraps the body of the expander in the appropriate code to translate into Scode. For example, (syntax-table-define <some syntax table> 'FOO (macro (x y z) `(list ,z (+ ,x ,y)))) adds a new keyword (FOO) to <some syntax table> with the obvious meaning. Note that (syntax-table-define <some syntax table> 'FOO (lambda (x y z) `(list ,z (+ ,x ,y)))) would not have the same effect, since there is no translation to Scode here, and lists are not necessarily valid Scode. The base syntax table is the value of the variable SYSTEM-GLOBAL-SYNTAX-TABLE. The syntax table of the read eval print loop (usually a child of SYSTEM-GLOBAL-SYNTAX-TABLE) is returned by the procedure named REP-SYNTAX-TABLE. Therefore, a way to install a "macro" in the syntax table used to translate keyboard input is to type (syntax-table-define (rep-syntax-table) 'FOO (macro (x y z) `(list ,z (+ ,x ,y)))) Note that the expression producing the expander is evaluated in the same environment where the whole expression is evaluated, since SYNTAX-TABLE-DEFINE is not a special form keyword. The recommended way of extending syntax tables is to have a bunch of SYNTAX-TABLE-DEFINE expressions in a file, and this file can be loaded into any appropriate environment. Since files are, theoretically, translated as a unit before they are evaluated, and SYNTAX-TABLE-DEFINE is not a special form keyword, a SYNTAX-TABLE-DEFINE expression appearing in a file should have no effect on the rest of the file. The syntax table according to which an expression (or a file) is translated can be manipulated by using the following special forms: (USING-SYNTAX <some syntax table> . <forms>) <some syntax table> is evaluated at translation time in a special enviroment (called syntax-environment) and should produce a valid syntax table. The rest of the form is translated according to this syntax table. (LET-SYNTAX ((<keyword1> <expander1>) (<keyword2> <expander2>) ...) . <forms>) LET-SYNTAX makes a new syntax table with the bindings expressed by the <keyword> <expander> pairs. This new syntax table has as its parent the syntax table used to expand the LET-SYNTAX itself. The forms are then expanded in this syntax table. Note that the expanders are just like the expanders given to SYNTAX-TABLE-DEFINE, and are therefore usually created by using the MACRO special form. They are evaluated (closed) in the syntaxer environment, which for all practical purposes is identical to the global environment. Some suggestions: LET-SYNTAX should be used only for relatively trivial things. For more complicated things, the code should be split into two files. One file should contain the code that creates and modifies syntax tables to provide the new syntactic features. The other file should contain the desired code with the relevant parts surrounded by appropriate USING-SYNTAX forms. In this way the file that defines the syntactic extensions has convenient control of the environment, since it contains "normal" Scheme code (a bunch of definitions, usually). Note also that the expression producing the syntax table in a USING-SYNTAX expression is evaluated in the syntaxer environment, which does not have the usual user environment as its parent, therefore a good way to reference user defined sytnax tables is to use the idioms (using-syntax (access my-syntax-table user-initial-environment) . <forms>) (using-syntax (access my-syntax-table (rep-environment)) . <forms>) ------------------- miscellaneous macro stuff ------------------ Define-macro lets you define a macro in the "current" syntax table using a convenient interface. (syntax-table-define current-syntax-table 'my-macro (macro (a b c) (list '+ a b c))) and (define-macro (my-macro a b c) (list '+ a b c)) both define the same macro (assuming current-syntax-table is somehow bound to the "current" syntax table). Macros are given their arguments unevaluated, so an invocation like: (my-macro 10 (- 20 10) 30) would (essentially) expand into: (+ 10 (- 20 10) 30) and would evaluate to 50. Right now lambda and macro do the same thing. If the implementation of macros changes, then lambda and macro might be different, so you shouldn't use them interchangably. The real issue is what happens when you use macro (or a lambda) as the expander associated with a syntax table entry. In that situation, the arguments that the expander gets when it is applied are the unevaluated arguments to the macro call. The evaluation occurs when the result of the expansion gets evaluated. When you use lambda (or macro) as the body of a define, then the procedure will get as its arguments the evaluated arguments of the procedure call. For example: (syntax-table-define user-initial-syntax-table 'foo-mac (macro (arg) (newline) (display arg) arg)) ;No value (define foo-proc (lambda (arg) (newline) (display arg) arg)) ;Value: foo-proc (foo-mac (+ 1 2)) (+ 1 2) ;Value: 3 (foo-proc (+ 1 2)) 3 ;Value: 3 See, in "foo-mac" the expander gets "arg" bound to '(+ 1 2) which it displays and then returns. The '(+ 1 2) is then evaluated and produced the value 3. In "foo-proc" the procedure gets "arg" bound to 3 which it then returns. A useful thing to remember when thinking about macros is that macro expansion is independent of interpretation and evaluation. The expansion can (and does when you use sf) occur way before the eventual evaluation of an expression. It can be confusing because when you are typing expressions into the read-eval-print loop there is macro expansion going on between the read and the eval.