summaryrefslogtreecommitdiff
path: root/artima
diff options
context:
space:
mode:
authormichele.simionato <devnull@localhost>2009-06-02 13:40:46 +0000
committermichele.simionato <devnull@localhost>2009-06-02 13:40:46 +0000
commit01b53681d84a0da8af948553674c66e1f45e04e0 (patch)
treeef93f3db3506bf752944ebfa6cee709d2c09ddcc /artima
parent1acfc8e3c06f500c738d8550def3ac16ece77dc7 (diff)
downloadmicheles-01b53681d84a0da8af948553674c66e1f45e04e0.tar.gz
Fixed a lot of misprints signaled by Marco Maggi
Diffstat (limited to 'artima')
-rw-r--r--artima/scheme/Makefile7
-rw-r--r--artima/scheme/scarti.txt29
-rw-r--r--artima/scheme/scheme11.ss10
-rw-r--r--artima/scheme/scheme12.ss18
-rw-r--r--artima/scheme/scheme13.ss18
-rw-r--r--artima/scheme/scheme19.ss3
-rw-r--r--artima/scheme/scheme2.ss14
-rw-r--r--artima/scheme/scheme20.ss23
-rw-r--r--artima/scheme/scheme25.ss117
-rw-r--r--artima/scheme/scheme26.ss208
-rw-r--r--artima/scheme/scheme27.ss10
-rw-r--r--artima/scheme/scheme28.ss (renamed from artima/scheme/macro4.ss)132
-rw-r--r--artima/scheme/scheme29.ss (renamed from artima/scheme/macro5.ss)57
-rw-r--r--artima/scheme/scheme3.ss64
-rw-r--r--artima/scheme/scheme5.ss42
-rw-r--r--artima/scheme/scheme7.ss18
-rw-r--r--artima/scheme/scheme8.ss20
-rw-r--r--artima/scheme/table-of-contents.txt2
18 files changed, 456 insertions, 336 deletions
diff --git a/artima/scheme/Makefile b/artima/scheme/Makefile
index 925e8bc..ae94d9a 100644
--- a/artima/scheme/Makefile
+++ b/artima/scheme/Makefile
@@ -143,6 +143,9 @@ table-of-contents: table-of-contents.txt
fourth-cycle: fourth-cycle.txt
$(POST) fourth-cycle.txt 253071
+eurolisp2: eurolisp2.txt
+ $(POST) eurolisp2.txt 259100
+
19: scheme19.ss
$(RST) scheme19.ss; $(POST) scheme19.rst 251476
@@ -160,3 +163,7 @@ fourth-cycle: fourth-cycle.txt
24: scheme24.ss
$(RST) scheme24.ss; $(POST) scheme24.rst 258103
+
+25: scheme25.ss
+ $(RST) scheme25.ss; $(POST) scheme25.rst 258580
+
diff --git a/artima/scheme/scarti.txt b/artima/scheme/scarti.txt
index 65315d4..edfa2b2 100644
--- a/artima/scheme/scarti.txt
+++ b/artima/scheme/scarti.txt
@@ -767,11 +767,12 @@ This is *wanted* behavior. According to Matthew Flatt:
the same way that the compiler complains when a module contains a free
variable.*
-However I am not convinced by this argument. Whereas it is true that one should
-be very careful when mixing compile-time and run-time, especially in
-presence of side effects, I believe one can solve the issue by using
-separate compilation, and that the benefits of the tower of meta-levels are not
-compensated by the complications it induces.
+However I am not convinced by this argument. Whereas it is true that
+one should be very careful when mixing compile-time and run-time,
+especially in presence of side effects, I believe one can solve the
+issue by using separate compilation, and that the benefits of the
+tower of meta-levels are not compensated by the complications it
+induces.
My wishlist for a Scheme implementation
@@ -805,15 +806,15 @@ I have a personal wishlist of features.
- missing features from the standard
- I think the standard lacks many important features. I have
- already noticed that I would welcome the ability to export all names from a
- library ``(export *)`` and the ability to introspect the names exported
- by library. In addition,
- I would welcome a mechanism to write helper functions for macros *in the
- same file* the macros are defined, if one wants to. One way to implement this
- feature is to standardize the ability of writing multiple libraries in
- the same file, which is already provided by some implementations.
- Another way is to introduce at ``define-at-all-phases`` form.
+ I think the standard lacks many important features. I have already
+ noticed that I would welcome the ability to export all names from a
+ library ``(export *)`` and the ability to introspect the names
+ exported by library. In addition, I would welcome a mechanism to
+ write helper functions for macros *in the same file* the macros are
+ defined, if one wants to. One way to implement this feature is to
+ standardize the ability of writing multiple libraries in the same
+ file, which is already provided by some implementations. Another
+ way is to introduce at ``define-at-all-phases`` form.
I have not even explained why one would like to
diff --git a/artima/scheme/scheme11.ss b/artima/scheme/scheme11.ss
index 4063ed7..96214eb 100644
--- a/artima/scheme/scheme11.ss
+++ b/artima/scheme/scheme11.ss
@@ -15,8 +15,8 @@ a C-like ``for`` loop and I said that it was suffering from the
problem of multiple evaluation. Here I explain what the
problem is and how to cure it. In order to understand the issue,
you must always remember that macros *expand* code at compile time,
-but they not *evaluate* it: that means that pattern variables do *not*
-correspond to evalued expression, as ordinary variables, but they
+but they not *evaluate* it: this means that pattern variables do *not*
+correspond to evaluated expression, as ordinary variables, but they
correspond to expressions to be evaluated later, at runtime.
As a consequence, it is easy to write macros
@@ -37,7 +37,7 @@ with a computation::
(printf "computing the value of end\n")
3)
-Then our naive macro suffers for the multiple evaluation problem::
+Then our naive macro suffers from the multiple evaluation problem::
> (for i 0 (get-end) 'do-nothing)
computing the value of end
@@ -103,7 +103,7 @@ $$repeat-benchmark:
I took the number ``n`` from the command line arguments
in order to fool the compiler: if I hard coded ``(+ 1 1)``, the compiler
would replace it with 2 at compilation time, therefore not performing
-the computation! (in the original version of this episode I made that
+the computation! (In the original version of this episode I made that
mistake, thanks to Aziz Ghuloum for pointing it out).
The output of the script is the following::
@@ -152,7 +152,7 @@ from the macros we have defined until now. The reason why the ``test``
macro is different is that it expands into a ``lambda``-expression
and therefore the arguments
of the macro are evaluated only when the ``lambda`` function is called
-and not a definition time. In other words, we are using a pattern of
+and not at definition time. In other words, we are using a pattern of
*delayed evaluation* here. This is important, since we want to distinguish
the definition of a test from its execution. For instance, let me
define a trivial test::
diff --git a/artima/scheme/scheme12.ss b/artima/scheme/scheme12.ss
index c16aa1e..3c54ab6 100644
--- a/artima/scheme/scheme12.ss
+++ b/artima/scheme/scheme12.ss
@@ -44,7 +44,7 @@ The problem is that it is very easy to abuse the mechanism. Generally
speaking, the adaptibility of the Scheme language is a double-edged
sword. There is no doubts that it increases the programmer
expressivity, but it can also make programs more difficult to
-read. The language allows you to invent your own idioms that
+read. The language allow you to invent your own idioms that
nobody else uses, but perhaps this is not such a good idea if you care
about other people reading your code. For this reason macros in the
Python community have always been viewed with suspicion: I am also
@@ -74,7 +74,7 @@ to that episode, whereas it is argued that it is impossible to
implement an equivalent functionality in Python.
So, you should not underestimate the power of macros; on the other
-hand, you should also not underestimate the complication of macros.
+hand, you should also not underestimate the complexity of macros.
Recently I have started a `thread on comp.lang.scheme`_ with 180+ messages
about the issues I have encountered when porting my ``sweet-macros``
library between different Scheme implementations, and the thread ended
@@ -97,11 +97,11 @@ each collection a little bit different from the other.
For instance,
when I started learning Scheme I wrote a lot of utilities;
-later one, I find out that I could find the same utilites, under
+later on, I discovered that I could find the same utilites, under
different names and slightly different signatures, in various Scheme
-frameworks. This never happened to me in Python to the same extend,
+frameworks. This never happened to me in Python to the same extent,
since the standard library is already coding in an uniform way most of
-the useful idioms, so that everybody use the library and there is
+the useful idioms, so that everybody uses the library and there is
less need to reinvent the wheel.
On the other hand, I am not a macro aficionado like Paul Graham, who says:
@@ -169,13 +169,13 @@ There are good use cases for macros, but there also plenty of workarounds
for the average application programmer.
For instance, a case where one could argue for macros, is when there
-are performance issue, since macros are able to lift computations from
-the runtime to the compile time, and they can be used for code
+are performance issues, since macros are able to lift computations from
+runtime to compile time, and they can be used for code
optimization. However, even without macros, there is plenty of room
for optimization in the scripting language world, which typically
involve interfacing with C/C++.
-There also various standard techniques for *code generation* in C,
+There are also various standard techniques for *code generation* in C,
whereas C++ has the infamous *templates*: while those are solutions
very much inferior to Scheme macros, they also have the enormous
advantage of working with an enterprise-familiar technology, and you
@@ -183,7 +183,7 @@ certainly cannot say that for Scheme.
The other good use for macros is to implement compile time checks:
compile time checks are a good thing, but in practice people have
-learned to live without by relying on a good unit test coverage, which
+learned to live without them by relying on a good unit test coverage, which
is needed anyway.
On the other hand, one should not underestimate the downsides of
diff --git a/artima/scheme/scheme13.ss b/artima/scheme/scheme13.ss
index 9175852..c1a03d2 100644
--- a/artima/scheme/scheme13.ss
+++ b/artima/scheme/scheme13.ss
@@ -29,7 +29,7 @@ I would not consider Python and Common Lisp as
truly functional languages: they are just imperative languages with
some support for functional programming (I mean constructs such as ``map``,
``filter``, ``reduce``, list comprehension, generators, et cetera).
-However, there is large gap between an
+However, there is a large gap between an
imperative language with some support for functional programming and a
true functional language.
@@ -46,7 +46,7 @@ you avoid rebinding and you restrict yourself to functional data
structures, and it allows many typical idioms of functional programming
which have no counterpart in imperative programming. We already saw a common
trick in `episode #5`_, i.e. the accumulator trick, which is a
-way to avoid mutation in loops by using recursion. In this episodes and
+way to avoid mutation in loops by using recursion. In this episode and
the next ones we will show many others.
.. figure:: diamond.jpg
@@ -71,7 +71,7 @@ Actually, it is still possible to mutate pairs, but only by
extending the rnrs environment, i.e. by importing the
``(rnrs mutable-pairs)`` extension, which is part of the standard library
but not of the core language. This is a clear indication of the fact
-that the functional paradigm is somewhat recommended paradigm in Scheme,
+that the functional paradigm is somewhat a recommended paradigm in Scheme,
even if it is not enforced (in strongly functional languages, such as SML
and Haskell, lists are immutable and there is no other option).
@@ -79,7 +79,7 @@ I should notice that the requirement of importing ``(rnrs
mutable-pairs)`` is only valid for scripts and libraries: the behavior
of the REPL is unspecified in the R6RS document (actually the R6RS
forbids a REPL but every R6RS implementation provides a REPL with some
-different semantics) and implementations are free to import or to not
+different semantics) and implementations are free to import or not to
import ``mutable-pairs`` in the REPL. The REPL of Ikarus imports
``mutable-pairs`` by default, so you have at your disposal
``set-car!`` and ``set-cdr!``, but this is an implementation specific
@@ -114,7 +114,7 @@ immutable data structures becomes easier
to understand and to debug. For instance, consider a routine taking
a list in input and suppose that the content of the list is not
what you would expect. If the list is mutable you potentially have to
-wonder about you whole code base, since everything could have mutated
+wonder about your whole code base, since everything could have mutated
the list before reaching the routine you are interested in.
If the list if immutable, you are sure that the bug must be
in the procedure which created the list, and in no
@@ -164,7 +164,7 @@ The trick is to use recursion::
> (list-set 2 '(a b c d) 'X)
(a b X d)
-Notice that ``list-set`` is nicer that its imperative counterpart::
+Notice that ``list-set`` is nicer than its imperative counterpart::
(define (list-set! n lst value)
(set-car! (list-tail lst n) value) lst)
@@ -188,7 +188,7 @@ out of the box (
``list-set!`` is not in the standard since it is of very little
utility: if you want to be able to modify the n-th element of a
sequence, you are much better off by using a vector and not a list).
-However, it does provide primitive to remove elements for a list
+However, it does provide primitives to remove elements for a list
functionally::
> (remp even? '(3 1 4 1 5 9 2 6 5)); complementary of filter
@@ -205,7 +205,7 @@ functionally::
(you can find other `list utilities`_ in R6RS standard library).
-One would expect functional update to be much slower that imperative
+One would expect functional update to be much slower than imperative
update, because of the need of creating potentially long lists to
modify just a single element. However, this is not necessarily
true. It all depends on how smart your compiler is. A smart compiler
@@ -215,7 +215,7 @@ imperative code. The advantage is that mutation is managed by the
compiler, not by the programmer, who can think purely in terms of
immutable data structures.
-It is true that compiler for functional languages are still slower
+It is true that compilers for functional languages are still slower
than C compilers in average, but they are not so bad. In most
situations the slowdown is acceptable and in particular situations
compilers for functional languages can be faster than a C compiler.
diff --git a/artima/scheme/scheme19.ss b/artima/scheme/scheme19.ss
index 32172be..a6bf86e 100644
--- a/artima/scheme/scheme19.ss
+++ b/artima/scheme/scheme19.ss
@@ -287,7 +287,8 @@ for the six episodes about the module system you are about to read.
I was very ignorant about the module system when I started this
project, and this work would not have been possible without the
help of Abdulaziz Ghuloum, Derick Eddington, Will Clinger, Eli
-Barzilay, Matthew Flatt and many others. Thank you guys, you rock!
+Barzilay, Matthew Flatt, André van Tolder and many others.
+Thank you guys, you rock!
|#
diff --git a/artima/scheme/scheme2.ss b/artima/scheme/scheme2.ss
index 1535e59..a1e617e 100644
--- a/artima/scheme/scheme2.ss
+++ b/artima/scheme/scheme2.ss
@@ -31,13 +31,13 @@ very good interoperability with C, others well integrated in Java or
in .NET, others with a particularly good documentation, others with especially
useful libraries, but there is no single implementation with all the
features which is definitively superior to the others. You may use
-more than an implementation at the time, but you need to careful in
+more than an implementation at the time, but you need to be careful in
the choice of the libraries you are going to use, if you are
interested in portability.
I cannot say I have tried all Scheme implementations (there are dozens
and dozens of them) so take my obvervations *cum grano salis*. I did
-try `PLT Scheme`_, Bigloo_, Chicken_, Guile_, Ikarus_ e Larceny_ which
+try `PLT Scheme`_, Bigloo_, Chicken_, Guile_, Ikarus_ and Larceny_ which
are Open Source, multiplatform and free. Other major implementations
are `Chez Scheme`_ (the interpreter, called Petit Scheme is free, the
compiler is not) e and `MIT Scheme`_ (available with GPL licence)
@@ -59,7 +59,7 @@ is the R5RS-compatible implementation
I like most since it has a very practical attitude: it
is written by people working in the industry and not in the academy.
Chicken is a compiler from Scheme to C and it is extremely easy to
-write *wrappers* for C/C++ libraries. Moreover, there already hundreds
+write *wrappers* for C/C++ libraries. Moreover, there are already hundreds
of interesting libraries available. They are called *eggs*, just as
in Python, and they work more a less in the same way. However, it
must be noticed that Chicken had eggs years before Python and more
@@ -187,7 +187,7 @@ which is invaluable in Scheme programming. I make heavy usage of all
these features.
By the way, I see now that I have used the term REPL
-(Read-Eval-Print-Loop) which may be is unknown to a few readers; REPL
+(Read-Eval-Print-Loop) which may be unknown to a few readers; REPL
is just the Lisp name for what is called the interactive interpreter
or console in the Python world, the one with the ``>>>`` prompt.
In Scheme the REPL is very
@@ -228,7 +228,7 @@ concerns introspection and debugging capabilities. Tracebacks and
error messages are not very informative. Sometimes, you cannot even
get the number of the line where the error occurred; the reason is
that Scheme code can be macro-generated and the notion of line number
-may become foggy. On the other hand, I must say than in the five
+may become foggy. On the other hand, I must say that in the five
years I have being using Scheme (admittedly for toying and not for
large projects) I have seen steady improvement in this area.
@@ -248,7 +248,7 @@ management::
.. image:: plt-green.jpg
-As you see, there is no much information: in particular the
+As you see, there is not much information: in particular the
information about the name of the function where the error
occurred (``inv``) is lost and the line number/char number
refers to the ``read-eval-print-loop`` code. You may contrast
@@ -268,7 +268,7 @@ that with the Python traceback::
I should mention however that PLT is meant to be run inside its own
IDE, DrScheme_. DrScheme highlights the line with the error and
-include a debugger. However such functionalities are not that common
+includes a debugger. However such functionalities are not that common
in the Scheme world and in my own experience it is much more difficult
to debug a Scheme program than a Python program.
diff --git a/artima/scheme/scheme20.ss b/artima/scheme/scheme20.ss
index fc544e7..d3b6e7c 100644
--- a/artima/scheme/scheme20.ss
+++ b/artima/scheme/scheme20.ss
@@ -287,29 +287,6 @@ and thus offer full autocompletion.
;;END
;(assert-distinct a b a)
-
-;;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 ...)
diff --git a/artima/scheme/scheme25.ss b/artima/scheme/scheme25.ss
index e5465b1..f2cb01a 100644
--- a/artima/scheme/scheme25.ss
+++ b/artima/scheme/scheme25.ss
@@ -1,38 +1,103 @@
-#|Recursive macros
+#|Macros, again
=====================================================================
-After a short introduction about the relevance of macros for
-programming language design, I show a few common patterns of Scheme
-macrology: recursive macros, accumulators, and the usage of literals
-to incorporate helpers in macros.
-
-Should everybody be designing her own programming language?
+This is episode #25, the beginning of another block
+of six posts constituting part V of my Adventures.
+Part I was an introduction to the language; part II was
+an introduction to macros; part III was an introduction to functional
+programming and part IV an introduction to the module system.
+We are done with the introductions now, and we are ready to face
+more beefy matters. In particular part V main is concerned with
+expert-level macrology.
+
+In the following six episodes I will
+discuss various techniques and patterns which are commonly
+used by Scheme macro writers, such as recursive macros, higher order
+macros, and more. I will go more in detail
+about how macros work, explaining what a syntax object is, what
+a (non-)hygienic macro is and how you can to compare lexical
+identifiers in macros.
+
+I will also discuss the relation
+between my own ``sweet-macros`` and traditional macros (``syntax-rules``,
+``syntax-case`` and ``define-macro``) so that you should be able to
+program with any Scheme macrology out there.
+
+Finally I will give some nontrivial example of macro, such as a
+macro implementing runtime pattern matching for lists, by filling the
+gap left in episode 15_ .
+
+.. _homoiconicity: http://en.wikipedia.org/wiki/Homoiconicity
+.. _15: http://www.artima.com/weblogs/viewpost.jsp?thread=249681
+.. _MetaPython: http://metapython.org/
+.. _Logix: http://www.livelogix.net
+.. _Dylan: http://en.wikipedia.org/wiki/Dylan_programming_language
+
+Macros pros and contras
------------------------------------------------------------------
Macros are the reason why I first became interested in Scheme, five or
six years ago. At the time - as at any time - there was a bunch of
-trolling in comp.lang.python, and arguing for the addition of macros
-to Python. Of course most Pythonistas opposed the idea, but
-I had no idea of the advantages/disadvantages off the proposal; I felt
-quite ignorant and powerless to argue. Since I never liked to feel ignorant,
-I decided to learn macros, especially Scheme macros, because they are
-the state of the art for the topics.
+people trolling in comp.lang.python, arguing for the addition of macros
+to the language. Of course most Pythonistas opposed the proposal.
+
+However, at the time I had no idea of the advantages/disadvantages of
+the proposal and I felt quite ignorant and powerless to argue. Since I
+never liked to feel ignorant, I decided to learn macros, especially
+Scheme macros, because they are the state of the art for what concerns
+the subject.
Nowadays I can say that the addition of macros to Python would be
-a bad idea knowing what I am talking about. Actually, I have already
-stated in episode 12_ an even stronger opinion, i.e. that macros
-are not a good fit for enterprise-oriented language. Of course,
-I am *not* implying that every enterprise should adopt
-only enterprise-oriented languages; it is a matter of fact
-that various cutting edge enterprises are taking advantage of
-non-conventional and/or research-oriented languages, but
-I see them as exceptions to the general rule.
-
-My opinion against macros in (most) enterprise programming
-does not mean that macros are worthless, and indeed
+a bad idea knowing what I am talking about. I have two main objections,
+one technical (and less important) and one political (the more important).
+
+The technical reason is that I do not believe in macros in languages
+without S-expressions. There are plenty of examples of macros for
+languages without S-expression - for instance Dylan_ in the Lisp
+world, or Logix_ and MetaPython_ in the Python world, but none of
+them ever convinced me. Scheme macros are much better because of
+the homoiconicity_ of the language ("homoiconicity" is just a big word for
+the *code is data* concept).
+
+I have already stated in episode 12_ my political objection, i.e. my
+belief that macros are not a good fit for enterprise-oriented
+languages. Of course, I am *not* implying that every enterprise should
+adopt only enterprise-oriented languages; it is a matter of fact that
+various cutting edge enterprises are taking advantage of
+non-conventional and/or research-oriented languages, but I see them as
+exceptions to the general rule.
+
+In my day to day work (I use Python exclusively there)
+I had occasion to write both small declarative
+languages and small command-oriented languages, but they were so simple
+that I did not feel the need for Scheme macros. Actually, judging
+from my past experience, I think extremely unlikely that I will
+ever need something as sophisticated as Scheme macros in my
+daily work. This is why my opinion is against macros in (most)
+enterprise programming.
+
+Having said that, I do not think that macros are worthless, and actually
I think they are extremely useful and important in another domain,
i.e. in the domain of design and research about programming
-languages. The major interest of Scheme macros for me is that they
+languages.
+
+Of course Scheme is not the only language where you can experiment
+with language design, it is just the best language for this kind of
+tasks.
+
+A few months ago I have described
+some experiment I did with the Python meta object protocol, in
+order to change how the object system work, and replace
+multiple inheritance with traits_. I am interested with this
+kind of experiments, even if I will never use them in
+production code, and I use Scheme in preference for such purposes.
+
+.. _traits:
+
+Writing your own programming language
+----------------------------------------
+
+The major interest of Scheme macros for me lies in the fact that they
*enable every programmer to write her own programming language*.
I think this is a valuable thing. Everybody who has got opinions
about language design, or about how an object system should should work, or
@@ -44,7 +109,7 @@ own programming language, and certainly not everybody should
*distribute* its own personal language. Nevertheless, I think
everybody can have opinions about language design and some experiment
with macrology can help to put to test such opinions and to learn
-many things.
+something.
The easiest approach is to start from a Domain Specific
Language (DSL), which does not need to be a fully grown programming
diff --git a/artima/scheme/scheme26.ss b/artima/scheme/scheme26.ss
index b1090c1..14415eb 100644
--- a/artima/scheme/scheme26.ss
+++ b/artima/scheme/scheme26.ss
@@ -6,16 +6,129 @@ In this episode I will give a couple of examples of second order
macros taking other macros as argument. Moreover I will bring
an argument in favor of good old parenthesis.
+Scheme as an unfinished language
+-----------------------------------------------------------------------
+
+Python is an example of a *finished* language. With *finished*, I mean
+that the language has not only a well defined and complete core, but also
+a full toolbox of shortcuts and niceties to take care of common
+cases and to make easier the life of the programmer. In other words
+Python (but I could name Ruby or a few other languages here) is an
+example of a language which is both easy to use and powerful. It
+spoils the programmer, and this is the reason it is so much popular
+nowadays. Of course, others will prefer Ruby, or Scala, or
+something else, but the concept of finished language should be
+clear.
+
+On the other hand, Scheme feels very much like an unfinished language
+from somebody coming from the Python world. While there are many
+practical reasons why Scheme it is the way it is and could not be
+different, I am convinced that Scheme has been left unfinished also
+*on purpose*: the language is incomplete, but it provides a built-in
+mechanism to give the user the ability to finish the language
+according to its preferences. Such a mechanism of course is the
+mechanism of macros and one of the main use of macros is to fill
+the deficiencies left by the standard.
+
+.. image:: bikeshed.jpg
+ :class: right
+ :width: 400
+
+I think the explanation for the current situation in Scheme is more
+historical and social than technical. On one side, a lot of people in
+the Scheme world want Scheme to stay the way it is, i.e. a language
+for language experimentations and research more than a language for
+enterprise work. On the other side, the fact that there are so many
+implementations of Scheme makes difficult/impossible to specify too
+much: this the reason why there are no standard debugging tools for
+Scheme, but only implementation-specific ones.
+
+Finally, there is the infamous `bikeshed effect`_ to take in account.
+The bikeshed effect is typical of any project designed by a committee:
+when it comes to proposing advanced functionalities that very few
+can understand, it is easy to get approval from the larger community.
+However, when it comes to simple functionality of common usage, everybody
+has got a different opinion and it is practically impossible to get
+anything approved at all.
+
+To avoid that, the standard does not provide
+directly usable instruments: instead, it provides general instruments
+which are intended as building blocks on that of which everybody can
+write the usable abstractions he/she prefers. Most people nowadays
+prefer to have ready-made solutions, because they have deadlines,
+projects to complete and no time nor interest in writing things
+that should be made by language designers, so that Scheme is little
+used in the enterprise world.
+
+The difference between Scheme and its concrete implementations
+--------------------------------------------------------------------
+
+In the previous paragraph I have been a little unfair: while it is
+true that Scheme - in the sense of the language specified by the
+R6RS standard - is unfinished, concrete implementations of Scheme
+tends to be much more complete. Consider for instance PLT Scheme,
+or Chicken Scheme, which are two big Scheme implementations: they
+have both decent size libraries and they are perfectly usable
+(and used) for practical tasks you could do with Python or another
+more mainstream language. Another option is to use a Scheme
+implementation running on the Java virtual machine or on the .NET
+platform. Alternatively, you could use a Scheme-like
+language such as Clojure_. Clojure runs on the Java Virtual Machine,
+it is half lisp and half Scheme, it has a strong functional flavour in
+it, it has interesting things to say about concurrency_. It also
+shares the following caracteristics with Python:
+
+1. it is a one-man language (i.e. it is not a comprimise language made
+ by a committee) with a clear philosophy and internal consistency;
+
+2. it is language made from scratch, with no preoccupations of backward
+ compatibility;
+
+3. it provides special syntax/libraries for common operations (
+ `syntax conveniences`_) that would never enter in the Scheme standard.
+
+Such caracteristics make Clojure very appealing to me. The only
+problems is that professionally I have no need to
+interact with the Java platform (and even there I would probably
+choose Jython over Clojure for reason of familiarity) so I have not
+checked out Clojure and I have no idea about it except what you can
+infer after reading its web site. If amongst my readers
+there is somebody with experience in Clojure, please feel free to add
+a comment to this post.
+
+My point here is that users that do not care at all for the freedom
+to "finish" their language and prefer a BDFL language where
+everything has been already finished and polished for them have
+that option, by choosing Clojure.
+
+I personally am using Scheme since I am interested in macrology and no
+language in existence can beat Scheme in this respect. Also, I am
+using for Scheme for idle speculation and not to get anything done ;-)
+A typical example of idle speculation is
+the following question: would Scheme be a better language if it has
+fewer parenthesis?
+
+.. _Clojure: http://clojure.org/
+.. _syntax conveniences: http://clojure.org/special_forms
+.. _concurrency: http://clojure.org/concurrent_programming
+.. _bikeshed effect: http://en.wikipedia.org/wiki/Bikeshed
+
+
Two second order macros to reduce parenthesis
-------------------------------------------------------------
-In the last episode I have defined a recursive ``cond-`` macro taking
+While finding tricks for reducing parenthesis is pointless,
+it gives me a reason to teach a few other macro programming
+techniques: in particular, here I will discuss second order
+macros taking macros as arguments.
+
+In the last episode I defined a recursive ``cond-`` macro taking
less parenthesis than a regular ``cond``, using an accumulator. Here
I will generalize that approach, by abstracting the accumulator
functionality into a second order macro, called ``collecting-pairs``,
which takes as input another macro and a sequence of arguments, and
calls the input macro with the arguments grouped in pairs.
-That make possible to call with less parenthesis any macro of
+That makes it possible to call with less parenthesis any macro of
the form ``(macro expr ... (a b) ...)``, by calling it as
``(collecting-pairs (macro expr ...) a b ...)``.
@@ -71,7 +184,9 @@ for instance Paul Graham's Arc_ uses less parenthesis. This is
possible since it does not provide a macro system based on
pattern matching (which is a big *minus* in my opinion). Is it possible
to have both? A syntax with few parenthesis for writing code manually
-and a syntax with many parenthesis for writing macros. The answer is yes:
+and a syntax with many parenthesis for writing macros?
+
+The answer is yes:
the price to pay is to double the constructs of the language and to
use a Python-like approach.
@@ -102,9 +217,6 @@ in practice Scheme only provides the low level syntax, leaving to
the final user the freedom (and the burden) of implementing his
own preferred high level syntax.
-Can a language be both easy and powerful?
------------------------------------------------------------------------
-
When it comes to designing programming languages, easy of use and
power seems to go in opposite directions. There are plenty of examples
where something went wrong, i.e. simple languages which are
@@ -113,90 +225,6 @@ professional languages which are too tricky to use
for the casual programmer. We have also examples of languages which
are both weak in power *and* difficult to use (insert your chosen
language here).
-
-Nevertheless, I think it is perfectly possible to design a language
-which is both easy to use and powerful. For instance, Python is a good
-example of such language (others will prefer Ruby, or Scala, or
-anything else they like).
-
-There are various reasons why Python can be both easy to use and powerful,
-the most important ones being the following, in my opinion:
-
-1. it is a one-man language (i.e. it is not a comprimise language made by a
- committee);
-
-2. it is language made from scratch, with no preoccupations of backward
- compatibility;
-
-3. between (premature) optimization and easy of use Python always chooses
- the latter;
-
-4. it provides special syntax/libraries for common operations.
-
-Scheme does not share any of these characters, and as a consequence it
-is definitively not an easy language. It is just a powerful language.
-
-However, it is powerful enough that you can make it easy to use, but
-that requires (a lot of work) on the part of the programmer, which must
-implement point 4 by himself, whereas
-nowadays we are all spoiled and we expect the language implementors to
-do this kind of work for us.
-
-.. image:: bikeshed.jpg
- :class: right
- :width: 400
-
-I think the explanation for the current situation in Scheme is more historical
-and social than technical. On one side, a lot of people in the Scheme
-world want Scheme to stay the way it is, i.e. a language for language
-experimentations and research more than a language for enterprise
-work. On the other side, the fact that there are so many
-implementations of Scheme makes difficult/impossible to specify too
-much: this the reason why there are no standard debugging tools for
-Scheme, but only implementation-specific ones.
-
-Finally, there is the infamous `bikeshed effect`_ to take in account.
-The bikeshed effect is typical of any project designed by a committee:
-when it comes to proposing advanced functionalities that very few
-can understand, it is easy to get approval from the larger community.
-However, when it comes to simple functionality of common usage, everybody
-has got a different opinion and it is practically impossible to get
-anything approved at all.
-
-To avoid that, the standard does not provide
-directly usable instruments: instead, it provides general instruments
-which are intended as building blocks on that of which everybody can
-write the usable abstractions he/she prefers. Most people nowadays
-prefer to have ready-made solutions, because they have deadlines,
-projects to complete and no time nor interest in writing things
-that should be made by language designers, so that Scheme is little
-used in the enterprise world.
-
-There are other options, however, if you are interested in a Scheme
-for usage in the enterprise world. You can just use a Scheme
-implementation running on the .NET or the Java platform, or a Scheme-like
-language such as Clojure_. Clojure runs on the Java Virtual Machine,
-it is half lisp and half Scheme, it has a strong functional flavour in
-it, it has interesting things to say about concurrency_,
-it is a one-man language (Rich Hickey is the BDFL) and provides
-access to all the Java libraries. Moreover it provides a whole set
-of `syntax conveniences`_ that would never enter in the Scheme standard.
-
-Professionally I have never
-interacted with the Java platform (and even there I would probably
-choose Jython over Clojure for reason of familiarity) so I have not
-checked out Clojure and I have no idea about it except what you can
-infer after reading its web site. If amongst my readers
-there is somebody with experience in Clojure, please feel free to add
-a comment to this post.
-
-I personally am using Scheme since I am interested in macrology and no
-language in existence can beat Scheme in this respect.
-
-.. _Clojure: http://clojure.org/
-.. _syntax conveniences: http://clojure.org/special_forms
-.. _concurrency: http://clojure.org/concurrent_programming
-.. _bikeshed effect: http://en.wikipedia.org/wiki/Bikeshed
|#
(import (rnrs) (sweet-macros) (for (aps lang) run expand)
diff --git a/artima/scheme/scheme27.ss b/artima/scheme/scheme27.ss
index 2f1828e..edc0b6a 100644
--- a/artima/scheme/scheme27.ss
+++ b/artima/scheme/scheme27.ss
@@ -54,10 +54,10 @@ syntax object with the ``syntax->datum`` primitive::
> (equal? (syntax->datum syntax-expr) '(display "hello"))
#t
-Different syntax-objects can be equivalent: for instance
-the improper list of syntax objects ``(cons #'display (cons #'"hello" #'()))``
-is equivalent to the syntax object ``#'(display "hello")`` in
-the sense that both corresponds to the same datum::
+Different syntax-objects can be equivalent: for instance the improper
+list of syntax objects ``(cons #'display (cons #'"hello" #'()))`` is
+equivalent to the syntax object ``#'(display "hello")`` in the sense
+that both corresponds to the same datum::
> (equal? (syntax->datum (cons #'display (cons #'"hello" #'())))
(syntax->datum #'(display "hello")))
@@ -109,7 +109,7 @@ by using an identifier::
#<syntax (display "hello")
(the meaning of the lexical context in ``datum->syntax`` is tricky and
-I will go back to that in future episodes).
+I will go back to that in the next episode).
What ``syntax-match`` really is
--------------------------------------------------------------
diff --git a/artima/scheme/macro4.ss b/artima/scheme/scheme28.ss
index 66114a2..0ef3bb7 100644
--- a/artima/scheme/macro4.ss
+++ b/artima/scheme/scheme28.ss
@@ -1,4 +1,4 @@
-#|Relation with standard macros
+#|Hygienic macros
==============================================================
In episode 9_ I akwnoledged the fact that Scheme provides at least three
@@ -28,15 +28,15 @@ for missing the ``sub`` literal)
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``: in particular you cannot
-break hygiene with it. Moreover, ``syntax-rules`` macros
+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.
``syntax-match`` vs ``syntax-case``
-----------------------------------------------------------------
-``syntax-case`` can also be defined in terms of ``syntax-match`` as follows::
+``syntax-case`` can also be defined in terms of ``syntax-match`` as
+follows::
(def-syntax syntax-case
(syntax-match ()
@@ -63,28 +63,22 @@ Why do I say so?
``syntax-match`` versus ``define-macro``
---------------------------------------------------------------
-Nowadays macros based on ``define-macro`` are much less used than in past,
-in part because of the hygiene issue, and in part because macro systems based
-on pattern matching are much more powerful and easy to use. The R6RS
-specification made ``syntax-case`` enters in the standard and this is
-the preferred macro system for most implementation. For instance
-Chicken Scheme, which traditionally used ``define-macro`` a lot, is
-going to remove it from the core, using ``syntax-case`` instead: this
-is the reason why the next major Chicken version, Chicken 4.0, will be
-called "hygienic Chicken".
+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
+system for most implementation.
Nowadays, there is a good chance that your Scheme implementation does
-not provide ``define-macro`` out of the box, therefore you need to implement
-it in term of ``syntax-case`` (or ``syntax-match``). Here is an example
-of such an implementation:
+not provide ``define-macro`` out of the box, therefore you need to
+implement it in term of ``syntax-case`` (or ``syntax-match``). Here is
+an example of such an implementation:
$$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. The problem with
-``define-macro`` (and the reason it is becoming less and less used in
-the Scheme world) is the hygiene problem.
+back into a syntax object in the context of the macro.
.. _9: http://www.artima.com/weblogs/viewpost.jsp?thread=240804
.. _On Lisp: http://www.paulgraham.com/onlisp.html
@@ -102,10 +96,22 @@ effects. 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 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 core, by using hygienic macros instead: this is the reason why the
+current Chicken (Chicken 4.0) 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.
+reference I know. Another extremely 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, with
+the readers unfamiliar with it.
.. image:: hygienic-paper-small.jpg
@@ -130,20 +136,32 @@ the macro is not safe under variable capture and indeed code such as
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.
+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:
-Hygiene is regarded as a great virtue in the Scheme
-community, less so in the Common Lisp community.
+> (let ((unless 'unless))
+ (dirty-for i 1 3 (print i)))
+
+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.
Breaking hygiene
-------------------------------------------------
If you only use hygienic macros, the hygiene probleme does not exist.
-However, there is the opposite problem: you neede a way of breaking hygiene on
-purpose. Consider for instance the following apparently trivial
-macro:
+However, there is the opposite problem: you need a way of breaking
+hygiene on purpose. Consider for instance the following apparently
+trivial macro:
.. code-block:: scheme
@@ -179,7 +197,7 @@ 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 names internally defined by a macro
+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.
@@ -187,8 +205,7 @@ 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. Therefore hygiene is a very good think, since it
-frees your from wondering about name clashes.
+your library works.
To be fair, I should
remark that in Common Lisp there
@@ -201,12 +218,13 @@ 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 first cited ``datum->syntax`` in episode 21 and you have seen
-it at work in the definition of ``define-macro`` from ``syntax-match``:
-here it is used to convert a list describing source code into a syntax
-object which is then expanded.
-The typical use case for ``datum->syntax`` is to turn symbols
+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``:
@@ -223,6 +241,20 @@ the identifier ``a`` is really introduced as follows:
> (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
|#
(import (rnrs) (sweet-macros) (aps lang) (aps list-utils) (aps compat)
@@ -232,10 +264,10 @@ the identifier ``a`` is really introduced as follows:
(def-syntax define-macro
(syntax-match ()
(sub (define-macro (name . params) body1 body2 ...)
- #'(define-macro name (lambda params body1 body2 ...)))
+ #'(define-macro name (lambda params body1 body2 ...)))
(sub (define-macro name expander)
- #'(def-syntax (name . args)
- (datum->syntax #'name (apply expander (syntax->datum #'args)))))
+ #'(def-syntax (name . args)
+ (datum->syntax #'name (apply expander (syntax->datum #'args)))))
))
;END
@@ -249,6 +281,28 @@ the identifier ``a`` is really introduced as follows:
`(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)
diff --git a/artima/scheme/macro5.ss b/artima/scheme/scheme29.ss
index 18ca87c..82872b0 100644
--- a/artima/scheme/macro5.ss
+++ b/artima/scheme/scheme29.ss
@@ -1,5 +1,5 @@
#|
-Generating temporary identifiers
+Records from macros
=========================================================================
In this episode I show how to introduce auxiliary identifiers in a
@@ -13,10 +13,10 @@ record types and I discuss the hygienic feature of Scheme macros.
The R6RS standard provides a few convenient utilities to work with
macros. One of such utilities is the ``with-syntax`` form, which
allows to introduce auxiliary pattern variables into a skeleton
-(a better name would have been ``let-pattern-vars``).
-Let me make an example.
-``with-syntax`` is often used in conjunction with the ``generate-temporaries``
-function, which returns a list of temporary identifiers.
+(a better name would have been ``let-pattern-vars``). Let me make an
+example. ``with-syntax`` is often used in conjunction with the
+``generate-temporaries`` function, which returns a list of temporary
+identifiers.
Here is an example where the temporary variable is used
as argument in the lambda function: a ``fold`` macro
@@ -137,32 +137,27 @@ methods, so that you may regard them as immutable records (actually
they are not, since you can change them by using ``vector-set!``,
but that would be a dirty trick ;)
-Notice that a record system like the one presented here features record types
-which are not first class objects - since they are macros; in this respect
-it is more similar to the type system of languages like SML, where types
-are not objects, and very different from a type system like the Python one,
-where classes are objects. Of course in Scheme you can also implement a
-Python-like object system, where it is possible to create dynamic record types
-at runtime and not only at compile time. You can implement it yourself, or
-wait for a future episode ;)
-
-Hygiene
----------------------------------------------------------------------
+Notice that a record system like the one presented here features
+record types which are not first class objects - since they are
+macros; in this respect it
+is more similar to the type system of languages like SML, where types
+are not objects, and very different from a type system like the Python
+one, where classes are objects. Of course in Scheme you can also
+implement a Python-like object system, where it is possible to create
+dynamic record types at runtime and not only at compile time. You can
+implement it yourself, or wait for a future episode ;)
.. hygiene in R6RS: http://docs.plt-scheme.org/r6rs-lib-std/r6rs-lib-Z-H-13.html#node_sec_12.1
There is a subtle point about the ``def-record-type`` macro defined in
-the previous paragraph. Such a macro introduces a lots
-of auxiliary functions, such as ``record-new``, ``record?``, and an accessor
-function with a temporary name for every record field.
-One may expect those names
-to litter the namespace: i.e., after expansion, you would expect the names
-``record-new``, ``record?`` and the temporary names to be defined in
-the namespaces.
-Actually this is not the case: Scheme macros are *hygienic*,
-and auxiliary names introduced in the macro *are not visible outside*.
-
-.. image:: hygienic-paper-small.jpg
+the previous paragraph. Such a macro introduces a lots of auxiliary
+functions, such as ``record-new``, ``record?``, and an accessor
+function with a temporary name for every record field. One may expect
+those names to litter the namespace: i.e., after expansion, you would
+expect the names ``record-new``, ``record?`` and the temporary names
+to be defined in the namespaces. Actually this is not the case:
+Scheme macros are *hygienic*, and auxiliary names introduced in the
+macro *are not visible outside*.
This is a major difference with respect to Common Lisp macros.
The only names which enter in the namespace are the ones we put in;
@@ -183,14 +178,6 @@ macro, I should notice that I have been able to use the name ``record?``
only because is an internal name: if the macroexpansion were literal,
I would have incurred in a name clash, since ``record?`` is a builtin
name.
-
-In general, 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. Therefore hygiene is a very good think, since it
-frees your from wondering about name clashes.
-
|#
(import (rnrs) (sweet-macros) (for (aps lang) run expand)
diff --git a/artima/scheme/scheme3.ss b/artima/scheme/scheme3.ss
index 5df7765..b77cc57 100644
--- a/artima/scheme/scheme3.ss
+++ b/artima/scheme/scheme3.ss
@@ -1,5 +1,5 @@
#|
-Of parenthesis and indentation
+Of parentheses and indentation
=================================================================
In the previous two episodes I have discussed a few important subjects
@@ -7,10 +7,10 @@ such as availability of libraries, reliability of implementations,
support in case of bugs, etc. However, I have not said anything about
the language *per se*. In this episode I will talk more about the
language, by starting from the syntax, with a discussion of the infamous
-parenthesis. Lisp parens have been the source of infinite debates
+parentheses. Lisp parens have been the source of infinite debates
from the very beginning and always will be. As you probably know,
-LISP_ means *Lots of Irritating Superfluous Parenthesis*, and Scheme
-has even more parenthesis than other Lisps!
+LISP_ means *Lots of Irritating Superfluous Parentheses*, and Scheme
+has even more parentheses than other Lisps!
.. _LISP: http://www.acronymfinder.com/af-query.asp?acronym=LISP
@@ -24,7 +24,7 @@ I came back to Emacs, I asked on comp.lang.scheme_ and comp.emacs_
and I was pointed out to excellent *scheme mode*, called quack.el_
and written by Neil Van Dike.
Moreover, I have discovered how to augment the contrast of the parens
-(parens-edit mode) and I feel completely confortable. But let me
+(parens-edit mode) and I feel completely comfortable. But let me
repeat that it is suicidal to try to edit Scheme/Lisp code
without a good support from the editor.
In my opinion this is the first reason why legions of beginners
@@ -48,7 +48,7 @@ programming language is a good think. In theory, everybody should
have the freedom to choose her editor, and it should be
possible to program even in Notepad (even if it is a thing
I do not wish to anybody!). This in theory: in practice, every
-professional programmer use some dedicate tool to write code,
+professional programmer use some dedicated tool to write code,
so if you don't want to use Emacs please make sure your editor/IDE has
a good Scheme mode, otherwise consider changing your developing
environment.
@@ -62,8 +62,8 @@ environment.
It is interesting to notice what
`Paul Graham`_ - a big name in the Lisp community and the main
-of Arc_, a new language of the Lisp family recently released
-and implemented in PLT Scheme - says about the parenthesis:
+author of Arc_, a new language of the Lisp family recently released
+and implemented in PLT Scheme - says about the parentheses:
*We also plan to let programmers omit parentheses where no ambiguity would
result, and show structure by indentation instead of parentheses.
@@ -72,26 +72,26 @@ and implemented in PLT Scheme - says about the parenthesis:
.. image:: paulgraham.jpg
-Arc_ for the moment seems to require the parens, but is has
-a bit less parenthesis than Scheme, as you can infer from
+Arc_ for the moment seems to require the parens, but it has
+a bit less parentheses than Scheme, as you can infer from
the tutorial_.
It is clear that the parens are NOT necessary and one could imagine
-various tricks to reduce their number (I myself I tried various
+various tricks to reduce their number (I personally tried various
approaches when I began programming in Scheme).
There is also an SRFI (SRFI-49_: *Indentation-sensitive syntax*)
-that proposes to use indentation instead of parenthesis keeping
+that proposes to use indentation instead of parentheses taking
inspiration from Python (!) The proposal should be considered as
curiosity; discussing about indentation could have had some sense
thirty years ago, at the time Scheme was designed. Nowadays, when
-100% of Scheme code is written with parenthesis, there is no point
-in not using them. The beginners would be penalized if she started
+100% of Scheme code is written with parentheses, there is no point
+in not using them. Beginners would be penalized if they started
using a style nobody uses.
In my (semi-serious) opinion, parens are a real *initiation test*:
if a programmer cannot stand them than he is not worth of using
Scheme/Lisp and he should address himself on inferior languages
-(i.e. *all* languages, according to wht the majority of
+(i.e. *all* languages, according to what the majority of
Schemers/Lispers think). In my experience the snobish attitude
is more common in the Common Lisp community whereas in the
Scheme community there is more respect for the newbie.
@@ -105,7 +105,7 @@ in other languages, since only the most persistent survive.
As a Pythonista I do not believe in those tricks: I think every
language should be made accessible to the largest possible public.
-That means that many second rate programmers will be able to learn
+That means many second rate programmers will be able to learn
it, but this is an opportunity, not an issue: the existence of poor
programmers increases the number of available positions, since you
need people to fix their mistakes! Otherwise how would you justify
@@ -114,20 +114,20 @@ the number of job offers for Java and C++?
Anyway, when after long suffering one has learned to manage with
parens, there is no way back: once you have mastered a good editor the
-parens gives you strong advantages when writing code. One of the main
-ones is automatic indentation with a single keypress. Never more
+parens give you strong advantages when writing code. One of the main
+ones is automatic indentation with a single keypress. No more
snippets of code send via e-mail and made unredable by the newlines
-added by the mail program; never more time wasted on reindenting code
+added by the mail program; no more time wasted on reindenting code
when refactoring.
-Of course, nothing is perfect, and you may forget a parens here and
+Of course, nothing is perfect, and you may forget a paren here and
there, but overall I will definitively admit that in the long run the
-parenthesis pay off. On the other hand, on the short run, they make
+parentheses pay off. On the other hand, in the short run, they make
life much harder for the newbies; I still think that an optional
-syntax with less parenthesis to be used by beginners or when using a
+syntax with less parentheses to be used by beginners or when using a
poor editor would make sense, in a new Scheme-like langauge. But it
is too late for Scheme itself: for the best or for the worst Scheme is
-a language full of parenthesis and it is best to take full advantage
+a language full of parentheses and it is better to take full advantage
of them.
*Nota Bene*: new languages based on *s*-expressions are born every day
@@ -135,7 +135,7 @@ of them.
which runs on the Java platform and is very interesting).
For those new languages it may have sense to investigate the available
options. The best reference
-discussing alternative to parenthesis that I know of is `a paper
+discussing alternative to parentheses that I know of is `a paper
by David Wheeler`_. It is an interesting reading, you may want to have
a look at it, if you are interested in the topic.
@@ -153,7 +153,7 @@ write ``(+ 1 1)``. The sum operator ``+`` is written at the
beginning,
as a prefix, and not in the middle, as an infix. I never had any
trouble with infix syntax (I had trouble with parens instead)
-since it is something perfectly consistente: in Python
+since it is something perfectly consistent: in Python
the function name is written before the arguments too.
Actually, when you write ``1+1`` in Python, you should think of
@@ -180,14 +180,14 @@ famous *batteries included* concept), whereas Scheme does not care.
Probably the real issue is that it is impossible
to get consensus in the committee about the size of the standard
library and about what to include in it, but the final result is
-that user is stack with a very small standard library. Anyway, I
+that user is stuck with a very small standard library. Anyway, I
should say that the standard library was
much smaller before the R6RS, so the situation is improving.
-Moreover, concrete implementation often have a lot of useful
+Moreover, concrete implementations often have a lot of useful
(but non-portable) libraries.
-But let me come back to syntax. It must be noticed that the prefix syntax
-has enourmous advantages when macros enter in the game. The absolute
+But let me go back to syntax. It must be noted that the prefix syntax
+has enourmous advantages when macros enter the game. The absolute
regularity of Scheme/Lisp programs, which are sequences of
*s*-espressions of the form
@@ -198,15 +198,15 @@ automatic generation of code extremely effective. I will discuss
this point in detail in future episodes; here I can anticipate
that *Scheme code is not meant to be written by humans, it is
intended to be written automatically by macros*. Only after having
-understood this point you will realize that the parenthesis are
+understood this point you will realize that the parentheses are
a Good Thing (TM). I needed a few months to understand it,
others never understand it and they quit Scheme with disgust.
If you will be able to pass the initiation test you will see
that *s*-expressions (which are usually but not *necessarily*
-associated to parenthesis) make sense. Once understood it,
+associated to parentheses) make sense. Once understood,
the traditional (infix) notation becomes an obstacle more than
-an help. Moreover the total uniformity of Scheme programs has
+a help. Moreover the total uniformity of Scheme programs has
a kind of beauty and elegance in itself. No useless syntax,
no boilerplate code, you breath an air of Zen minimalism.
|#
diff --git a/artima/scheme/scheme5.ss b/artima/scheme/scheme5.ss
index 3aef13c..9f44bce 100644
--- a/artima/scheme/scheme5.ss
+++ b/artima/scheme/scheme5.ss
@@ -11,8 +11,8 @@ times the same instructions and measuring the total time spent? We
will see in a moment that there is an easy solution to this question
(use recursion). On the other hand, there is a second more serious
question: since there is no fully portable way to write a library in
-Scheme, how can we write a benchmark library? There is no real answer
-to this question, so we will restrict ourselves to R6RS-compliant
+Scheme, how can we write a benchmark library? There is no real answer,
+so we will restrict ourselves to R6RS-compliant
Scheme implementations where there is a standard concept of library.
There are no ``for`` loops in Scheme
@@ -20,10 +20,10 @@ There are no ``for`` loops in Scheme
The ``for`` loop is missing in Scheme as a primitive construct since
it is useless in a language that guarantees tail call optimization.
-If you studied the concept of *tail call* at the college you know what
+If you studied the concept of *tail call* at college you know what
I am talking about; on the other hand, if you forgot it, or if you did
-study Physics like myself, it is worth spenting a couple of words on
-the subject. The ones who wants to know more, may consult
+study Physics like me, it is worth spending a couple of words on
+the subject. The ones who want to know more, may consult
this `Wikipedia article`_.
.. image:: Ouroboros.png
@@ -38,7 +38,7 @@ the Python loop::
for i in range(1,4):
print i,
-can be converted as a recursive function ``print_up_to_3``::
+can be converted to a recursive function ``print_up_to_3``::
def print_up_to_3(i):
if i == 4: return
@@ -50,7 +50,7 @@ can be converted as a recursive function ``print_up_to_3``::
Here the last instruction of the function (the tail) is a call to
itself, hence the name *tail call*.
-The tail call optimization is an optimization guaranteed by the
+Tail call optimization is guaranteed by the
Scheme language. Scheme compilers/interpreters are able
to recognize recursive functions in tail call form and to convert
them internally in ``for`` loops. As a consequence, the programmer
@@ -83,13 +83,13 @@ Let me point out two things before closing this paragraph:
> (let ((a 1) (b 2)) (+ a b)) ; => 3
- The scope of ``a`` and ``b`` is limited to the current block;
- if ``a`` and ``b`` are defined outside the ``let`` block, internally
+ The scope of ``a`` and ``b`` is limited to the current S-expression/form;
+ if ``a`` and ``b`` are defined outside the ``let`` form, internally
``a`` and ``b`` *shadow* the outer names.
2) there is actually a ``do`` loop in the language, but it is cumbersome
to use and redundant because the *named let* allows you to perform
- anything ``do`` does. I see it as an useless construct in a language
+ everything ``do`` does. I see it as a useless construct in a language
that would like to be minimalist but it is not.
.. _Wikipedia article: http://en.wikipedia.org/wiki/Tail_call_optimization
@@ -97,11 +97,11 @@ Let me point out two things before closing this paragraph:
There is no portable module system
---------------------------------------------------------------------
-As I have anticipated before, libraries are the weak point of Scheme.
+As I have anticipated, libraries are the weak point of Scheme.
There are few libraries available and it is also difficult to
-write portable libraries. The reason is that historically Scheme
+write portable ones. The reason is that historically Scheme
never had any standard module system until very recently, with
-the R6RS document: that means that nearly all current implementations
+the R6RS document: that means nearly all current implementations
provide different and incompatible module systems.
In order to understand the reason for this serious lack, you must
@@ -110,16 +110,16 @@ understand the philosophy behind Scheme, i.e. the so called
For thirty years the Scheme community has not been able to converge on
a well done single module system. It is only in 2007 that a standard module
system has been blessed by the Scheme committee: but even that
-was done was a lot of opposition and there are implementors who
-said they will *never* support R6R5.
+was done with a lot of opposition and there are implementors who
+said they will *never* support R6RS.
-As a consequence of history and mentality, even as pof now, if you
+As a consequence of history and mentality, if you
want to write a library for implementation X, you need to do a lot of
boring and uninspiring work to port the library to implementations Y,
Z, W, ... (there are *dozens* of different implementations).
Moreover, a few implementations do not have a module system at all, so
you may be forced to solve name clashes issue *by hand*, changing the
-names of the functions exported by our library, if they shadow names
+names of the functions exported by your library, if they shadow names
coming from third party libraries (!)
Personally, I picked up Scheme 5 years ago, but never used it
@@ -243,7 +243,7 @@ with the following R6RS-compliant script (written in Ikarus Scheme)::
I will notice two things:
-1. Python manage to compute the factorial of 995, but then it faces
+1. Python manages to compute the factorial of 995, but then it faces
the stack wall and it raises a
``RuntimeError: maximum recursion depth exceeded`` whereas Scheme
has no issues whatsoever;
@@ -260,16 +260,16 @@ long integers is more efficient than the Python one, but they
say nothing on the relative performances of the two languages.
It is more interesting to see what happens for small numbers.
For instance, in order to compute the factorial of 7 for 10 millions
-of times, Python takes 30.5 seconds, whereas Ikarus taks
+of times, Python takes 30.5 seconds, whereas Ikarus takes
3.08 seconds and thus it is nearly *ten times* faster than Python.
This is not surprising at all, since function calls in Python
are especially slow whereas they are optimized in Scheme. Moreover
Ikarus is a native code compiler.
-That means that Ikarus' REPL_ works by compiling expressions to native code,
+It means Ikarus' REPL_ works by compiling expressions to native code,
whereas Python's REPL compiles to bytecode. The technology is called
incremental compilation and it is commonly used in Lisp languages
-from decades, even it may look futuristic for C/C++ programmers.
+from decades, even if it may look futuristic for C/C++ programmers.
The factorial example is not very practical (on purpose), but it
is significant, in the sense that it is legitimate to expect
good performances from Scheme compilers. The fastest
diff --git a/artima/scheme/scheme7.ss b/artima/scheme/scheme7.ss
index dfa94b4..4eee28c 100644
--- a/artima/scheme/scheme7.ss
+++ b/artima/scheme/scheme7.ss
@@ -15,8 +15,8 @@ languages (with the exception of Ruby): the *symbol*.
.. image:: symbols.jpg
:height: 250
-From the grammar point of view, a symbols is just a quoted identifier,
-i.e. a sequences of characters corresponding to a valid identifier
+From the grammar point of view, a symbol is just a quoted identifier,
+i.e. a sequence of characters corresponding to a valid identifier
preceded by a quote. For instance, ``'a``, ``'b1`` e ``'c_`` are
symbols. On the set of symbols there is an equality operator ``eq?``
which is able to determine if two symbols are the same or not::
@@ -28,7 +28,7 @@ which is able to determine if two symbols are the same or not::
#t
``#f`` e ``#t`` are the Boolean values False and True respectively,
-how you may have imagined. The equality operator is extremely
+as you may have imagined. The equality operator is extremely
efficient on symbols, since the compiler associates to every
symbol an integer number (this operation is called *hashing*) and stores
it in an interal registry
@@ -59,7 +59,7 @@ functions hash_ e intern_, which says: *normally, the names used in
Python programs are automatically interned, and the dictionaries used
to hold module, class or instance attributes have interned keys*.
BTW, if you want to know exactly how string comparison works in Python
-I suggest you to look at `this post`:
+I suggest you to look at `this post`_:
.. _hash: http://docs.python.org/lib/built-in-funcs.html#l2h-36
.. _intern: http://docs.python.org/lib/non-essential-built-in-funcs.html#l2h-90
@@ -67,7 +67,7 @@ I suggest you to look at `this post`:
Scheme has much more valid identifiers than Python or C, where
the valid characters are restricted to ``a-zA-Z-0-9_`` (I am ignoring
the possibility of having Unicode characters in identifiers, which is
-possible both in R6RS Scheme and Python 3.0). By conventions, symbols
+possible both in R6RS Scheme and Python 3.0). By convention, symbols
ending by ``?`` are associated to boolean values or to boolean-valued
functions, whereas symbols ending by ``!`` are associated to functions
or macros with side effects.
@@ -82,7 +82,7 @@ sometimes::
#f
The reason is that ``eq?`` (corrisponding to ``is`` in Python) checks
-if two objects are the same objects at the pointer level, but it does
+if two objects are the same object at the pointer level, but it does
not check the content. Actually, Python works the same. It is only by
accident than ``"pippo" is "pippo"`` returns
True on my machine, since the CPython implementation
@@ -110,7 +110,7 @@ and for integer numbers ``=``::
#t
To be entirely accurate, in addition to ``eq`` and ``equal``, Scheme
-also provides a third equality operator ``eqv``. ``eqv`` looks to me
+also provides a third equality operator ``eqv?``. ``eqv?`` looks to me
like an useless complication of the language, so I will not discuss
it. If you are interested, you can read what the R6RS document `says
about it`_.
@@ -215,7 +215,7 @@ functions with different behavior according to the number of arguments.
``for-all`` is an R6RS higher order function: ``(for-all pred
lst)`` applies the predicate ``pred`` to the elements of list ``lst``,
until a false value is found - in that case it returns ``#f`` -
-otherwise is returns ``#t``. Here the assertion checks at
+otherwise it returns ``#t``. Here the assertion checks at
runtime that all the passed arguments ``n0``, ``n`` and ``s`` are
numbers, with ``s`` non-zero.
@@ -253,7 +253,7 @@ I suggest you try the following exercises:
I will show the solutions in the next episode. If you are lazy and you
want an already written list library, I suggest you to give a look at the
-SRFI-1_, libraries, which is very rich and available practically in all
+SRFI-1_ library, which is very rich and available practically in all
Scheme implementations. Many of the SRFI-1_ features are built-in in
the R6RS standard, but many other are still available only in the SRFI-1_ .
diff --git a/artima/scheme/scheme8.ss b/artima/scheme/scheme8.ss
index 750d7ba..028d302 100644
--- a/artima/scheme/scheme8.ss
+++ b/artima/scheme/scheme8.ss
@@ -5,7 +5,7 @@ Quoting and quasi-quoting
In this episode I will
explain the meaning of the "code is data" concept. To this aim I
discuss the quoting operation which allows to convert a code fragment
-into a list of symbols and primitive values - i.e. converts code into
+into a list of symbols and primitive values - i.e. it converts code into
data. Then, I discuss the issue of evaluating data as code.
Quoting
@@ -13,10 +13,10 @@ Quoting
A distinguishing feature of the Lisp family of languages is the existence
of a quoting operator denoted with a quote ``'`` or with ``(quote )``,
-the first form being syntacting sugar for the second.
+the first form being syntactic sugar for the second.
The quoting operator works as follows:
-1. on primitive values such as numbers, literal strings, symbols etc, it
+1. on primitive values such as numbers, literal strings, symbols, etc, it
works as an identity operator:
::
@@ -62,7 +62,7 @@ a dynamic library, then you can import it at runtime;
you also have the mechanism of pre-processor macros at your disposal
for working at compile time. The point is that there is no language where
code generation is as convenient as in Scheme/Lisp where it is buil-in,
-thanks to*s*-expressions or, you wish, thanks to parenthesis.
+thanks to *s*-expressions or, you wish, thanks to parenthesis.
Quasi-quoting
-------------------------------------------------
@@ -92,12 +92,12 @@ our case we are unquoting the user name, ``,user``. The function
containing the string ``"hello"`` together with the username.
There is another operator like ``unquote``, called
-``unquote-splice`` or comma-at and written ``,@``, which works as follows::
+``unquote-splicing`` or comma-at and written ``,@``, which works as follows::
> (let ((ls '(a b c))) `(func ,@ls))
(func a b c)
-In practice ``,@ls`` "unpack" the list ``ls`` into its components: without
+In practice ``,@ls`` "unpacks" the list ``ls`` into its components: without
the splice operator we would get::
> (let ((ls '(a b c))) `(func ,ls))
@@ -108,7 +108,7 @@ since Scheme/Lisp code is nothing else than a list, it is easy
to build code by interpolating a list template.
For instance, suppose we want to evaluate a Scheme expression
in a given context, where the contexts is given as a list of
-binding, i.e. a list names/values::
+bindings, i.e. a list names/values::
(eval-with-context '((a 1)(b 2) (c 3))
'(* a (+ b c)))
@@ -124,7 +124,7 @@ known by the interpreter; in our case we declared that ``eval`` understands
all the procedures and macros of the most recent RnRS standard (i.e.
the R6RS standard). The environment specification has the same syntax
of an ``import``, since in practice it is the same concept: it is
-possible to specify user-define modules as the ``eval`` environment.
+possible to specify user-defined modules as the ``eval`` environment.
This is especially useful if you have security concerns, since you
can run untrusted code in a stricter and safer subset of R6RS Scheme.
@@ -142,7 +142,7 @@ Programs writing programs
:class: right
Once you realize that code is nothing else than data, it becomes easy
-to write programs taking in input source code and generating in output
+to write programs taking as input source code and generating as output
source code, i.e. it is easy to write a compiler.
For instance, suppose we want to convert the following code fragment
@@ -200,7 +200,7 @@ recursion) and structures more general than ``begin``: but I leave
that as an useful exercise. In a future episode I will talk of
*code-walkers* and I will discuss how to convert generic source code.
In general, one can convert s-expression based source code by using
-an external compiler, as we did here, or by using the buil-in mechanism
+an external compiler, as we did here, or by using the built-in mechanism
provided by Scheme macros. Scheme macros are particularly powerful,
since they feature extremely advanced pattern matching capabilities:
the example I have just given, based on the primitive list operations
diff --git a/artima/scheme/table-of-contents.txt b/artima/scheme/table-of-contents.txt
index 55f2355..b3e57be 100644
--- a/artima/scheme/table-of-contents.txt
+++ b/artima/scheme/table-of-contents.txt
@@ -78,4 +78,4 @@ Part IV - The Module System
.. _Ep21: http://www.artima.com/weblogs/viewpost.jsp?thread=255612
.. _Ep22: http://www.artima.com/weblogs/viewpost.jsp?thread=256848
.. _Ep23: http://www.artima.com/weblogs/viewpost.jsp?thread=257998
-.. _Ep23: http://www.artima.com/weblogs/viewpost.jsp?thread=258103
+.. _Ep24: http://www.artima.com/weblogs/viewpost.jsp?thread=258103