summaryrefslogtreecommitdiff
path: root/artima
diff options
context:
space:
mode:
authormichele.simionato <devnull@localhost>2009-06-17 05:47:44 +0000
committermichele.simionato <devnull@localhost>2009-06-17 05:47:44 +0000
commitdb02e3d038bb6978307a96fb43fe602fc17f3612 (patch)
treed5d1dd1e481f178e65eb9ad5134e97377acc566e /artima
parent1edb00ff4c434771ce93976a2b4497f94179503a (diff)
downloadmicheles-db02e3d038bb6978307a96fb43fe602fc17f3612.tar.gz
Committed scheme27 and various improvements
Diffstat (limited to 'artima')
-rw-r--r--artima/scheme/scheme27.ss212
1 files changed, 117 insertions, 95 deletions
diff --git a/artima/scheme/scheme27.ss b/artima/scheme/scheme27.ss
index de8adbf..3e7d030 100644
--- a/artima/scheme/scheme27.ss
+++ b/artima/scheme/scheme27.ss
@@ -10,14 +10,16 @@ on it.
A *syntax-object* is a kind of enhanced *s*-espression: it contains
the source code as a list of symbols and primitive values, but also
additional informations, such as
-the name of the file containing the source code, the line numbers,
+the name of the file containing the source code, the position
+of the syntax object in the file,
a set of marks to distinguish identifiers according to their
lexical context, and more.
-The easiest way to get a syntax object is to use the syntax quoting operation,
-i.e. the funny ``#'`` symbol you have seen in all the macros I have defined
-until now. Consider for instance the following script, which displays
-on standard out the string representation of the syntax object ``#1``:
+The easiest way to get a syntax object is to use the syntax quoting
+operation, i.e. the ``syntax`` (``#'``) symbol you have seen in all the
+macros I have defined until now. Consider for instance the following
+script, which displays the string representation of
+the syntax object ``#'1``:
.. code-block:: scheme
@@ -32,20 +34,21 @@ If you run it under PLT Scheme you will get
$ plt-r6rs x.ss
#<syntax:/home/micheles/Dropbox/gcode/artima/scheme/x.ss:2:11>
-i.e. the full pathname of the script and the line number/column number
+In other words, the string representation of the syntax object ``#'1``
+contains the full pathname of the script and the line number/column number
where the syntax object appears in the source code. Clearly this
information is pretty useful for tools like IDEs and debuggers. The
-internal implementation of syntax objects is totally
-implementation-dependent, so that you will get different
+internal implementation of syntax objects is not standardized at all,
+so that you get different
informations in different implementations. For instance Ikarus
-would give
+gives
::
$ ikarus --r6rs-script x.ss
#<syntax 1 [char 28 of x.ss]>
-i.e. Ikarus syntax objects do not store line numbers, they just store
+i.e. in Ikarus syntax objects do not store line numbers, they just store
the character position from the beginning of the file. If you are using
the REPL you will have less information, of course, and even more
implementation-dependency. Here are a few example of syntax objects
@@ -66,8 +69,7 @@ Here I am running all my examples under Ikarus; your Scheme
system may have a slightly different output representation for syntax
objects.
-In general ``#'`` - also spelled ``(syntax )`` - can be "applied"
-to any expression::
+In general ``#'`` can be "applied" to any expression::
> (define syntax-expr #'(display "hello"))
> syntax-expr
@@ -92,7 +94,8 @@ The ``(syntax )`` macro is analogous to the ``(quote )`` macro.
Mreover, there is a ``quasisyntax`` macro denoted with ``#``` which
is analogous to the ``quasiquote`` macro (`````).
In analogy to
-the operations ``,`` and ``,@`` on regular lists, there are two
+the operations comma (``,``) and comma-splice
+(``,@``) on regular lists, there are two
operations ``unsyntax`` ``#,`` (*sharp comma*) e ``unsyntax-splicing``
``#,@`` (*sharp comma splice*) on lists and improper lists of
syntax objects.
@@ -102,7 +105,7 @@ Here is an example using sharp-comma::
> (let ((user "michele")) #`(display #,user))
(#<syntax display> "michele" . #<syntax ()>)
-and here is an example using sharp-comma-splice::
+Here is an example using sharp-comma-splice::
> (define users (list #'"michele" #'"mario"))
> #`(display (list #,@users))
@@ -121,7 +124,7 @@ However, the result of a quasi quote interpolation is very much
*implementation-dependent*: Ikarus returns an improper list, but other
implementations returns different results; for instance Ypsilon
returns a proper list of syntax objects whereas PLT Scheme returns
-an atomic syntax object. The lesson is that you cannot
+an atomic syntax object. The lesson here is that you cannot
rely on properties of the inner representation of syntax objects:
what matters is the code they correspond to, i.e. the result of
``syntax->datum``.
@@ -135,15 +138,15 @@ 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 a future episode, when I will talk about hygiene).
+I will go back to that in a future episode).
What ``syntax-match`` really is
--------------------------------------------------------------
-``syntax-match`` is a general utility to perform pattern matching
-on syntax objects; it takes a syntax object in output and returns
-another syntax object in output, depending on the patterns, skeletons and guards
-used. Here is an example of a simple transformer based on ``syntax-match``::
+``syntax-match`` is a general utility to perform pattern matching on
+syntax objects; it takes a syntax object in output and returns a
+syntax object in output. Here is an example of a simple transformer based on
+``syntax-match``::
> (define transformer
(syntax-match ()
@@ -170,101 +173,117 @@ Here is an example using ``quasisyntax`` and ``unsyntax-splicing``::
.. image:: hieroglyphics.jpg
-As you see, it easy to write hieroglyphs if you use ``quasisyntax``
-and ``unsyntax-splicing``. You can avoid that by means of the ``with-syntax``
-form::
+As you see, it easy to write hieroglyphs if you use ``quasisyntax``
+and ``unsyntax-splicing``. You can avoid that by means of the
+``with-syntax`` form::
> (syntax-match #'(a 1 2 3) ()
(sub (name . args) (with-syntax (((a ...) #'args)) #'(name a ...))))
(#<syntax a> #<syntax 1> #<syntax 2> #<syntax 3>)
The pattern variables introduced by ``with-syntax``
-are automatically expanded inside the syntax template, without
-resorting to the quasisyntax notation (i.e. there is no need for
+are automatically expanded inside the syntax template, without need to
+resort to the quasisyntax notation (i.e. there is no need for
``#``` ``#,`` ``#,@``).
-The previous paragraphs about syntax objects were a little abstract and
-probably of unclear utility (but what would you expect from
-an advanced macro tutorial? ;). Now let me be more
-concrete. I will provide an example where
-``syntax-match`` is used as a list matcher inside a bigger macro.
-The final goal is to provide
-a nicer syntax for association lists (an association list is just
-a non-empty list of non-empty lists). The macro accepts a variable
-number of arguments; every argument is of the form ``(name value)`` or
-it is a single identifier: in this case latter case it must be
-magically converted
-into the form ``(name value)`` where ``value`` is the value of the
-identifier, assuming it is bound in the current scope, otherwise
-a run time error is raised ``"unbound identifier"``. If you try to
-pass an argument which is not of the expected form, a compile time
-syntax error must be raised.
-In concrete, the macro works as follows:
-
-$$TEST-ALIST
+What macros really are
+--------------------------------------
-``(alist a (b (* 2 a)))`` would raise an error ``unbound identifier a``.
-Here is the implementation:
-
-$$ALIST
+Macros are in one-to-one correspondence with syntax transformers, i.e. every
+macro is associated to a transformer which converts a syntax object
+(the macro and its arguments) into another syntax object (the
+expansion of the macro). Scheme itself takes care of converting the
+input code into a syntax object (if you wish, internally
+there is a ``datum->syntax`` conversion) and the output syntax object
+into code (an internal ``syntax->datum`` conversion).
-The expression ``#'(arg ...)`` expands to a list of syntax
-objects which are then transformed by is the ``syntax-match`` transformer,
-which converts identifiers of the form ``n`` into couples of the form
-``(n n)``, whereas it leaves couples ``(n v)`` unchanged, however
-it checks that ``n`` is an identifier.
+Consider for instance a macro to apply a function to a (single)
+argument:
-Macros as list transformers
----------------------------------------------------------------------
-
-Macros are in one-to-one correspondence with list transformers, i.e. every
-macro is associated to a transformer which converts a list of syntax objects
-(the arguments of the macro) into another list of syntax objects (the expansion
-of the macro). Scheme itself takes care of converting the input code
-into a list of syntax objects (if you wish, internally there is a
-``datum->syntax`` conversion) and the output syntax list into code
-(an internal ``syntax->datum`` conversion).
-The sharp-quote notation in macros is just an abbreviation for the underlying
-list: for instance a macro describing function composition
+.. code-block:: scheme
-::
+ (def-syntax (apply1 f a)
+ #'(f a))
- (def-syntax (o f g)
- #'(f g))
+This macro can be equivalently written as
-can be written equivalently also as
+.. code-block:: scheme
-::
+ (def-syntax apply1 (syntax-match () (sub (apply1 f a) (list #'f #'a))))
- (def-syntax (o f g)
- (list #'f #'g))
+The sharp-quoted syntax is more readable, but it hides the underlying list
+representation which in some cases is pretty useful. This second form
+of the macro is more explicit, but still it relies on ``syntax-match``.
+It is possible to provide the same functionality without using
+``syntax-match`` as follows:
-or
+$$APPLY1
-::
+Here the macro transformer is explicitly written as a lambda function,
+and the pattern matching is performed by hand by converting
+the input syntax object into a list and by using the list
+destructuring form ``let+`` introduced in episode 15_. At the
+end, the resulting list is converted back to a syntax object
+in the context of ``apply1``. Here is an example of usage:
- (def-syntax (o f g)
- (cons* #'f #'g #'()))
+.. code-block:: scheme
-The sharp-quoted syntax is more readable, but it hides the underlying list
-representation which in some cases is pretty useful. This is why
-``syntax-match`` macros are much more powerful than ``syntax-rules``
-macros.
+ > (apply1 display "hey")
+ hey
``sweet-macros`` provide a convenient feature:
it is possible to extract the associated
transformer for each macro defined via ``def-syntax``. For instance,
-here is the transformer associated to the ``o`` macro:
+here is the transformer associated to the ``apply1`` macro:
.. code-block:: scheme
- > (define tr (o <transformer>))
- > (tr (list #'o #'f #'g))
- (#<syntax f> #<syntax g> . #<syntax ()>)
+ > (define tr (apply1 <transformer>))
+ > (tr #'(apply1 display "hey"))
+ #<syntax (display "hey")>
+
+The ability to extract the underlying transformer is useful in
+certain situations, in particular when debugging. It can also
+be exploited to define extensible macros, and I will come back
+to this point in the future.
-Notice that the name of the macro (in this case ``define-a`` is ignored
-by the transformer, i.e. it is a dummy identifier.
+A nicer syntax for association lists
+--------------------------------------------
+The previous paragraphs were a little abstract and
+probably of unclear utility (but what would you expect from
+an advanced macro tutorial? ;). Now let me be more
+concrete. My goal is to provide
+a nicer syntax for association lists (an association list is just
+a non-empty list of non-empty lists) by means of an ``alist``
+macro expanding into an association list.
+The macro accepts a variable
+number of arguments; every argument is of the form ``(name value)`` or
+it is a single identifier: in this case latter case it must be
+magically converted
+into the form ``(name value)`` where ``value`` is the value of the
+identifier, assuming it is bound in the current scope, otherwise
+a run time error is raised ``"unbound identifier"``. If you try to
+pass an argument which is not of the expected form, a compile time
+syntax error must be raised.
+In concrete, the macro works as follows:
+
+$$TEST-ALIST
+
+Here is the implementation:
+
+$$ALIST
+
+The expression ``#'(arg ...)`` expands into a list of syntax
+objects which are then transformed by the ``syntax-match`` transformer,
+which converts identifiers of the form ``n`` into couples of the form
+``(n n)``, whereas it leaves couples ``(n v)`` unchanged, just
+checking that ``n`` is an identifier.
+This is a typical use case for
+``syntax-match`` as a list matcher inside a bigger macro. We will
+see other use cases in the next Adventures.
+
+.. _15: http://www.artima.com/weblogs/viewpost.jsp?thread=249681
|#
(import (rnrs) (sweet-macros) (aps easy-test) (aps compat)
(for (aps list-utils) expand) (for (aps record-syntax) expand run))
@@ -292,6 +311,13 @@ by the transformer, i.e. it is a dummy identifier.
; (sub (ctx v title) (syntax (vector-ref v 0)))
; (sub (ctx v author) (syntax (vector-ref v 1)))))
+;;APPLY1
+(def-syntax apply1
+ (lambda (x)
+ (let+ ((macro-name func arg) (syntax->datum x))
+ (datum->syntax #'apply1 (list func arg)))))
+;;END
+
(display (syntax-expand (alist (a 1) (b (* 2 a)))))
(run
@@ -302,12 +328,8 @@ by the transformer, i.e. it is a dummy identifier.
(alist a (b 1) (c (* 2 b))))
'((a 0) (b 1) (c 2)))
- ;;END
- ;(test "with-error"
- ; (catch-error (alist2 (a 1) (2 3)))
- ; "invalid syntax")
-
-)
-
-
-
+ (test "with-error"
+ (catch-error (alist a))
+ "unbound variable")
+;;END
+ )