diff options
Diffstat (limited to 'artima/scheme/scheme27.ss')
-rw-r--r-- | artima/scheme/scheme27.ss | 104 |
1 files changed, 13 insertions, 91 deletions
diff --git a/artima/scheme/scheme27.ss b/artima/scheme/scheme27.ss index bf24676..0994c23 100644 --- a/artima/scheme/scheme27.ss +++ b/artima/scheme/scheme27.ss @@ -14,6 +14,19 @@ that, the *same* implementation can implement phase separation *differently* in compiled code and in interpreted code, and/or differently in the REPL and in scripts! + +If you have a macro depending on helper functions, like +the previous one, you must put the helper functions in a separated +module if you want to ensure portability. Moreover, you must +import the helper functions with the syntax ``(for (module-name) expand)`` +meaning that the helper functions are intended to be used at expand +time, in macros. Ikarus is quite forgiving and can just use a regular +import, but PLT Scheme and Larceny will raise an error if you do not +use the ``for expand``. A full description of the module system, with +all the gory details, will require six more episodes, and will constitute +part V of these *Adventures*. + + Consider for instance this example in Ypsilon, which tries to implement a macro registry: @@ -44,20 +57,6 @@ simply recognized in the order given by their position in the source code. In our example the ``register`` function comes before the ``m`` macro, so it can be used in the right hand side of the macro definition. -Life is different when you have a Scheme implementation supporting phase -separation, which means most Scheme implementations. For such implementations -macro definitions are taken in consideration -*before* function definitions, independently from their relative -position in the source code. Therefore our example fails to compile -since the ``m`` macro makes use of the ``register`` function which is -*not yet defined* at the time the macro is considered, i.e. at compilation -time. The only way to make available a function defined -at runtime at compilation time is to define the function in a different -module and to import it in the original module. -This is enough to solve the problem for Ikarus, which has *weak phase -separation*, but it is not enough for PLT Scheme or Larceny, which have -*strong phase separation*. - An example will clarify the point. Suppose we define a registry module as follows @@ -136,84 +135,7 @@ However, I do not want to complicate the explanation of phase separation now, which is already complicated as it is, so let me defer a full explanation of this point to a future episode of my *Adventures*. - -Discussion -------------------------------------------------- - -Is phase separation a good thing? -It is clear that for the programmer's point of view, the simplest thing -is lack of phase separation. This is the semantic typically (but now -always) chosen by Scheme interpreters and REPLs: as soon as you type -it in, an helper function is available for use in macros. -If you look at it with honesty, at the end phase separation is -nothing else that a *performance hack*: by separing compilation time -from runtime you can perform some computation at compilation time only -and gain performance. - -Therefore, if you have a compiled version of Scheme, -it makes sense to separate compilation time from runtime, and to -expand macros *before* compiling the helper functions (in absence of -phase separation, macros are still expanded before running any runtime -code, but *after* recognizing the helper functions). -Notice that Scheme has a concept of *macro expansion time* which is -valid even for interpreted implementation when there is no compilation -time. The `expansion process`_ of Scheme source code is specified in -the R6RS. - -There is still the question if strong phase separation is a good thing, -or if weak phase separation (as in Ikarus) is enough. For the programmer -weak phase separation is easier, since he does not need to specify -the phase in which he want to import names. Strong phase separation -has been introduced so that at compile time a language which is -completely different from the language you use at runtime. In particular -you could decided to use in macros a subset of the full R6RS language. - -Suppose for instance you are a teacher, and you want to force your -students to write their macros using only a functional subset of Scheme. -You could then import at compile time all R6RS procedures except the -nonfunctional ones (like ``set!``) while keeping import at runtime -the whole R6RS. You could even perform the opposite, and remove ``set!`` -from the runtime, but allowing it at compile time. - -Therefore strong phase separation is strictly more powerful than week -phase separation, since it gives you more control. In Ikarus, when -you import a name in your module, the name is imported in all phases, -and there is nothing you can do about it. -On the other hand strong phase separation makes everything more complicated: -it is somewhat akin to the introduction of multiple namespace, because -the same name can be imported in a given phase and not in another, -and that can lead to confusion. - -There are people in the Scheme community thinking that strong phase -separation is a mistake, and that weak phase separation is the right thing -to do. On the other side people (especially from the PLT community where -all this originated) sing the virtues of strong phase separation and say -all good things about it. I personally I have not seen a compelling -use case for strong phase separation yet, and I would be happy is -some of my readers could give me such an example. -On the other hand, I am well known for preferring simplicity over -(unneeded) power. - -.. _expansion process: http://www.r6rs.org/final/html/r6rs/r6rs-Z-H-13.html#node_chap_10 |# (import (rnrs) (sweet-macros) (aps list-utils) (aps easy-test) (aps compat) (for (aps lang) expand run)) - -;;ALIST2 -(def-syntax (alist2 arg ...) - (: with-syntax ((name value) ...) (normalize #'(arg ...)) - (if (for-all identifier? #'(name ...)) - #'(let* ((name value) ...) - (list (list 'name name) ...)) - (syntax-violation 'alist "Found non identifier" #'(name ...) - (remp identifier? #'(name ...)))))) -;;END - -(run - (let ((a 1)) - (test "mixed" - (alist2 a (b (* 2 a))) - '((a 1) (b 2)))) - ) - |