# STklos Reference Manual

2. Expressions

Contents
| *Top*
1 | Introduction

2

Expressions

2.1 | Literal expressions
2.2 | Procedures
2.3 | Assignments
2.4 | Conditionals
2.5 | Binding Constructs
2.6 | Sequencing
2.7 | Iterations
2.8 | Delayed Evaluation
2.9 | Quasiquotation
2.10 | Macros

3 | Program structure
4 | Standard Procedures
5 | Regular Expressions
6 | Pattern Matching
7 | Exceptions and Conditions
8 | STklos Object System
9 | Threads, Mutexes and Condition Variables
10 | Customizations
11 | Using the SLIB package
12 | SRFIs
13 | Index
| Bibliography

This chapter describes the main forms available in STklos. R5RS constructions are given very succinctly here for reference. See the (ref :bib "R5RS") for a complete description.

### 2.1 Literal expressions

`(quote <datum>)` | R5RS syntax

`'<datum>`
The quoting mechanism is identical to R5RS, except that keywords constants evaluate "to themselves" as numerical constants, string constants, character constants, and boolean constants

>     '"abc"     ⇒  "abc"
>     "abc"      ⇒  "abc"
>     '145932    ⇒  145932
>     145932     ⇒  145932
>     '#t        ⇒  #t
>     #t         ⇒  #t
>     :foo       ⇒  :foo
>     ':foo      ⇒  :foo

Note: R5RS requires to quote constant lists and constant vectors. This is not necessary with STklos.

### 2.2 Procedures

`(lambda <formals> <body>)` | STklos syntax

A lambda expression evaluates to a procedure. STklos lambda expression have been extended to allow a optional and keyword parameters. `<formals>` should have one of the following forms:

- `(<variable1> ...)`
  The procedure takes a fixed number of arguments; when the procedure is called, the arguments will be stored in the bindings of the corresponding variables. This form is identical to R5RS.
- `<variable>`
  The procedure takes any number of arguments; when the procedure is called, the sequence of actual arguments is converted into a newly allocated list, and the list is stored in the binding of the `<variable>`. This form is identical to R5RS.
- `(<variable1> ... <variablen> . <variablen+1>)`
  If a space-delimited period precedes the last variable, then the procedure takes n or more arguments, where n is the number of formal arguments before the period (there must be at least one). The value stored in the binding of the last variable will be a newly allocated list of the actual arguments left over after all the other actual arguments have been matched up against the other formal arguments. This form is identical to R5RS.
- `(<variable1 ... <variablen> [:optional ...] [:rest ...] [:key ...])`
  This form is specific to STklos and allows to have procedure with optional and keyword parameters. The form `:optional` allows to specify optional parameters. All the parameters specified after `:optional` to the end of `<formals>` (or until a `:rest` or `:key`) are optional parameters. An optional parameter can declared as:
  - `variable`: if a value is passed when the procedure is called, it will be stored in the binding of the corresponding variable, otherwise the value `#f` will be stored in it.
  - `(variable value)`: if a value is passed when the procedure is called, it will be stored in the binding of the corresponding variable, otherwise `value` will be stored in it.
  - `(variable value test?)`: if a value is passed when the procedure is called, it will be stored in the binding of the corresponding variable, otherwise `value` will be stored in it. Furthermore, `test?` will be given the value `#t` if a value is passed for the given variable, otherwise `test?` is set to `#f`
  Hereafter are some examples using `:optional` parameters

  >     ((lambda (a b :optional c d) (list a b c d)) 1 2)
  >                                 ⇒ (1 2 #f #f)
  >     ((lambda (a b :optional c d) (list a b c d)) 1 2 3)
  >                                 ⇒ (1 2 3 #f)
  >     ((lambda (a b :optional c (d 100)) (list a b c d)) 1 2 3)
  >                                 ⇒ (1 2 3 100)
  >     ((lambda (a b :optional c (d #f d?)) (list a b c d d?)) 1 2 3)
  >                                 ⇒ (1 2 3 #f #f)

  The form `:rest` parameter is similar to the dot notation seen before. It is used before an identifier to collects the parameters in a single binding:

  >     ((lambda (a :rest b) (list a b)) 1)
  >                                 ⇒ (1 ())
  >     ((lambda (a :rest b) (list a b)) 1 2)
  >                                 ⇒ (1 (2))
  >     ((lambda (a :rest b) (list a b)) 1 2 3)
  >                                 ⇒ (1 (2 3))

  The form `:key` allows to use keyword parameter passing. All the parameters specified after `:key` to the end of `<formals>` are keyword parameters. A keyword parameter can be declared using the three forms given for optional parameters. Here are some examples illustrating how to declare and how to use keyword parameters:

  >     ((lambda (a :key b c) (list a b c)) 1 :c 2 :b 3)
  >                                 ⇒ (1 3 2)
  >     ((lambda (a :key b c) (list a b c)) 1 :c 2)
  >                                 ⇒ (1 #f 2)
  >     ((lambda (a :key (b 100 b?) c) (list a b c b?)) 1 :c 2)
  >                                 ⇒ (1 100 2 #f)

  At last, here is an example showing `:optional` `:rest` and `:key` parameters

  >     (define f (lambda (a :optional b :rest c :key d e)
  >                  (list a b c d e)))

  >     (f 1)                       ⇒ (1 #f () #f #f)
  >     (f 1 2)                     ⇒ (1 2 () #f #f)
  >     (f 1 2 :d 3 :e 4)           ⇒ (1 2 (:d 3 :e 4) 3 4)
  >     (f 1 :d 3 :e 4)             ⇒ (1 #f (:d 3 :e 4) 3 4)

`(closure? obj)` | STklos procedure

Returns `#t` if `obj` is a procedure created with the `lambda` syntax and `#f` otherwise.

`(case-lambda <clause> ...)` | STklos syntax

Each `<clause>` should have the form `(<formals> <body>)`, where `<formals>` is a formal arguments list as for `lambda`. Each `<body>` is a `<tail-body>`, as defined in R5RS.
A `case-lambda` expression evaluates to a procedure that accepts a variable number of arguments and is lexically scoped in the same manner as procedures resulting from `lambda` expressions. When the procedure is called with some arguments `v1 ... vk`, then the first `<clause>` for which the arguments agree with `<formals>` is selected, where agreement is specified as for the `<formals>` of a `lambda` expression. The variables of `<formals>` are bound to fresh locations, the values `v1 ... vk` are stored in those locations, the `<body>` is evaluated in the extended environment, and the results of `<body>` are returned as the results of the procedure call.
It is an error for the arguments not to agree with the `<formals>` of any `<clause>`.
This form is defined in SRFI-16 (Syntax for procedures of variable arity).

>      (define plus
>        (case-lambda
>         (() 0)
>         ((x) x)
>         ((x y) (+ x y))
>         ((x y z) (+ (+ x y) z))
>         (args (apply + args))))

>      (plus)                     ⇒ 0
>      (plus 1)                   ⇒ 1
>      (plus 1 2 3)               ⇒ 6

>      ((case-lambda
>        ((a) a)
>        ((a b) (* a b)))
>       1 2 3)                    ⇒ error

### 2.3 Assignments

`(set! <variable> <expression>)` | R5RS syntax

`(set! (<proc> <arg> ...) <expression>)`
The first form of `set!` is the R5RS one:
`<Expression>` is evaluated, and the resulting value is stored in the location to which `<variable>` is bound. `<Variable>` must be bound either in some region enclosing the `set!` expression or at top level.

>     (define x 2)
>     (+ x 1)                   ⇒  3
>     (set! x 4)                ⇒  unspecified
>     (+ x 1)                   ⇒  5

The second form of `set!` is defined in SRFI-17 (Generalized set!):
This special form `set!` is extended so the first operand can be a procedure application, and not just a variable. The procedure is typically one that extracts a component from some data structure. Informally, when the procedure is called in the first operand of `set!`, it causes the corresponding component to be replaced by the second operand. For example,

>     (set (vector-ref x i) v)

would be equivalent to:

>     (vector-set! x i v)

Each procedure that may be used as the first operand to `set!` must have a corresponding setter procedure. The procedure `setter` (see below) takes a procedure and returns the corresponding setter procedure. So,

>     (set! (proc arg ...) value)

is equivalent to the call

>     ((setter proc) arg ... value)

The result of the `set!` expression is unspecified.

`(setter proc)` | R5RS procedure

Returns the setter associated to a `proc`. Setters are defined in the SRFI-17 (Generalized set!) document. A setter proc, can be used in a generalized assignment, as described in `set!`.
To associate `s` to the procedure `p`, use the following form:

>     (set! (setter p) s)

For instance, we can write

>     (set! (setter car) set-car!)

The following standard procedures have pre-defined setters:

>     (set! (car x) v)              == (set-car! x v)
>     (set! (cdr x) v)              == (set-cdr! x v)
>     (set! (string-ref x i) v)     == (string-set! x i v)
>     (set! (vector-ref x i) v)     == (vector-set! x i v)!
>     (set! (slot-ref x 'name) v)   == (slot-set! x 'name v)
>     (set! (struct-ref x 'name) v) == (struct-set! x 'name v)

Furhermore, parameters objects are their own setter:

>     (real-precision)              ⇒ 15
>     (set! (real-precision) 12)
>     (real-precision)              ⇒ 12

### 2.4 Conditionals

`(if <test> <consequent> <alternate>)` | R5RS syntax

`(if <test> <consequent>)`
An `if` expression is evaluated as follows: first, `<test>` is evaluated. If it yields a true value, then `<consequent>` is evaluated and its value(s) is(are) returned. Otherwise `<alternate>` is evaluated and its value(s) is(are) returned. If `<test>` yields a false value and no `<alternate>` is specified, then the result of the expression is void.

>       (if (> 3 2) 'yes 'no)           ⇒  yes
>       (if (> 2 3) 'yes 'no)           ⇒  no
>       (if (> 3 2)
>           (- 3 2)
>           (+ 3 2))                    ⇒  1

`(cond <clause1> <clause2> ...)` | R5RS syntax

In a `cond`, each `<clause>` should be of the form

>     (<test> <expression1> ...)

where `<test>` is any expression. Alternatively, a `<clause>` may be of the form

>     (<test> ⇒ <expression>)

The last `<clause>` may be an "else clause," which has the form

>     (else <expression1> <expression2> ...)

A cond expression is evaluated by evaluating the `<test>` expressions of successive `<clause>`s in order until one of them evaluates to a true value When a `<test>` evaluates to a true value, then the remaining `<expression>`s in its `<clause>` are evaluated in order, and the result(s) of the last `<expression>` in the `<clause>` is(are) returned as the result(s) of the entire cond expression. If the selected `<clause>` contains only the `<test>` and no `<expression>`s, then the value of the `<test>` is returned as the result. If the selected `<clause>` uses the ⇒ alternate form, then the `<expression>` is evaluated. Its value must be a procedure that accepts one argument; this procedure is then called on the value of the `<test>` and the value(s) returned by this procedure is(are) returned by the cond expression.
If all `<test>`s evaluate to false values, and there is no else clause, then the result of the conditional expression is void; if there is an else clause, then its `<expression>`s are evaluated, and the value(s) of the last one is(are) returned.

>       (cond ((> 3 2) 'greater)
>             ((< 3 2) 'less))                    ⇒  greater

>       (cond ((> 3 3) 'greater)
>             ((< 3 3) 'less)
>             (else 'equal))                      ⇒  equal

>       (cond ((assv 'b '((a 1) (b 2))) ⇒ cadr)
>             (else #f))                          ⇒  2

`(case <key> <clause1> <clause2> ...)` | R5RS syntax

In a `case`, each `<clause>` should have the form

>     ((<datum1> ...) <expression1> <expression2> ...),

where each `<datum>` is an external representation of some object. All the `<datum>`s must be distinct. The last `<clause>` may be an "else clause," which has the form

>         (else <expression1> <expression2> ...).

A case expression is evaluated as follows. `<Key>` is evaluated and its result is compared against each `<datum>`. If the result of evaluating `<key>` is equivalent (in the sense of eqv?) to a `<datum>`, then the expressions in the corresponding `<clause>` are evaluated from left to right and the result(s) of the last expression in the `<clause>` is(are) returned as the result(s) of the case expression. If the result of evaluating `<key>` is different from every `<datum>`, then if there is an else clause its expressions are evaluated and the result(s) of the last is(are) the result(s) of the case expression; otherwise the result of the case expression is void.

>       (case (* 2 3)
>         ((2 3 5 7) 'prime)
>         ((1 4 6 8 9) 'composite))     ⇒  composite
>       (case (car '(c d))
>         ((a) 'a)
>         ((b) 'b))                     ⇒  void
>       (case (car '(c d))
>         ((a e i o u) 'vowel)
>         ((w y) 'semivowel)
>         (else 'consonant))            ⇒  consonant

`(and <test1> ...)` | R5RS syntax

The `<test>` expressions are evaluated from left to right, and the value of the first expression that evaluates to a false value is returned. Any remaining expressions are not evaluated. If all the expressions evaluate to true values, the value of the last expression is returned. If there are no expressions then `#t` is returned.

>       (and (= 2 2) (> 2 1))           ⇒  #t
>       (and (= 2 2) (< 2 1))           ⇒  #f
>       (and 1 2 'c '(f g))             ⇒  (f g)
>       (and)                           ⇒  #t

`(or <test1> ...)` | R5RS syntax

The `<test>` expressions are evaluated from left to right, and the value of the first expression that evaluates to a true value is returned. Any remaining expressions are not evaluated. If all expressions evaluate to false values, the value of the last expression is returned. If there are no expressions then `#f` is returned.

>       (or (= 2 2) (> 2 1))            ⇒  #t
>       (or (= 2 2) (< 2 1))            ⇒  #t
>       (or #f #f #f)                   ⇒  #f
>       (or (memq 'b '(a b c))
>           (/ 3 0))                    ⇒  (b c)

`(when <test> <expression1> <expression2> ...)` | STklos syntax

If the `<test>` expression yields a true value, the `<expression>`s are evaluated from left to right and the value of the last `<expression>` is returned. Otherwise, `when` returns void.

`(unless <test> <expression1> <expression2> ...)` | STklos syntax

If the `<test>` expression yields a false value, the `<expression>`s are evaluated from left to right and the value of the last `<expression>` is returned. Otherwise, `unless` returns void.

### 2.5 Binding Constructs

The three binding constructs `let`, `let*`, and `letrec` are available in STklos. These constructs differ in the regions they establish for their variable bindings. In a `let` expression, the initial values are computed before any of the variables become bound; in a `let*` expression, the bindings and evaluations are performed sequentially; while in a `letrec` expression, all the bindings are in effect while their initial values are being computed, thus allowing mutually recursive definitions.
STklos also provides a `fluid-let` form which is described below.

`(let <bindings> <body>)` | R5RS syntax

`(let <variable> <bindings> <body>)`
In a `let`, `<bindings>` should have the form

>     ((<variable1> <init1>) ...)

where each `<init>` is an expression, and `<body>` should be a sequence of one or more expressions. It is an error for a `<variable>` to appear more than once in the list of variables being bound.
The `<init>`s are evaluated in the current environment (in some unspecified order), the `<variable>`s are bound to fresh locations holding the results, the `<body>` is evaluated in the extended environment, and the value(s) of the last expression of `<body>` is(are) returned. Each binding of a `<variable>` has `<body>` as its region.

>     (let ((x 2) (y 3))
>       (* x y))                      ⇒  6

>     (let ((x 2) (y 3))
>       (let ((x 7)
>             (z (+ x y)))
>         (* z x)))                   ⇒  35

The second form of `let`, which is generally called a named let, is a variant on the syntax of let which provides a more general looping construct than `do` (@pxref{do}) and may also be used to express recursions. It has the same syntax and semantics as ordinary let except that `<variable>` is bound within `<body>` to a procedure whose formal arguments are the bound variables and whose body is `<body>`. Thus the execution of `<body>` may be repeated by invoking the procedure named by `<variable>`.

>     (let loop ((numbers '(3 -2 1 6 -5))
>                (nonneg  '())
>                (neg     '()))
>       (cond ((null? numbers) (list nonneg neg))
>             ((>= (car numbers) 0)
>                (loop (cdr numbers)
>                      (cons (car numbers) nonneg)
>                      neg))
>             ((< (car numbers) 0)
>                (loop (cdr numbers)
>                       nonneg
>                       (cons (car numbers) neg)))))
>        ⇒  ((6 1 3) (-5 -2))

`(let* <bindings> <body>)` | R5RS syntax

In a `let*`, `<bindings>` should have the same form as in a `let` (however, a <variable> can appear more than once in the list of variables being bound).
`Let*` is similar to `let`, but the bindings are performed sequentially from left to right, and the region of a binding indicated by

>     (<variable> <init>)

is that part of the `let*` expression to the right of the binding. Thus the second binding is done in an environment in which the first binding is visible, and so on.

>     (let ((x 2) (y 3))
>       (let* ((x 7)
>              (z (+ x y)))
>         (* z x)))             ⇒  70

`(letrec <bindings> <body>)` | R5RS syntax

<bindings> should have the form as in `let`. The `<variable>`s are bound to fresh locations holding undefined values, the `<init>`s are evaluated in the resulting environment (in some unspecified order), each `<variable>` is assigned to the result of the corresponding `<init>`, the `<body>` is evaluated in the resulting environment, and the value(s) of the last expression in `<body>` is(are) returned. Each binding of a `<variable>` has the entire `letrec` expression as its region, making it possible to define mutually recursive procedures.

>     (letrec ((even? (lambda (n)
>                       (if (zero? n)
>                           #t
>                           (odd? (- n 1)))))
>              (odd?  (lambda (n)
>                       (if (zero? n)
>                           #f
>                           (even? (- n 1))))))
>       (even? 88))
>                       ⇒  #t

`(fluid-let <bindings> <body>)` | STklos syntax

The `<bindings>` are evaluated in the current environment, in some unspecified order, the current values of the variables present in `<bindings>` are saved, and the new evaluated values are assigned to the `<bindings>` variables. Once this is done, the expressions of `<body>` are evaluated sequentially in the current environment; the value of the last expression is the result of `fluid-let`. Upon exit, the stored variables values are restored. An error is signalled if any of the `<bindings>` variable is unbound.

>     (let* ((a 'out)
>            (f (lambda () a)))
>       (list (f)
>             (fluid-let ((a 'in)) (f))
>             (f))) ⇒ (out in out)

When the body of a `fluid-let` is exited by invoking a continuation, the new variable values are saved, and the variables are set to their old values. Then, if the body is reentered by invoking a continuation, the old values are saved and new values are restored. The following example illustrates this behavior

>     (let ((cont #f)
>           (l    '())
>           (a    'out))
>       (set! l (cons a l))
>       (fluid-let ((a 'in))
>         (set! cont (call-with-current-continuation (lambda (k) k)))
>         (set! l (cons a l)))
>       (set! l (cons a l))

>       (if cont (cont #f) l)) ⇒  (out in out in out)

### 2.6 Sequencing

`(begin <expression1> <expression2> ...)` | R5RS syntax

The `<expression>`s are evaluated sequentially from left to right, and the value(s) of the last `<expression>` is(are) returned. This expression type is used to sequence side effects such as input and output.

>       (define x 0)

>       (begin (set! x 5)
>              (+ x 1))                  ⇒  6

>       (begin (display "4 plus 1 equals ")
>              (display (+ 4 1)))        -| 4 plus 1 equals 5
>                                        ⇒  void

### 2.7 Iterations

`(do [[<var1> <init1> <step1>] ...] [<test> <expr> ...] <command> ...)` | R5RS syntax

`Do` is an iteration construct. It specifies a set of variables to be bound, how they are to be initialized at the start, and how they are to be updated on each iteration. When a termination condition is met, the loop exits after evaluating the `<expr>`s.
`Do` expressions are evaluated as follows: The `<init>` expressions are evaluated (in some unspecified order), the `<var>`s are bound to fresh locations, the results of the `<init>` expressions are stored in the bindings of the `<var>`s, and then the iteration phase begins.
Each iteration begins by evaluating `<test>`; if the result is false then the `<command>` expressions are evaluated in order for effect, the `<step>` expressions are evaluated in some unspecified order, the `<var>`s are bound to fresh locations, the results of the `<step>`s are stored in the bindings of the `<var>`s, and the next iteration begins.
If `<test>` evaluates to a true value, then the `<expr>`s are evaluated from left to right and the value(s) of the last `<expr>` is(are) returned. If no `<expr>`s are present, then the value of the do expression is void.
The region of the binding of a `<var>` consists of the entire do expression except for the `<init>`s. It is an error for a `<var>` to appear more than once in the list of do variables.
A `<step>` may be omitted, in which case the effect is the same as if

>     (<var> <init> <var>)

had been written.

>       (do ((vec (make-vector 5))
>            (i 0 (+ i 1)))
>           ((= i 5) vec)
>         (vector-set! vec i i))            ⇒  #(0 1 2 3 4)

>       (let ((x '(1 3 5 7 9)))
>         (do ((x x (cdr x))
>              (sum 0 (+ sum (car x))))
>             ((null? x) sum)))             ⇒  25

`(dotimes [var count] <expression1> <expression2> ... )` | STklos syntax

`(dotimes [var count result] <expression1> <expression2> ... )`
Evaluates the `count` expression, which must return an integer and then evaluates the `<expression>`s once for each integer from zero (inclusive) to `count` (exclusive), in order, with the symbol `var` bound to the integer; if the value of `count` is zero or negative, then the `<expression>`s are not evaluated. When the loop completes, `result` is evaluated and its value is returned as the value of the `dotimes` construction. If `result` is omitted, `dotimes` result is void.

>     (let ((l '()))
>       (dotimes (i 4 l)
>          (set! l (cons i l)))) ⇒ (3 2 1 0)

`(while <test> <expression1> <expression2> ...)` | STklos syntax

`While` evaluates the `<expression>`s until `<test>` returns a false value. The value returned by this form is void.

`(until <test> <expression1> <expression2> ...)` | STklos syntax

`Until` evaluates the `<expression>`s until `<while>` returns a false value. The value returned by this form is void.

### 2.8 Delayed Evaluation

`(delay <expression>)` | R5RS procedure

The `delay` construct is used together with the procedure `force` to implement lazy evaluation or call by need. `(delay <expression>)` returns an object called a promise which at some point in the future may be asked (by the `force` procedure) to evaluate `<expression>`, and deliver the resulting value. The effect of `<expression>` returning multiple values is unpredictable. See the description of `force` (@pxref{force}) for a more complete description of `delay`.

`(promise? obj)` | STklos procedure

Returns `#t` if `obj` is a promise, otherwise returns `#f`.

### 2.9 Quasiquotation

`(quasiquote <template>)` | R5RS syntax

``<template>`
"Backquote" or "quasiquote" expressions are useful for constructing a list or vector structure when most but not all of the desired structure is known in advance. If no commas appear within the `<template>`, the result of evaluating ``<template>` is equivalent to the result of evaluating `'<template>`. If a comma appears within the `<template>`, however, the expression following the comma is evaluated ("unquoted") and its result is inserted into the structure instead of the comma and the expression. If a comma appears followed immediately by an at-sign (@), then the following expression must evaluate to a list; the opening and closing parentheses of the list are then "stripped away" and the elements of the list are inserted in place of the comma at-sign expression sequence. A comma at-sign should only appear within a list or vector `<template>`.

>     `(list ,(+ 1 2) 4)  ⇒  (list 3 4)
>     (let ((name 'a)) `(list ,name ',name))
>                         ⇒  (list a (quote a))
>     `(a ,(+ 1 2) ,@(map abs '(4 -5 6)) b)
>                         ⇒  (a 3 4 5 6 b)
>     `((foo ,(- 10 3)) ,@(cdr '(c)) . )
>                         ⇒  ((foo 7) . cons)
>     `#(10 5 ,(sqrt 4) ,@(map sqrt '(16 9)) 8)
>                         ⇒  #(10 5 2 4 3 8)

Quasiquote forms may be nested. Substitutions are made only for unquoted components appearing at the same nesting level as the outermost backquote. The nesting level increases by one inside each successive quasiquotation, and decreases by one inside each unquotation.

>     `(a `(b ,(+ 1 2) ,(foo ,(+ 1 3) d) e) f)
>               ⇒  (a `(b ,(+ 1 2) ,(foo 4 d) e) f)
>     (let ((name1 'x)
>           (name2 'y))
>       `(a `(b ,,name1 ,',name2 d) e))
>               ⇒  (a `(b ,x ,'y d) e)

The two notations ``<template>` and `(quasiquote <template>)` are identical in all respects. `,<expression>` is identical to `(unquote <expression>)`, and `,@<expression>` is identical to `(unquote-splicing <expression>)`.

### 2.10 Macros

STklos supports hygienic macros such as the ones defined in R5RS as well as low level macros.

Low level macros are defined with `define-macro` whereas R5RS macros are defined with `define-syntax` 1. Hygienic macros use the implementation called Macro by Example (Eugene Kohlbecker, R4RS) done by Dorai Sitaram. This implementation generates low level STklos macros. This implementation of hygienic macros is not expensive.

The major drawback of this implementation is that the macros are not referentially transparent (see section `Macros' in R4RS for details). Lexically scoped macros (i.e., `let-syntax` and `letrec-syntax` are not supported). In any case, the problem of referential transparency gains poignancy only when `let-syntax` and `letrec-syntax` are used. So you will not be courting large-scale disaster unless you're using system-function names as local variables with unintuitive bindings that the macro can't use. However, if you must have the full R5RS macro functionality, you can do

>     (require "full-syntax")

to have access to the more featureful (but also more expensive) versions of `syntax-rules`. Requiring `"full-syntax"` loads the version 2.1 of an implementation of hygienic macros by Robert Hieb and R. Kent Dybvig.

`(define-macro (<name> <formals>) <body>)` | STklos syntax

`(define-macro <name> (lambda <formals> <body>))`
`define-macro` can be used to define low-level macro (i.e. non hygienic macros). This form is similar to the `defmacro` form of Common Lisp.

>     (define-macro (incr x) `(set! ,x (+ ,x 1)))
>     (let ((a 1)) (incr a) a)   ⇒ 2

>     (define-macro (when test . body)
>       `(if ,test ,@(if (null? (cdr body)) body `((begin ,@body)))))
>     (macro-expand '(when a b)) ⇒ (if a b)
>     (macro-expand '(when a b c d))
>                                ⇒ (if a (begin b c d))

>     (define-macro (my-and . exprs)
>       (cond
>        ((null? exprs)        #t)
>        ((= (length exprs) 1) (car exprs))
>        (else                 `(if ,(car exprs)
>     			      (my-and ,@(cdr exprs))
>     			      #f))))
>     (macro-expand '(my-and a b c))
>                               ⇒ (if a (my-and b c) #f)

`(define-syntax <identifier> <transformer-spec>)` | R5RS syntax

`<Define-syntax>` extends the top-level syntactic environment by binding the `<identifier>` to the specified transformer. Note: `<transformer-spec>` should be an instance of `syntax-rules`.

>     (define-syntax let*
>       (syntax-rules ()
>         ((let* () body1 body2 ...)
>          (let () body1 body2 ...))
>         ((let* ((name1 val1) (name2 val2) ...)
>            body1 body2 ...)
>          (let ((name1 val1))
>            (let* (( name2 val2) ...)
>              body1 body2 ...))))

`(syntax-rules <literals> <syntax-rule> ...)` | R5RS syntax

`<literals>` is a list of identifiers, and each `<syntax-rule>` should be of the form

>     (pattern template)

An instance of `<syntax-rules>` produces a new macro transformer by specifying a sequence of hygienic rewrite rules. A use of a macro whose name is associated with a transformer specified by <syntax-rules> is matched against the patterns contained in the <syntax-rules>, beginning with the leftmost syntax-rule. When a match is found, the macro use is transcribed hygienically according to the template.
Each pattern begins with the name for the macro. This name is not involved in the matching and is not considered a pattern variable or literal identifier.
Note: For a complete description of the Scheme pattern language, refer to R5RS.

`(let-syntax <bindings> <body>)` | R5RS syntax

`<Bindings>` should have the form

>     ((<keyword> <transformer spec>) ...)

Each `<keyword>` is an identifier, each `<transformer spec>` is an instance of `syntax-rules`, and `<body>` should be a sequence of one or more expressions. It is an error for a `<keyword>` to appear more than once in the list of keywords being bound.
The `<body>` is expanded in the syntactic environment obtained by extending the syntactic environment of the `let-syntax` expression with macros whose keywords are the `<keyword>`s, bound to the specified transformers. Each binding of a `<keyword>` has `<body>` as its region.
Note: `let-syntax` is available only after having required the file `"full-syntax"`.

>     (let-syntax ((when (syntax-rules ()
>     		     ((when test stmt1 stmt2 ...)
>     		      (if test
>     			  (begin stmt1
>     				 stmt2 ...))))))
>       (let ((if #t))
>         (when if (set! if 'now))
>         if))                           ⇒  now

>     (let ((x 'outer))
>       (let-syntax ((m (syntax-rules () ((m) x))))
>         (let ((x 'inner))
>           (m))))                       ⇒  outer

`(letrec-syntax <bindings> <body>)` | R5RS syntax

Syntax of `letrec-syntax` is the same as for `let-syntax`.
The `<body>` is expanded in the syntactic environment obtained by extending the syntactic environment of the `letrec-syntax` expression with macros whose keywords are the `<keyword>`s, bound to the specified transformers. Each binding of a `<keyword>` has the `<bindings>` as well as the `<body>` within its region, so the transformers can transcribe expressions into uses of the macros introduced by the `letrec-syntax` expression.
Note: `letrec-syntax` is available only after having required the file `"full-syntax"`.

>     (letrec-syntax
>       ((my-or (syntax-rules ()
>                 ((my-or) #f)
>                 ((my-or e) e)
>                 ((my-or e1 e2 ...)
>                  (let ((temp e1))
>                    (if temp
>                        temp
>                        (my-or e2 ...)))))))
>       (let ((x #f)
>             (y 7)
>             (temp 8)
>             (let odd?)
>             (if even?))
>         (my-or x
>                (let temp)
>                (if y)
>                y)))        ⇒  7

`(macro-expand form)` | STklos procedure

Returns the macro expansion of `form` if it is a macro call, otherwise `form` is returned unchanged.

>     (define-macro (incr x) `(set! ,x (+ ,x 1)))
>     (macro-expand '(incr foo)) ⇒ (set! foo (+ foo 1))
>     (macro-expand '(car bar))  ⇒ (car bar)

1: Documentation about hygienic macros has been stolen in the SLIB manual

This Html page has been produced by Skribe.
Last update Tue Dec 19 15:25:36 2006