summaryrefslogtreecommitdiff
path: root/artima
diff options
context:
space:
mode:
authormichele.simionato <devnull@localhost>2009-06-20 13:43:55 +0000
committermichele.simionato <devnull@localhost>2009-06-20 13:43:55 +0000
commitf35fd45d4cbb3ac3b7c177cdbd2d4bbefd63a074 (patch)
tree78a961becbdd3f11222bd023a05ac5c7fb4d0db9 /artima
parentdb02e3d038bb6978307a96fb43fe602fc17f3612 (diff)
downloadmicheles-f35fd45d4cbb3ac3b7c177cdbd2d4bbefd63a074.tar.gz
Published scheme29
Diffstat (limited to 'artima')
-rw-r--r--artima/scheme/Makefile4
-rw-r--r--artima/scheme/scheme22.ss3
-rw-r--r--artima/scheme/scheme28.ss416
-rw-r--r--artima/scheme/scheme29.ss220
4 files changed, 419 insertions, 224 deletions
diff --git a/artima/scheme/Makefile b/artima/scheme/Makefile
index 3ca9ca6..6f41a13 100644
--- a/artima/scheme/Makefile
+++ b/artima/scheme/Makefile
@@ -74,8 +74,8 @@ linkcheck:
@echo "Link check complete; look for any errors in the above output " \
"or in .build/linkcheck/output.txt."
-POST = python2.5 ../post.py
-RST = python2.5 ../../scheme2rst.py -r
+POST = python ../post.py
+RST = python ../../scheme2rst.py -r
1: scheme1.ss
$(RST) scheme1.ss; $(POST) scheme1.rst 238789
diff --git a/artima/scheme/scheme22.ss b/artima/scheme/scheme22.ss
index a52f387..0c6e0b2 100644
--- a/artima/scheme/scheme22.ss
+++ b/artima/scheme/scheme22.ss
@@ -217,7 +217,7 @@ the whole of R6RS. You could even perform the opposite, and remove ``set!``
from the run-time, but allowing it at compile time.
However, personally I do not feel a need to distinguish the languages
-at different phases and I like Scheme to be a Lisp-1 language with a
+at different phases and I like Scheme to be a Lisp-1_ language with a
single namespace for all variables. I am also not happy with having
to keep manually track of the meta-levels, which is difficult and
error prone when writing higher order macros. Moreover, in PLT and
@@ -238,5 +238,6 @@ the programmer does not need to think about it explicitly). The model
of implicit phasing was proposed by Kent Dybvig and
Abdul Aziz Ghuloum, who wrote his `Ph. D. thesis`_ on the subject.
+.. _Lisp-1: http://en.wikipedia.org/wiki/Lisp-1#The_function_namespace
.. _Ph. D. thesis: http://portal.acm.org/citation.cfm?id=1291151.1291197&coll=GUIDE&dl=GUIDE&CFID=34012650&CFTOKEN=38507862
|#
diff --git a/artima/scheme/scheme28.ss b/artima/scheme/scheme28.ss
index 32ec794..af25a29 100644
--- a/artima/scheme/scheme28.ss
+++ b/artima/scheme/scheme28.ss
@@ -1,26 +1,38 @@
#|Hygienic macros
==============================================================
-In episode 9_ I akwnoledged the fact that Scheme provides at least three
-different macro systems - ``syntax-rules``, ``syntax-case`` and
-``define-macro`` - yet I have
-decided to explain only my own personal macro system - ``sweet-macros``.
-The decision was motivated by pedagogical reasons, because I did not want
-to confuse my readers by describe too many macro frameworks
-at the same time, and also because I want to make macros
-easier, by providing a nicer syntax and introspection features.
-However now, after more than a dozen
-episodes about macros, I can assume my readers are beginners no more,
-and it is time to have a look at the larger Scheme world and to
-compare/contrast ``sweet-macro`` with the most used systems, i.e.
-``syntax-rules``, ``syntax-case`` and ``define-macro``.
-
-Actually, there a few other intesting options
-out there, such as syntactic closures and systems based on explicit
-renaming. Since I do not want to discuss all the macro systems in existence
-here, I will point out a good resource on the
-subject: this excellent `post by Alex Shinn`_ on the Chicken mailing list
-summarizes the situation better than I could do.
+In episode 9_ I noted that Scheme provides three
+major macro systems (``syntax-rules``, ``syntax-case`` and
+``define-macro``), yet I went on to discuss
+my own personal macro system, ``sweet-macros``.
+The decision was motivated by various reasons.
+First of all, I did not want
+to confuse my readers by describing too many macro systems
+at the same time. Secondly, I wanted to make macros
+easier and more debuggable. Finally, ``sweet-macros`` are
+slightly more powerful than the other macro systems, with a better
+support for guarded patterns and with a few extensibility features
+which I have not shown yet (but I like to keep some trick under my
+sleeve ;).
+
+After 19 episodes about macros, I can safely assume that my
+readers are not beginners anymore. It is time to have a look at the
+larger Scheme world and to compare/contrast ``sweet-macros`` with the
+other macro systems. I do not want to discuss all the macro
+systems in existence here, therefore I will skip a few interesting systems
+such as syntactic closures and explicit renaming macros. However,
+readers interested in alternative macro systems for Scheme should have
+a look at this excellent `post by Alex Shinn`_ which summarizes the
+current situation better than I could do. Notice that Alex is
+strongly biased against ``syntax-case`` and very much in favor of
+explicit renaming macros. The two
+systems are not incompatible though, and actually Larceny provides a
+``syntax-case`` implementation built on top of explicit renaming
+macros (see also SRFI-72_).
+
+.. _SRFI-72: http://srfi.schemers.org/srfi-72/srfi-72.html
+.. _The Scheme Programming Language: http://www.scheme.com/tspl3/
+.. _Syntax-Rules Primer for the Mildly Insane: http://groups.google.com/group/comp.lang.scheme/browse_frm/thread/86c338837de3a020/eb6cc6e11775b619?#eb6cc6e11775b619
``syntax-match`` vs ``syntax-rules``
-----------------------------------------------------------------
@@ -31,19 +43,22 @@ summarizes the situation better than I could do.
(def-syntax (syntax-rules (literal ...) (patt templ) ...)
#'(syntax-match (literal ...) (sub patt #'templ) ...))
-As you see, the difference between ``syntax-rules`` (a part for
-missing the ``sub`` literal) is that ``syntax-rules`` automatically
+As you see, the main difference between ``syntax-rules`` (apart for a
+missing ``sub``) is that ``syntax-rules`` automatically
adds the syntax-quote ``#'`` operator to you templates. That means
that you cannot use quasisyntax tricks and that ``syntax-rules`` is
-strictly less powerful than ``syntax-match``. Moreover,
-``syntax-rules`` macros do not have guarded patterns; the most direct
-consequence is that providing good error messages for wrong syntaxes
-is more difficult.
+strictly less powerful than ``syntax-match``. The other difference is
+that ``syntax-rules`` macros do not have guarded patterns; the most direct
+consequence is that providing good error messages for incorrect syntax
+is more difficult. You may learn everything you ever wanted to know
+about ``syntax-rules`` in the `Syntax-Rules Primer for the Mildly Insane`_
+by Joe Marshall.
+
``syntax-match`` vs ``syntax-case``
-----------------------------------------------------------------
-``syntax-case`` could be defined in terms of ``syntax-match`` as
+In principle, ``syntax-case`` could be defined in terms of ``syntax-match`` as
follows::
(def-syntax syntax-case
@@ -57,17 +72,35 @@ follows::
In reality, ``syntax-case`` is a Scheme primitive and
``syntax-match`` is defined on top of it. So, ``syntax-case`` has
theoretically the same power as ``syntax-match``, but in practice
-``syntax-match`` is more convenient to use because of the
-introspection features.
+``syntax-match`` is more convenient to use.
-The major syntactic difference (apart from the absense of the ``sub`` literal)
+The major syntactic difference
is the position of the guard, which in ``syntax-case`` is positioned *before*
-the skeleton, whereas in ``syntax-match`` is positioned *after* the skeleton.
-Changing the position has cost me a lot of reflection, since I *hate*
-gratuitous breaking. However, I am convinced that the position of the
+the skeleton, whereas in ``syntax-match`` is positioned *after* the skeleton
+I did spent a lot of time thinking about the right position for the guard:
+I hate gratuitous breaking, but I convinced myself that the position of the
guard in ``syntax-case`` is really broken, so I had to *fix* the issue.
+The problem with ``syntax-case`` is that while the pattern is always
+in the first position, you never know what is in the
+second position: it could be the guard *or* the template; in order
+to distinguish the possibilities you have to check if there is
+a third expression in the clause and that is annoying,
+
+In ``syntax-match`` the template
+is *always* in the second position; if there is something in the
+third position, it is always a guard; moreover, there could be
+another expression in the clause (in the fourth position) which is
+used as alternative template if the guard is not satisfied.
+The advantage of having fixed positions is that it is easier to
+write higher order macros expanding to ``syntax-match`` transformers
+(``syntax-match`` itself is implemented in terms of ``syntax-case``
+where the template are not in fixed position and I had to make the
+implementation more complex just to cope with that).
+
+You may learn (nearly) everything there is to know
+about ``syntax-case`` in the the book
+`The Scheme Programming Language`_ by Kent Dybvig.
-XXX: why do I say so?
``syntax-match`` versus ``define-macro``
---------------------------------------------------------------
@@ -75,7 +108,7 @@ XXX: why do I say so?
Nowadays macros based on ``define-macro`` are much less used than in
past because macro systems based on pattern matching are much more
powerful, easier and safer to use. The R6RS specification made
-``syntax-case`` enter in the standard and this is the preferred macro
+``syntax-case`` enter in the standard and it is the preferred macro
system for most implementation.
Nowadays, there is a good chance that your Scheme implementation does
@@ -87,8 +120,16 @@ $$DEFINE-MACRO
The code should be clear: the arguments of the macro are converted into
a regular list which is then transformed with the expander, and converted
-back into a syntax object in the context of the macro.
+back into a syntax object in the context of the macro.
+``define-macro`` macros are based on simple list manipulations and
+are very easy to explain and to understand: unfortunately, they
+are affected by the hygiene problem.
+
+You can find examples of use
+of ``define-macro`` in many references; I learned it from
+`Teach Yourself Scheme in Fixnum Days`_ by Dorai Sitaram.
+.. _Teach Yourself Scheme in Fixnum Days: http://www.ccs.neu.edu/home/dorai/t-y-scheme/t-y-scheme-Z-H-1.html
.. _9: http://www.artima.com/weblogs/viewpost.jsp?thread=240804
.. _On Lisp: http://www.paulgraham.com/onlisp.html
.. _hygiene in R6RS: http://docs.plt-scheme.org/r6rs-lib-std/r6rs-lib-Z-H-13.html#node_sec_12.1
@@ -99,181 +140,137 @@ The hygiene problem
If you have experience in Common Lisp or other Lisp dialects, you will
have heard about the problem of hygiene in macros, a.k.a. the problem
-of *variable capture*. In Scheme you have the same problem if you use
-``define-macro``. The issue is that having macros
-introducing identifiers implicitly can cause unespected side
-effects. As Paul Graham puts it,
+of *variable capture*. As Paul Graham puts it,
*errors caused by variable capture are rare, but what they lack
in frequency they make up in viciousness*.
-
-The hygiene problem is the main reason why `define-macro`` is becoming
+The hygiene problem is the main reason why ``define-macro`` is becoming
less and less used in the Scheme world. PLT Scheme has being
-deprecating it for many years and nowadays even Chicken Scheme, which
-traditionally used ``define-macro`` a lot, has removed it from the
+deprecating it for years and nowadays even Chicken Scheme, which
+traditionally used ``define-macro``, has removed it from the
core, by using hygienic macros instead: this is the reason why the
-current Chicken (Chicken 4.0) is called "hygienic Chicken".
+current Chicken (Chicken 4) is called "hygienic Chicken".
-You can find good discussions of the hygiene problem in Common Lisp
-in many places; I am familiar with Paul Graham's book `On Lisp`_ which
-I definitively recommend: the chapter on variable chapter is the best
-reference I know. Another good reference is the chapter
-about ``syntax-case`` - by Kent Dybvig - in the book `Beautiful Code`_.
-Here I will give just a short example exhibiting the problem, for the
-sake of the readers unfamiliar with it.
+You can find good discussions of the hygiene problem in Common Lisp in
+many places; I am familiar with Paul Graham's book `On Lisp`_ which I
+definitively recommend: chapter 9 on variable capture
+has influenced this section. Another good
+reference is the chapter about ``syntax-case`` - by Kent Dybvig - in
+the book `Beautiful Code`_. Here I will give just a short example
+exhibiting the problem, for the sake of the readers unfamiliar with
+it.
.. image:: hygienic-paper-small.jpg
-Consider for instance this "dirty" definition of the ``for`` loop:
+Consider this "dirty" definition of the ``for`` loop:
+
+$$DIRTY-FOR
+
+Superficially ``define-macro`` looks quite similar to ``def-syntax``,
+except that in the macro body you need to a add a comma in front of
+each macro argument argument. Internally, however, macros based on
+``define-macro`` are completely different. In particular, they are not
+safe under variable capture and that may cause surprises.
+For instance, code such as
.. code-block:: scheme
- (define-macro (dirty-for i i1 i2 . body)
- `(let ((start ,i1) (stop ,i2))
- (let loop ((i start))
- (unless (>= i stop) ,@body (loop (+ 1 i))))))
+ > (let ((start 42))
+ (dirty-for i 1 3 (display start) (newline)))
+ 1
+ 1
+
+prints the number 1 (twice) and not the number 42!
-The mistake here is having forgotten to ``gensym`` the newly
-introduced variables ``start`` and ``stop``, a common mistake for
-beginners (and occasionally, even for non beginners). That means that
-the macro is not safe under variable capture and indeed code such as
+The reason is clear if you expand the macro (notice
+the if you implement ``define-macro`` in terms of
+``syntax-match`` then ``syntax-expand`` still works):
.. code-block:: scheme
- > (let ((start 42))
- (dirty-for i 1 3 (print start)))
- 11
-
-prints twice the number 1 and not the number 42. On the other hand,
-everything works fine if the ``for`` macro is defined using
-``def-syntax``. There is no doubt that ``def-syntax`` is
-nicer/simpler to writer than ``define-macro`` *and* much less error
-prone.
-
-The other problem with non-hygienic macros is that introduced identifiers
-will have the scope of expanded code, not the scope of the original macro:
-that means that if the outer scope redefines the meaning of an
-identifier used internally, the macro will work in an unexpected way.
-Consider for instance the following expression:
+ > (syntax-expand (dirty-for i 1 3 (display start) (newline)))
+ (let ((start 1) (stop 3))
+ (let loop ((i start))
+ (unless (>= i stop) (display start) (newline)
+ (loop (+ 1 i)))))
-> (let ((unless 'unless))
- (dirty-for i 1 3 (print i)))
+Since the inner variable ``start`` is shadowing the outer variable ``start``,
+the number 1 is printed instead of the number 42.
+The problem can be solved by introducing unique identifiers in the
+macro by means of ``gensym``
+(``gensym`` is not in the R6RS standard, but in practice every Scheme
+implementation has it; for convenience I have included it in my
+``(aps compat)`` compatibility library).
-There is an error here because shadowing ``unless`` affects the
-``dirty-for`` macro. This is pretty tricky to debug: in practice, it
-means that the macro user is forced to know all the identifiers
-that are used internally by the macro.
+The ``dirty-for`` macro
+can be improved to use ``gensym`` for every variable which is internally
+defined (and it is not a macro argument):
-.. _Beautiful Code: http://oreilly.com/catalog/9780596510046/
+$$FOR-WITH-GENSYM
-Breaking hygiene
--------------------------------------------------
+.. code-block:: scheme
-If you only use hygienic macros, the hygiene probleme does not exist.
-However, there is the opposite problem: you need a way of breaking
-hygiene on purpose. Consider for instance the following apparently
-trivial macro:
+ > (let ((start 42))
+ (less-dirty-for i 1 3 (display start) (newline)))
+ 42
+ 42
+
+``less-dirty-for`` works because all internal variables have now
+unique names that cannot collide with existing identifiers by
+construction. You can see the names used internally by invoking
+``syntax-expand`` (notice that by construction such names change every
+time you expand the macro):
.. code-block:: scheme
- (def-syntax (define-a x)
- #`(define a x))
-
-``(define-a x)`` *apparently* should expand to ``(define a x)``, so
-you may find the following surprising::
-
- > (define-a 1)
- > a
- Unhandled exception
- Condition components:
- 1. &undefined
- 2. &who: eval
- 3. &message: "unbound variable"
- 4. &irritants: (a)
-
-Why is the variable ``a`` not defined? The reason is that Scheme
-macros are *hygienic*, i.e. they *do not introduce identifiers
-implicitly*. Auxiliary names introduced in a macro *are not visible
-outside*: the only names which enter in the expansion are the ones we
-put in.
-
-This is a major difference with respect to Common Lisp macros. In
-Common Lisp the mechanism of macro expansion is much simpler to
-explain, since the expansion is literal: you could just cut and paste
-the result of the expansion in your original code. In Scheme instead
-the expansion is not literally inserted in the original code, and a
-lot of magic takes place to avoid name clashes. In practice, the
-implementation of Scheme macros takes care of distinguishing the
-introduced identifiers with some specific mechanism (it
-could be based on marking the names, or on explicit renaming).
-
-Once you get used to the idea that the expansion is not
-literal, and that all the identifiers internally used by a macro
-are opaque unless they are explicitly marked as visible, you will
-see the advantages of hygiene.
-
-For instance, if you are writing a library which can be imported
-in an unknown environment, in absence of hygiene you could introduce
-name clashes impossible to foresee in advance, and that could be solved
-only by the final user, which however will likely be ignorant of how
-your library works.
-
-To be fair, I should
-remark that in Common Lisp there
-are ways to work around the absence of hygiene;
-nevertheless I like the Scheme way
-better, since by default you cannot introduce unwanted names. If
-want to introduce new names you can, but you must say so. Introducing
-new names in a macro is called *breaking hygiene*.
-
-Scheme provides a builtin mechanism to break hygiene, via the
-``datum->syntax`` utility which converts
-literal objects (*datums*) into syntax objects.
-
-I have already shown ``datum->syntax``
-at work in the definition of ``define-macro`` from ``syntax-match``,
-where it was used to convert a list describing source code into a syntax
-object.
-
-A typical use case for ``datum->syntax`` is to turn symbols
-into proper identifiers which can be introduced in macros and made
-visible to expanded code, thus breaking hygiene. Here is how
-you can "fix" the macro ``define-a``:
-
-$$DEFINE-A
-
-Notice that I have used the name of the macro as the context identifier
-in ``datum->syntax``. This is the common practice, but any dummy
-identifier would have worked for this example. You can check that
-the identifier ``a`` is really introduced as follows:
+ > (syntax-expand (less-dirty-for i 1 3 (display i))); in Ikarus
+ (let ((#{g0 |K!ZoUGmIIl%SrMfI|} 1) (#{g1 |qecoGeEAOv0R8$%0|} 3))
+ (let #{g2 |kD?xfov61j0$G1=M|} ((i #{g0 |K!ZoUGmIIl%SrMfI|}))
+ (unless (>= i #{g1 |qecoGeEAOv0R8$%0|}) (display i)
+ (#{g2 |kD?xfov61j0$G1=M|} (+ 1 i)))))
+
+Unfortunately ``less-dirty-for`` is not really clean.
+``gensym`` cannot do anything to solve the *free-symbol capture*
+problem (I am using Paul Graham's terminology here).
+
+The problem is that identifiers used (but not defined) in the macro
+have the scope of expanded code, not the scope of the original macro:
+in particular, if the outer scope in expanded code redefines the meaning of an
+identifier used internally, the macro will not work as you would expect.
+Consider for instance the following expression:
.. code-block:: scheme
- > (define-a 1)
- > a
- 1
+ > (let ((unless 'unless))
+ (less-dirty-for i 1 3 (display i)))
-A more realistic example is to build identifiers from strings and
-symbols. For that purpose I have added an ``identifier-append``
-utility in my ``(aps lang)`` library, defined as follow:
+If you try to run this, your system will go into an infinite loop!
-$$lang:IDENTIFIER-APPEND
+The problem here is that shadowing ``unless`` in the outer scope
+affects the inner working of the macro (I leave as an exercise to
+understand what exactly is going on).
+As you can easily see, this
+kind of problem is pretty tricky to debug: in practice, it
+means that the macro user is forced to know all the identifiers
+that are used internally by the macro.
-Here is a simple ``def-book`` macro using ``identifier-append``:
+On the other hand,
+if you use hygienic macros, all the subtle
+problems I described before simply disappear and
+you can write a clean for loop just as it should be written:
-$$DEF-BOOK
+$$CLEAN-FOR
-to be used as in the following test:
+Everything works fine with this definition, and the macro looks
+even better, with less commas and splices ;-)
-$$TEST-DEF-BOOK
+That's all for today. The next episode will discuss how to break
+hygiene on purpose, don't miss it!
+
+.. _Beautiful Code: http://oreilly.com/catalog/9780596510046/
-There are better ways to define records in Scheme, and there is also
-a built-in facility to define record types: you should consider
-``def-book`` just as an example of use of ``identifier-append``,
-not as a recommended pattern to define records.
|#
-(import (rnrs) (sweet-macros) (aps lang) (aps list-utils) (aps compat)
- (aps test-utils))
+(import (rnrs) (sweet-macros) (aps lang) (aps list-utils) (aps compat))
;DEFINE-MACRO
(def-syntax define-macro
@@ -286,51 +283,30 @@ not as a recommended pattern to define records.
))
;END
-;;DEFINE-A
-(def-syntax (define-a x)
- #`(define #,(datum->syntax #'define-a 'a) x))
-;;END
+;DIRTY-FOR
+ (define-macro (dirty-for i i1 i2 . body)
+ `(let ((start ,i1) (stop ,i2))
+ (let loop ((,i start))
+ (unless (>= ,i stop) ,@body (loop (+ 1 ,i))))))
+;END
+
+;FOR-WITH-GENSYM
+ (define-macro (less-dirty-for i i1 i2 . body)
+ (let ((start (gensym)) (stop (gensym)) (loop (gensym)))
+ `(let ((,start ,i1) (,stop ,i2))
+ (let ,loop ((,i ,start))
+ (unless (>= ,i ,stop) ,@body (,loop (+ 1 ,i)))))))
+;END
+
+;CLEAN-FOR
+ (def-syntax (clean-for i i1 i2 body1 body2 ...)
+ #'(let ((start i1) (stop i2))
+ (let loop ((i start))
+ (unless (>= i stop) body1 body2 ... (loop (+ 1 i))))))
+;END
-;DEFINE-A-NH
-(define-macro (define-a* x)
+;DEFINE-A
+(define-macro (define-a x)
`(define a ,x))
;END
-;;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 inner-name (vector title author))
- (define name-title (vector-ref inner-name 0))
- (define name-author (vector-ref inner-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
-
-(def-syntax (let-3 name list-3 body body* ...)
- #`(let+ ((x y z) list-3)
- (let ((#,(identifier-append #'name ".x") x)
- (#,(identifier-append #'name ".y") y)
- (#,(identifier-append #'name ".z") z))
- body body* ...)))
-
-(pretty-print (syntax-expand
- (let-3 v '(a b c)
- (display (list v.x v.y v.z)))
-))
-
-
-(let-3 v '(a b c)
- (display (list v.x v.y v.z)))
-(newline)
diff --git a/artima/scheme/scheme29.ss b/artima/scheme/scheme29.ss
index 1245246..af83c0a 100644
--- a/artima/scheme/scheme29.ss
+++ b/artima/scheme/scheme29.ss
@@ -1,6 +1,160 @@
#|Comparing identifiers
==============================================================
+
+Hygienic vs non-hygienic macro systems
+----------------------------------------------
+
+Understanding the hygiene issue is important if you intend to work
+in the larger Lisp world. In the small Scheme world
+everybody thinks that hygiene is an essential feature and nowadays
+all major Scheme implementations provide hygienic macros;
+however, in the rest of the world things are different.
+
+Common Lisp does not use hygienic macros and cope with the
+variable capture problem by using ``gensym``; the free symbol
+capture problem is not solved, but it is rare. Also the fact
+that Common Lisp has multiple namespaces and a package system
+helps to mitigate the issue.
+
+The hygiene problem is more
+serious in Lisp-1_ dialects like the newborn Arc_ and Clojure_:
+still their authors, which are extremely competent programmers,
+have decided not to use hygienic macros and to rely on various workarounds.
+
+Personally I have made my mind up and I am in the pro-hygiene camp now.
+I should admit that for a long time I have been in the opposite
+camp, preferring the simplicity of ``define-macro`` over
+the complexity of ``syntax-case``. It turns out I was wrong.
+The only problem of ``syntax-case`` is a cosmetic one: it
+looks very complex and cumbersome to use, but that can be easily
+solved by providing a nicer API - which I did with ``sweeet-macros``.
+I believe that
+eventually all Lisp dialects will start using hygienic macros, but
+that could take decades, because of inertia and backward-compatibility
+concerns.
+
+Having attended to a talk on the subject at the `EuroLisp
+Symposium`_, I will mention here that Pascal Costanza has found a way to
+implement hygienic macros on top of ``defmacro`` in Common
+Lisp *portably*. There is no technical reason why
+hygienic macros are not widespread in the whole Lisp world,
+just a matter of different opinions on the importance of
+the problem and the different tradeoffs.
+
+
+.. _Arc: http://arclanguage.org/
+.. _Clojure: http://clojure.org/
+.. _Lisp-1: http://en.wikipedia.org/wiki/Lisp-1#The_function_namespace
+.. _EuroLisp Symposium: http://www.european-lisp-symposium.org/
+
+The hygiene problem (II)
+-------------------------------------------------
+
+However, there is the opposite problem: you need a way of breaking
+hygiene on purpose. Consider for instance the following apparently
+trivial macro:
+
+$$DEFINE-A
+
+``(define-a x)`` *apparently* should expand to ``(define a x)``, so
+you may find the following surprising::
+
+ > (define-a 1)
+ > a
+ Unhandled exception
+ Condition components:
+ 1. &undefined
+ 2. &who: eval
+ 3. &message: "unbound variable"
+ 4. &irritants: (a)
+
+Why is the variable ``a`` not defined? The reason is that Scheme
+macros are *hygienic*, i.e. they *do not introduce identifiers
+implicitly*. Auxiliary names introduced in a macro *are not visible
+outside*: the only names which enter in the expansion are the ones we
+put in.
+
+This is a major difference with respect to Common Lisp macros. In
+Common Lisp the mechanism of macro expansion is much simpler to
+explain, since the expansion is literal: you could just cut and paste
+the result of the expansion in your original code. In Scheme instead
+the expansion is not literally inserted in the original code, and a
+lot of magic takes place to avoid name clashes. In practice, the
+implementation of Scheme macros takes care of distinguishing the
+introduced identifiers with some specific mechanism (it
+could be based on marking the names, or on explicit renaming).
+
+Once you get used to the idea that the expansion is not
+literal, and that all the identifiers internally used by a macro
+are opaque unless they are explicitly marked as visible, you will
+see the advantages of hygiene.
+
+For instance, if you are writing a library which can be imported
+in an unknown environment, in absence of hygiene you could introduce
+name clashes impossible to foresee in advance, and that could be solved
+only by the final user, which however will likely be ignorant of how
+your library works.
+
+To be fair, I should
+remark that in Common Lisp there
+are ways to work around the absence of hygiene;
+nevertheless I like the Scheme way
+better, since by default you cannot introduce unwanted names. If
+want to introduce new names you can, but you must say so. Introducing
+new names in a macro is called *breaking hygiene*. Since I like
+to keep you in suspense, I will close this episode here, and I will
+make you wait for the next one to discover how to break hygiene ;-)
+
+Breaking hygiene
+--------------------------------------------
+
+Scheme provides a builtin mechanism to break hygiene, via the
+``datum->syntax`` utility which converts
+literal objects (*datums*) into syntax objects.
+
+I have already shown ``datum->syntax``
+at work in the definition of ``define-macro`` from ``syntax-match``,
+where it was used to convert a list describing source code into a syntax
+object.
+
+A typical use case for ``datum->syntax`` is to turn symbols
+into proper identifiers which can be introduced in macros and made
+visible to expanded code, thus breaking hygiene. Here is how
+you can "fix" the macro ``define-a``:
+
+$$DEFINE-A
+
+Notice that I have used the name of the macro as the context identifier
+in ``datum->syntax``. This is the common practice, but any dummy
+identifier would have worked for this example. You can check that
+the identifier ``a`` is really introduced as follows:
+
+.. code-block:: scheme
+
+ > (define-a 1)
+ > a
+ 1
+
+A more realistic example is to build identifiers from strings and
+symbols. For that purpose I have added an ``identifier-append``
+utility in my ``(aps lang)`` library, defined as follow:
+
+$$lang:IDENTIFIER-APPEND
+
+Here is a simple ``def-book`` macro using ``identifier-append``:
+
+$$DEF-BOOK
+
+to be used as in the following test:
+
+$$TEST-DEF-BOOK
+
+There are better ways to define records in Scheme, and there is also
+a built-in facility to define record types: you should consider
+``def-book`` just as an example of use of ``identifier-append``,
+not as a recommended pattern to define records.
+
What does it mean that two identifiers are equal in a lexically
scoped language with hygienic macros?
@@ -156,7 +310,18 @@ considered ``free-identifier=?``:
> (free-identifier=? #'a #'b)
#f
-Moreover
+Moreover you can use ``free-identifier=?`` to compare two unbound
+identifiers or a bound and an unbound identifier. It returns true only
+if the identifiers share the same name and the same bound/unbound state.
+You can leverage on this feature to define a function which is able
+to determine if an identifier is bound or unbound:
+
+$$lang:UNBOUND?
+
+Using ``unbound?`` it is easy to write code that performs a certain
+action only if a given identifier is bound:
+
+$$WHEN-BOUND
For things like cond which need to distinguish macro-specific literals
from bound names (e.g.: (let ((else 1)) (cond (else ---)))),
@@ -311,3 +476,56 @@ the macros using the auxiliary syntax.
(display (let ((a 1))
(compare-ids a a)))
+
+;;WHEN-BOUND
+(def-syntax (when-bound (id) e* ...)
+ #'#f (unbound? #'id) #'(begin e* ...))
+;;END
+;;DEFINE-A
+(def-syntax (define-a x)
+ #`(define #,(datum->syntax #'define-a 'a) x))
+;;END
+
+;;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 inner-name (vector title author))
+ (define name-title (vector-ref inner-name 0))
+ (define name-author (vector-ref inner-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
+
+(def-syntax (is-name x)
+ #'(begin (display 'x) (display " is a name\n"))
+ (identifier? #'x)
+ #'(begin (display 'x) (display " is not a name\n")))
+
+(def-syntax (let-3 name list-3 body body* ...)
+ #`(let+ ((x y z) list-3)
+ (let ((#,(identifier-append #'name ".x") x)
+ (#,(identifier-append #'name ".y") y)
+ (#,(identifier-append #'name ".z") z))
+ body body* ...)))
+
+(pretty-print (syntax-expand
+ (let-3 v '(a b c)
+ (display (list v.x v.y v.z)))
+))
+
+
+(let-3 v '(a b c)
+ (display (list v.x v.y v.z)))
+(newline)