summaryrefslogtreecommitdiff
path: root/artima
diff options
context:
space:
mode:
authormichele.simionato <devnull@localhost>2009-04-20 05:10:03 +0000
committermichele.simionato <devnull@localhost>2009-04-20 05:10:03 +0000
commit573abefd1cd7e13181a2b53a44619234c449ae6c (patch)
treedef307839ca35bd54b3015df6f9992d16c785b7e /artima
parentc214d459cad0a463716c83c9eb12f22c9c42bf75 (diff)
downloadmicheles-573abefd1cd7e13181a2b53a44619234c449ae6c.tar.gz
Committed a lot of work on the Scheme module system
Diffstat (limited to 'artima')
-rw-r--r--artima/scheme/Makefile2
-rw-r--r--artima/scheme/scarti.txt37
-rw-r--r--artima/scheme/scheme19.ss22
-rw-r--r--artima/scheme/scheme20.ss158
-rw-r--r--artima/scheme/scheme21.ss155
-rw-r--r--artima/scheme/scheme23.ss229
-rw-r--r--artima/scheme/scheme27.ss180
-rw-r--r--artima/scheme/table-of-contents.txt2
-rw-r--r--artima/scheme/use-ct-mapping.ikarus.ss8
-rw-r--r--artima/scheme/use-ct-mapping.ss (renamed from artima/scheme/use-ct-mapping.mzscheme.ss)0
-rw-r--r--artima/scheme/use-registry.ikarus.ss9
-rw-r--r--artima/scheme/use-registry.mzscheme.ss12
-rw-r--r--artima/scheme/use-registry.ss13
13 files changed, 451 insertions, 376 deletions
diff --git a/artima/scheme/Makefile b/artima/scheme/Makefile
index e88904b..e906a05 100644
--- a/artima/scheme/Makefile
+++ b/artima/scheme/Makefile
@@ -150,7 +150,7 @@ fourth-cycle: fourth-cycle.txt
$(RST) scheme20.ss; $(POST) scheme20.rst 255303
21: scheme21.ss
- $(RST) scheme21.ss; $(POST) scheme21.rst
+ $(RST) scheme21.ss; $(POST) scheme21.rst 255612
22: scheme22.ss
$(RST) scheme22.ss; $(POST) scheme22.rst
diff --git a/artima/scheme/scarti.txt b/artima/scheme/scarti.txt
index 454bd3a..a6c8bc1 100644
--- a/artima/scheme/scarti.txt
+++ b/artima/scheme/scarti.txt
@@ -583,3 +583,40 @@ Suppose you wanted to define the macros
#'(def-syntax kw
(let ((a (alist (name value) ...)))
<more code here> ...)))
+
+Using list-comprehension
+------------------------------------------------------------
+
+For instance, once I needed to write the permutation of a set
+of symbols in order to write a complex macro, and I did spend
+a lot of time trying to implement them in time of ``map``
+and ``filter``, whereas it was trivial to solve the problem
+with list comprehensions.
+The algorithm is very clear; an empty list has no permutations::
+
+ > (perm '())
+ ()
+
+A list of lenght 1 has only one possible permutation::
+
+ > (perm '(a))
+ ((a))
+
+The permutations of list of length n are the sum of the
+permutations of its sublists of lenght n-1::
+
+ > (perm '(a b));; the sublists are '(b) and '(a)
+ ((a b) (b a))
+ > (perm '(a b c));; the sublists are '(b c), '(a c) and '(a b)
+ ((a b c) (a c b) (b a c) (b c a) (c a b) (c b a))
+
+Here is the implementation using list comprehension::
+ ;; compute the permutations of a list of distinct elements
+ (define (perm lst)
+ (cond
+ ((null? lst) '()); empty list
+ ((null? (cdr lst)) (list lst)); single element list
+ (else; multi-element list
+ (list-of (cons el subls)
+ (el in lst)
+ (subls in (perm (list-of e (e in lst) (not (eq? e el)))))))))
diff --git a/artima/scheme/scheme19.ss b/artima/scheme/scheme19.ss
index 687f0c9..398d2c0 100644
--- a/artima/scheme/scheme19.ss
+++ b/artima/scheme/scheme19.ss
@@ -1,10 +1,6 @@
-#|
-The R6RS module system
+#|The R6RS module system
=========================================================
-Preamble
----------------------------------------------------------
-
For nearly 30 years Scheme lived without a standard module system.
The consequences of this omission were the proliferation of dozens
of incompatible module systems and neverending debates.
@@ -47,7 +43,7 @@ the ``lib.pyc`` file becomes outdated: the Python interpreter is
smart enough to recognize the issue and to seamlessly recompile ``lib.pyc``.
In Scheme the compilation process is very much *implementation-dependent*.
-Here I will focus on the Ikarus mechanism, which is the most Pythonic one.
+Here I will focus on the Ikarus mechanism, which is Pythonic enough.
Ikarus has two modes of operation; by default it just compiles
everything from scratch, without using any intermediate file.
This is possible since the Ikarus compiler is very fast. However,
@@ -74,6 +70,18 @@ lot of Python programs are actually calling underlying C libraries, so
that Python can be pretty fast in some cases (for instance in
numeric computations using numpy).
+All I said for Ikarus, can be said from Ypsilon, with minor differences.
+Ypsilon compiles to intermediate code, like Python.
+Precompiled files are automatically generated
+without the need to specify any flag, as in Python; however they
+are stored in a so called auto-compile-cache directory, which by
+default is situated in ``$HOME/.ypsilon``. Rhe location can
+be changed by setting the environment variable ``YPSILON_ACC``
+or by passing the ``--acc=dir`` argument to the Ypsilon interpreter.
+It is possible to disable the cache and to clear the cache; if you
+are curious about the details you should look at Ypsilon manual
+(``man ypsilon``).
+
Modules are not first class objects
-------------------------------------------------------------
@@ -277,6 +285,6 @@ architecture of the target processor.
.. image:: compiler-crosscompiler.jpg
-.. _cross compilation: http://chicken.wiki.br/cross-compilation
+.. cross compilation: http://chicken.wiki.br/cross-compilation
.. _cross compilation: http://en.wikipedia.org/wiki/Cross_compilation
|#
diff --git a/artima/scheme/scheme20.ss b/artima/scheme/scheme20.ss
index 04db2f7..f927dff 100644
--- a/artima/scheme/scheme20.ss
+++ b/artima/scheme/scheme20.ss
@@ -81,6 +81,8 @@ and thus very much implementation-dependent, I will focus on the
compiler semantics of Scheme programs. Such semantics is quite
tricky, especially when macros enters in the game.
+.. _9: http://www.artima.com/weblogs/viewpost.jsp?thread=240804
+
Macros and helper functions
---------------------------------------------------------------------
@@ -91,9 +93,10 @@ simple macro
$$ASSERT-DISTINCT
which raises a compile-time exception (syntax-violation) if it is
-invoked with duplicate arguments. A typical use case for such macro
-is definining specialized lambda forms. The
-macro relies on the builtin function
+invoked with duplicate arguments. Such macro could be used as a
+helper in macros defining multiple names at the same time, like
+the ``multi-define`` macro of episode 9_.
+``assert-distinct`` relies on the builtin function
``free-identifier=?`` which returns true when two identifiers
are equal and false otherwise (this is a simplified explanation,
let me refer to the `R6RS document`_ for the gory details) and
@@ -101,18 +104,19 @@ on the helper function ``distinct?`` defined as follows:
$$list-utils:DISTINCT?
-Here are a couple of test cases for ``distinct?``:
+``distinct?`` takes a list of objects and finds out they are all
+distinct according to some equality operator, of if there are duplicates.
+Here are a couple of test cases:
$$TEST-DISTINCT
-The problem with the evaluation semantics is that it is natural, when
-writing the code, to define first the function and then the macro, and
-to try things at the REPL. Here everything works; in some
-Scheme implementation, like Ypsilon, this will also work as a
-script, unless the strict R6RS-compatibility flag is set.
-However, in R6RS-conforming implementations, if you cut and paste
-from the REPL and convert it into a script, you will run into
-an error!
+It is natural, when writing new code, to try things at the REPL and to
+define first the function and then the macro. The problem is that
+everything works in the REPL; moreover, in some Scheme implementation
+like Ypsilon, the code will also
+work as a script (unless the strict R6RS-compatibility flag is set).
+However, in R6RS-conforming implementations, if you cut and paste from
+the REPL and convert it into a script, you will run into an error!
The problem is due to the fact than in the compiler semantics macro
definitions and function definitions happens at *different times*. In
@@ -134,12 +138,18 @@ available at expand time a function defined at runtime is to
define the function in a different module and to import it at
expand time*
+The `expansion process`_ of Scheme source code is specified in
+the R6RS document. The standard has the generic concept of
+*macro expansion time* which is valid even for interpreted
+implementation when there is no compilation time.
+
Phase separation
--------------------------------------------------------------
-I have put ``distinct?`` in the ``(aps list-utils)``
+Let me go back to the example of the ``assert-distinct`` macro.
+I have put the ``distinct?`` helper function in the ``(aps list-utils)``
module, so that you can import it. This is enough to solve the
-problem for Ikarus, which has no concept of *phase separation*, but it is not
+problem of compile time vs runtime separation for Ikarus, but it is not
enough for PLT Scheme or Larceny, which have full *phase separation*.
In other words, in Ikarus (but also Ypsilon, IronScheme and Mosh)
the following script
@@ -154,13 +164,14 @@ is correct, but in PLT Scheme and Larceny it raises an error::
.. image:: salvador-dali-clock.jpg
-The problem is that PLT Scheme has *strong phase separation*: by default
+The problem is that PLT Scheme has *full phase separation* and therefore
+requires *phase specification*: by default
names defined in external modules are imported *only* at runtime, *not*
at compile time. In some sense this is absurd since
names defined in an external pre-compiled modules
are of course known at compile time
(this is why Ikarus has no trouble to import them at compile time);
-nevertheless PLT Scheme and Larceny Scheme forces you to specify at
+nevertheless PLT Scheme (and Larceny) forces you to specify at
which phase the functions must be imported. Notice that personally I
do not like the PLT and Larceny semantics since it makes things more
complicated, and that I prefer the Ikarus semantics:
@@ -180,102 +191,41 @@ With this import form, the script is portable in all R6RS implementations.
Discussion
-------------------------------------------------
-Is phase separation a good thing?
-In my opinion, from the programmer's point of view, the simplest thing
-is lack of complete lack of phase separation, and interpreter semantics, in
-which everything happens at runtime.
-If you look at it with honesty, at the end the compiler semantics is
+Personally, I find the interpreter semantics the most intuitive and
+easier to understand. In such semantics everything happens at runtime,
+and there is no phase separation at all; it is true that the code may
+still be compiled before being executed, as it happens in Ikarus, but
+this is an implementation detail: from the point of view of the
+programmer the feeling is the same as using an interpreter.
+The interpreter semantics is also the most powerful semantics at all:
+for instance, it is possible to redefine identifiers and it is
+possible to import modules at runtime, things which are both impossible
+in compiler semantics.
+
+After all, if you look at it with honesty, the compiler semantics is
nothing else that a *performance hack*: by separing compilation time
-from runtime you can perform some computation only once at compilation time
-and gain performance. Moreover, in this way you can make cross compilation
-easier. Therefore the compiler semantics has practical advantages and
-I am willing cope with it, even if conceptually I still prefer the
-straightforwardness of interpreter semantics.
+from runtime you can perform some computation only once (at compilation time)
+and gain performance. This is not strange at all: compilers *are*
+performance hacks. It is just more efficient to convert a a program into
+machine code with a compiler than to interpret one expression at the time.
+Since in practice there are lots of situations where performance is
+important and one does need a compiler, it makes a lot of sense to
+have a compiler semantics. The compiler
+semantics is also designed to make separate compilation and cross compilation
+possible. Therefore the compiler semantics
+has many practical advantages and
+I am willing cope with it, even if it is not as
+straightforward as interpreter semantics.
+
Moreover, there are (non-portable) tricks to define helper functions
at expand time without need to move them into a separate module, therefore
-compiler semantics is not so unbearable.
+it is not so difficult to work around the restrictions of the compiler
+semantics.
The thing I really dislike is full phase separation. But a full discussion
of the issues releated to phase separation will require a whole episode.
See you next week!
-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 wants 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 decide 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. For instance this program
-in Ikarus (but also IronScheme, Ypsilon, MoshScheme)
-
-.. code-block:: scheme
- (import (rnrs) (for (only (aps list-utils) distinct?) expand))
- (display distinct?)
-
-runs, contrarily to what one would expect, because it is impossible
-to import the name ``distinct?`` at expand time and not at runtime.
-In PLT Scheme and Larceny instead the program will not run, as you
-would expect.
-
-You may think the R6RS document to be schizophrenic, since it
-accepts both implementations with phase separation and without
-phase separation. The previous program is *conforming* R6RS code, but
-behaves *differently* in R6RS-compliant implementations!
-
-but using the semantics without phase separation results in
-non-portable code. Here a bold decision was required to ensure
-portability: to declare the PLT semantics as the only acceptable one,
-or to declare the Dibvig-Gouloum semantics as the only acceptable one.
-
-De facto, the R6RS document is the result
-of a compromise between the partisans of phase separation
-and absence of phase separation.
-
-
-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. To contain the confusion, the R6RS
-documents states that *the same name cannot be used in different phases
-with different meaning in the same module*.
-For instance, if the identifier ``x`` is bound to the
-value ``v`` at the compilation time and ``x`` is defined even
-at runtime, ``x`` must be bound to ``v`` even at runtime. However, it
-is possible to have ``x`` bound at runtime and not at compile time, or
-viceversa. This is a compromise, since PLT Scheme in non R6RS-compliant mode
-can use different bindings for the same name at different phases.
-
-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.
-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
|#
diff --git a/artima/scheme/scheme21.ss b/artima/scheme/scheme21.ss
index 513ce49..94251fb 100644
--- a/artima/scheme/scheme21.ss
+++ b/artima/scheme/scheme21.ss
@@ -1,8 +1,93 @@
-#|More on phase separation
+#|Phase separation
===================================================================
-In this episode I will discuss in detail the trickiness
-associated with the concept of phase separation.
+We saw in the last episode that Scheme programs executed
+in compiler semantics exhibit phase separation, i.e. some code
+is executed at compile (expand) time and some code is executed
+at runtime. Things, however, are more complicated than that.
+
+There are two different concepts of phase
+separation, even for R6RS-conforming implementations.
+Ikarus, Ypsilon, IronScheme e MoshScheme have
+a weak (partial) form of phase separation: there is a distinction
+between expand time and runtime, but it is not possible to import
+names only at runtime or only at expand time, i.e. the phases are
+not fully separated. PLT Scheme and Larceny instead have a strong
+form of phase separation in which phases are completely separated:
+in such implementations, when you import (more correctly
+*instantiate*) a module, you may specify in which phases to import
+it, and each phases sees a *different instance* of the module.
+The consequence is that the language used at at compile time (the
+language seen by a piece of code is the sum of the imported names)
+can be completely different from the language used at runtime. In particular
+you could decide 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 importing 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 implementations
+with weak/partial phase separation when
+you import a name in your module, the name is imported in all phases,
+and there is nothing you can do about it. For instance this program
+in Ikarus (but also IronScheme, Ypsilon, MoshScheme)
+
+.. code-block:: scheme
+
+ (import (rnrs) (for (only (aps list-utils) distinct?) expand))
+ (display distinct?)
+
+runs, contrarily to what one would expect, because it is impossible
+to import the name ``distinct?`` at expand time and not at runtime.
+In PLT Scheme and Larceny instead the program will not run, as you
+would expect.
+
+You may think the R6RS document to be schizophrenic, since it
+accepts both implementations with phase separation and without
+phase separation. The previous program is *conforming* R6RS code, but
+behaves *differently* in R6RS-compliant implementations!
+
+but using the semantics without phase separation results in
+non-portable code. Here a bold decision was required to ensure
+portability: to declare the PLT semantics as the only acceptable one,
+or to declare the Dibvig-Gouloum semantics as the only acceptable one.
+
+De facto, the R6RS document is the result
+of a compromise between the partisans of phase separation
+and absence of phase separation.
+
+
+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 wants to import names.
+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. To contain the confusion, the R6RS
+documents states that *the same name cannot be used in different phases
+with different meaning in the same module*.
+For instance, if the identifier ``x`` is bound to the
+value ``v`` at the compilation time and ``x`` is defined even
+at runtime, ``x`` must be bound to ``v`` even at runtime. However, it
+is possible to have ``x`` bound at runtime and not at compile time, or
+viceversa. This is a compromise, since PLT Scheme in non R6RS-compliant mode
+can use different bindings for the same name at different phases.
+
+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.
+On the other hand, I am well known for preferring simplicity over
+(unneeded) power.
+
+.. _5: http://www.artima.com/weblogs/viewpost.jsp?thread=239699
More examples of macros depending on helper functions
-----------------------------------------------------------------
@@ -69,24 +154,25 @@ The problem with auxiliary macros
------------------------------------------------------------------
We said a few times that auxiliary functions are not available to macros
-defined in the same module, but actually in general
+defined in the same module, but in general
there is the *same* problem for any identifier which is used in the
right hand side of a macro definition, *including auxiliary macros*.
-For instance, we may regard the ``indexer-syntax`` macro
-as an auxiliary macro, to be used in the right hand side of a
-``def-syntax`` form.
In systems with strong phase separation, like
PLT Scheme and Larceny, auxiliary macros
are not special, and they behave as auxiliary functions: you
must put them into a separare module and you must import them
with ``(for (only (module) helper-macro) expand)`` before using them.
-This is why the following script
+
+In particular, the ``indexer-syntax`` macro defined before is
+an auxiliary macro, to be used in the right hand side of a
+``def-syntax`` form. The following script
$$indexer-syntax:
-fails in PLT scheme:
+fails in PLT scheme
+::
$ plt-r6rs indexer-syntax.ss
indexer-syntax.ss:9:0: def-syntax: bad syntax in: (def-syntax (indexer-syntax a b c))
@@ -95,10 +181,10 @@ fails in PLT scheme:
/usr/lib/plt/collects/rnrs/base-6.ss:492:6
-The problem is that in PLT and Larceny, the second ``def-syntax`` does
+because the second ``def-syntax`` does
not see the binding for the ``indexer-syntax`` macro.
-This is a precise design choice: systems with strong phase
+This is a precise design choice: systems with full phase
separation are making the life harder for programmers,
by forcing them to put auxiliary functions/macros/objects
in auxiliary modules, to keep absolute control on how the
@@ -119,53 +205,14 @@ is that a long as the compiler reads macro definitions, it expands
the compile-time namespace of recognized names which are available
to successive syntax definitions.
-In Ikarus the script ``identifier-syntax.ss`` is
+In such systems the script ``identifier-syntax.ss`` is
perfectly valid: the first syntax
definition would add a binding for ``identifier-syntax`` to the macro
namespace, so that it would be seen by the second syntax definition.
-
-Systems with strong phase separation instead are effectively using
-different namespaces for each phase.
-
-
-Implementing a first class module system
------------------------------------------
-
-This is the same as implementing a Pythonic interface over hash tables
-or association lists.
-
-Working around phase separation
---------------------------------------------------------------
-
-I have always hated being forced to put my helper functions in an
-auxiliary module, because I often use auxiliary functions which
-are intended to be used only once inside a given macro, thus
-it makes sense to put those auxiliary functions *in the same
-module* as the macro the are used in.
-In principle you could solve the problem by definining all the
-functions *inside* the macro, but I hate this, both for dogmatic
-reasons (it is a Pythonista dogma that *flat is better than nested*)
-and for pragmatic reasons, i.e. I want to be able to debug my
-helper functions and this is impossible if they are hidden inside
-the macro. They must be available at the top-level. Moreover, you
-never know, and a functionality which was intended for use in a specific
-macro my turn useful for another macro after all, and it is much
-more convenient if the functionality is already encapsulated in
-a nice exportable top level function.
-
-I am not the only to believe that it should be possible to define
-helper functions in the same module as the macro and actually
-many Scheme implementations provide a way to do so via a
-``define-for-syntax`` form which allows to define function
-at *expand time*, so that they are available for usage in macros.
-
-If your Scheme does not provide ``define-for-syntax``, which is not
-part of the R6RS specification, you can still work around phase
-separation with some clever hack. For instance, you could
-use the following macro:
-
-$$lang:literal-replace
+In any case, if you want to write portable code, you must follow
+the PLT/Larceny route, to put your auxiliary macros in a separated
+module and to import them at expand time.
|#
(import (rnrs) (sweet-macros) (for (aps list-utils) expand run)
diff --git a/artima/scheme/scheme23.ss b/artima/scheme/scheme23.ss
new file mode 100644
index 0000000..989966a
--- /dev/null
+++ b/artima/scheme/scheme23.ss
@@ -0,0 +1,229 @@
+#|Side effects in modules
+===============================================================
+
+In functional programs there are no side effects; in real life programs
+however there are side effects, and we must be able to cope with them.
+The way the module system copes with side effects is quite tricky and
+implementation-dependent. The goal of this episode is to shed some
+light on the subject.
+
+Side effects at the REPL
+------------------------------------------------------
+
+Let me start with a simple example. Consider a module exporting a variable
+(``x``) and a function with side effects affecting that variable (``incr-x``)):
+
+$$experimental/mod1:
+
+(for convenience I am storing all the code of this episode code into a
+package called ``experimental``).
+This kind of side effect is ruled out by the R6RS specification, since
+exported variables must be immutable. This is the reason why both Ikarus
+and Ypsilon reject the code with errors like
+``attempt to export mutated variable`` or
+``attempt to modify immutable variable``.
+On the other hand PLT Scheme compiles the code without raising any
+warning, therefore even this simple case is tricky and exposes
+a non-portability of the module system :-(
+
+Consider now a module exporting a function with side effects affecting a
+non-exported variable:
+
+$$experimental/mod2:
+
+This is a valid library which compiles correctly. The accessor ``get-x``
+gives access to the internal variable ``x``. We may import it
+at the REPL and experiment with it:
+
+.. code-block:: scheme
+
+ > (import (experimental mod2))
+ > (get-x)
+ 0
+ > (incr-x)
+ 1
+ > (incr-x)
+ 2
+ > (get-x)
+ 2
+
+Everything works as you would expect.
+
+As always, things are trickier in scripts, when compiler semantics and
+phase separation enters in the game.
+
+Side effects and phase separation
+------------------------------------------------------
+
+Consider the following script:
+
+$$experimental/use-mod2:
+
+Here we import the module ``mod2`` twice, both at run-time and at expand time.
+In Scheme implementations with full phase separation there are two fully
+separated instances of the module, and running the script returns
+what you would expect::
+
+ $ plt-r6rs use-mod2.ss
+ At expand-time x=1
+ At run-time x=1
+
+The fact that ``x`` was incremented at compile-time has no effect
+at all at run-time, since the run-time variable ``x`` belongs to a completely
+different instance of the module. In system with weak phase separation
+instead, there is only a *single instance of the module for all phases*,
+so that incrementing ``x`` at expand-time has effect at runtime::
+
+ $ ikarus --r6rs-script use-mod2.ss
+ At expand-time x=1
+ At run-time x=2
+
+You would get the same with Ypsilon.
+
+This only works because the script is executed immediately
+after compilation *in the same process*. However, having compile-time
+effects affecting run-time values is *utterly wrong*, since it breaks
+separate compilation. If we turn the script into a library and we
+compile it separately, it is clear than the run-time value of ``x``
+cannot be affected by the compile-time value of ``x``
+(maybe the code was compiled 10 years ago!) and it
+must use a separate instance of the imported module.
+
+Side effects and separate compilation
+-------------------------------------------------------------
+
+Let me explain in detail how separate compilation works in Ikarus,
+Ypsilon and PLT Scheme. Suppose we turn the previous script into a library
+
+$$experimental/use-mod3:
+
+and let us invoke this library though a script ``use-mod3.ss``:
+
+$$experimental/use-mod3:
+
+If we use PLT Scheme, nothings changes::
+
+ $ plt-r6rs use-mod3.ss
+ At expand-time x=1
+ At run-time x=1
+
+This is expected: turning a script into a library did not make
+anything magic happens. On the other hand, things are very
+different if we run the same code under Ypsilon.
+The first time the script is run it prints three lines::
+
+ $ ypsilon --r6rs use-mod3.ss
+ At expand-time x=1
+ At expand-time x=2
+ At run-time x=3
+
+However, if we run the script again it prints just one line::
+
+ $ ypsilon --r6rs use-mod3.ss
+ At run-time x=1
+
+The reason is that the first time Ypsilon compiles the libraries, using
+the same module instance, so that there is a single ``x`` variable which
+is incremented twice at expand time - the first time when ``mod2``
+is imported and the second time when ``mod3`` is imported - and
+once at run-time. The second time there is nothing
+to recompile, so only the runtime ``x`` variable is incremented, and
+there is no reference to the compile time instance.
+
+The situation for Ikarus is subtler. Apparently we get the same
+as before, when ``mod3`` was just a script::
+
+ $ ikarus --r6rs-script use-mod3.ss
+ At expand-time x=1
+ At run-time x=2
+
+However, this only happens because Ikarus is compiling all the libraries
+at the same time. If we use separate compilation we get::
+
+ $ ikarus --compile-dependencies use-mod3.ss
+ At expand-time x=1
+ Serializing "/home/micheles/gcode/scheme/experimental/mod3.sls.ikarus-fasl" ...
+ Serializing "/home/micheles/gcode/scheme/experimental/mod2.sls.ikarus-fasl" ...
+
+As you see, ``mod2`` the message ``At expand-time x=1`` is printed when
+``mod2`` is compiled. If we run the script ``use-mod3.ss`` now, we
+get just the runtime message::
+
+ $ ikarus --r6rs-script use-mod3.ss
+ At run-time x=1
+
+Both in Ikarus and in Ypsilon, the same invocation of the script
+returns different results, depending if the libraries have been
+precompiled or not. This is ugly and error prone. The full phase
+separation mechanism of PLT Scheme has been designed to avoid this
+problem: in PLT (and Larceny) one consistently gets always the same
+result. That is good.
+
+However, I think that both the PLT/Larceny people and the Ikarus/Ypsilon
+people are wrong. The PLT/Larceny people are wrong since full phase
+separation causes more problems than it solves: it is not the
+right solution to this problem. On the other hand, the Ikarus/Ypsilon
+people are wrong because they lack separate instantiation
+of modules. However, it would be trivial to get separate instantiation
+of modules for Ikarus and Ypsilon: it is enough to spawn
+two separate processes, run one after the other: the
+first to compile the script and its libraries, and the second to
+execute it. That would make sure that incrementing
+``x`` in the expansion phase could not influence the value of ``x``
+at runtime: in this way they could get repeatable results.
+
+In other words, in my own opinionated view the implementations
+with partial phase separation are much closer to the right path than
+implementation with full phase separation.
+
+ --------------------------------------------------------------------------
+| | single instantiation | multiple instantiation |
+ --------------------------------------------------------------------------
+| partial phase separation | not so bad | good |
+| full phase separation | bad (Larceny) | bad (PLT) |
+ --------------------------------------------------------------------------
+
+
+|#
+
+(import (rnrs) (sweet-macros) (for (aps lang) expand) (aps compat))
+
+;;DEF-BOOK
+(def-syntax (def-book name title author)
+ (: with-syntax
+ name-title (identifier-append #'name "-title")
+ name-author (identifier-append #'name "-author")
+ #'(begin
+ (define name (vector title author))
+ (define name-title (vector-ref name 0))
+ (define name-author (vector-ref name 1)))))
+
+;;END
+(pretty-print (syntax-expand (def-book bible "The Bible" "God")))
+
+
+ ;;TEST-DEF-BOOK
+ (test "def-book"
+ (let ()
+ (def-book bible "The Bible" "God")
+ (list bible-title bible-author))
+ (list "The Bible" "God"))
+ ;;END
+
+
+;;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))))
+ )
diff --git a/artima/scheme/scheme27.ss b/artima/scheme/scheme27.ss
deleted file mode 100644
index 81ba924..0000000
--- a/artima/scheme/scheme27.ss
+++ /dev/null
@@ -1,180 +0,0 @@
-#|Phase separation
-===============================================================
-
-Phase separation
--------------------------------------------------
-
-Phase separation is one of the trickiest concepts in Scheme macrology,
-and perhaps the one that gave me the most headaches when I was learning
-macros. It still beats me sometimes. Actually, the concept it is not
-that difficult, it is its practical implementation which is extremely
-tricky because it is *unspecified by the Scheme standard* and
-totally *implementation-dependendent*: worse than
-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:
-
-$$registry.ypsilon:
-
-You can run the example and you will get
-
-``registry: ((#<syntax m>)``
-
-as result (notice however that if you comment out the macro use, i.e.
-the ``(m)`` line, the registry will *not* be populated).
-So everything seems to work as one would expect.
-However, if you try to run the same
-example in Ikarus or in PLT Scheme or in most other R6RS Scheme
-implementations you will get an error. Let me
-show the PLT error message message, which is rather
-clear if you understand what phase separation is, wheread
-the Ikarus error message is somewhat misleading for reasons misterious to me::
-
- $ plt-r6rs registry.ypsilon.ss
- registry.ypsilon.ss:10:5: compile: unbound variable in module
- (in the transformer environment, which does not include the
- run-time definition) in: register
-
-Ypsilon, as most interpreted Scheme implementations, has no phase separation:
-there is no big difference between macros and functions, which are
-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.
-
-An example will clarify the point. Suppose we define a registry module
-as follows
-
-$$experimental/registry:
-
-(for convenience I am storing all this
-code in a package called ``experimental``) and suppose we use it as follows:
-
-$$use-registry.ikarus:
-
-In Ikarus everything works fine (in Ypsilon too of course)
-and running the script will return you something like
-
-::
-
- registering #<syntax m [char 83 of use-registry.ikarus.ss]>
- (#<syntax m [char 83 of use-registry.ikarus.ss]>)
-
-(notice the annotation about the position of the identifier ``m`` in
-the source code of the script, a signature of the fact that ``m``
-is a bona fide syntax object).
-
-In PLT Scheme instead running the script raise an error::
-
- $ plt-r6rs use-registry.ikarus.ss
- use-registry.ikarus.ss:5:5: compile: unbound variable in module
- (transformer environment) in: register
-
-.. image:: salvador-dali-clock.jpg
-
-The problem is that PLT Scheme has *strong phase separation*: by default
-names defined in external modules are imported *only* at runtime.
-In some sense this is absurd since
-names defined in an external pre-compiled modules
-are of course known at compile time
-(this is why Ikarus has no trouble to import them at compile time);
-nevertheless PLT Scheme and Larceny Scheme forces you to specify
-at which phase the functions must be imported. If you want to import
-them at expansion time (the time when macros are processed; often
-incorrectly used as synonymous for compilation time) you must say so:
-
-$$use-registry.mzscheme:
-
-Notice also that I did specify importation at run time for the
-``registry`` function, since it is called at runtime, i.e. not inside
-macros. If you run this script you will get::
-
- $ plt-r6rs use-registry.mzscheme.ss
- registering #<syntax:/home/micheles/gcode/artima/scheme/use-registry.mzscheme.ss:8:16>
- ()
-
-The PLT Scheme representation of syntax objects shows the line number and
-the column number, therefore you should interpret the previous out as
-*I am registering the syntax object defined in the source file
-use-registry.mzscheme.ss, at line 8 and column 16*, which corresponds
-to the identifier ``m``.
-
-This is close, but not quite cigar. The script now runs, but it
-returns a rather unexpected empty list. The reason why the registry is
-empty has nothing to do with phase separation, but rather
-another "feature" of PLT Scheme (and only of PLT Scheme) which goes
-under the name of multiple instantiation of modules. In practice,
-importing a module in PLT Scheme imports *an independent
-copy (instance) of the original module*.
-
-The imported instance of the module includes a copy of all bindings
-defined in the original module,
-*including the internal bindings which are not exported*.
-This remark explains why the registry is empty: the ``register``
-functions changes the ``_registry`` list in the *current* instance,
-but the value of ``_registry`` in the *original* instance (i.e. the
-value returned by the ``(registry)`` function) is left unchanged and
-is the same as at the beginning, i.e. the empty list.
-
-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*.
-|#
-
-(import (rnrs) (sweet-macros) (for (aps lang) expand) (aps compat))
-
-;;DEF-BOOK
-(def-syntax (def-book name title author)
- (: with-syntax
- name-title (identifier-append #'name "-title")
- name-author (identifier-append #'name "-author")
- #'(begin
- (define name (vector title author))
- (define name-title (vector-ref name 0))
- (define name-author (vector-ref name 1)))))
-
-;;END
-(pretty-print (syntax-expand (def-book bible "The Bible" "God")))
-
-
- ;;TEST-DEF-BOOK
- (test "def-book"
- (let ()
- (def-book bible "The Bible" "God")
- (list bible-title bible-author))
- (list "The Bible" "God"))
- ;;END
-
-
-;;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))))
- )
diff --git a/artima/scheme/table-of-contents.txt b/artima/scheme/table-of-contents.txt
index e5ad2fe..54b7b2a 100644
--- a/artima/scheme/table-of-contents.txt
+++ b/artima/scheme/table-of-contents.txt
@@ -41,7 +41,7 @@ Part III - Functional Aspects
Ep18_ Streams
-Part IV - Advanced Macrology
+Part IV - The Module System
.. _Ep1: http://www.artima.com/weblogs/viewpost.jsp?thread=238789
.. _Ep2: http://www.artima.com/weblogs/viewpost.jsp?thread=238941
diff --git a/artima/scheme/use-ct-mapping.ikarus.ss b/artima/scheme/use-ct-mapping.ikarus.ss
deleted file mode 100644
index a328e8e..0000000
--- a/artima/scheme/use-ct-mapping.ikarus.ss
+++ /dev/null
@@ -1,8 +0,0 @@
-(import (rnrs) (sweet-macros) (experimental ct-mapping))
-
-(def-syntax color (ct-mapping (red #\R) (green #\G) (yellow #\Y)))
-
-(display "Available colors: ")
-(display (color <names>))
-(display (list (color red) (color green) (color yellow)))
-(newline)
diff --git a/artima/scheme/use-ct-mapping.mzscheme.ss b/artima/scheme/use-ct-mapping.ss
index 2d16acb..2d16acb 100644
--- a/artima/scheme/use-ct-mapping.mzscheme.ss
+++ b/artima/scheme/use-ct-mapping.ss
diff --git a/artima/scheme/use-registry.ikarus.ss b/artima/scheme/use-registry.ikarus.ss
deleted file mode 100644
index 59edde1..0000000
--- a/artima/scheme/use-registry.ikarus.ss
+++ /dev/null
@@ -1,9 +0,0 @@
-(import (rnrs) (sweet-macros) (experimental registry))
-
-(def-syntax m
- (begin
- (register #'m)
- (syntax-match () (sub (m) #'42))))
-
-(m)
-(display (registry))
diff --git a/artima/scheme/use-registry.mzscheme.ss b/artima/scheme/use-registry.mzscheme.ss
deleted file mode 100644
index eaa803d..0000000
--- a/artima/scheme/use-registry.mzscheme.ss
+++ /dev/null
@@ -1,12 +0,0 @@
-#!r6rs
-(import (rnrs) (sweet-macros)
- (for (only (experimental registry) register) expand)
- (for (only (experimental registry) registry) run))
-
-(def-syntax m
- (begin
- (register #'m)
- (syntax-match () (sub (m) #'42))))
-
-(m)
-(display (registry))
diff --git a/artima/scheme/use-registry.ss b/artima/scheme/use-registry.ss
new file mode 100644
index 0000000..c480601
--- /dev/null
+++ b/artima/scheme/use-registry.ss
@@ -0,0 +1,13 @@
+#!r6rs
+(import (rnrs) (sweet-macros) (only (aps compat) printf)
+ (for (only (experimental registry) register) expand)
+ (for (only (experimental registry) registry) run)
+ (experimental def-m1))
+
+(def-syntax m2
+ (begin
+ (register #'m2)
+ (syntax-match () (sub (m2) #'2))))
+
+(m2)
+(printf "Registered ~a macro(s)\n" (length (registry)))