2. Expressions

This chapter describes the main forms available in STklos. R5RS constructions are given very succinctly here for reference. See [R5RS] for a complete description.

2.1. Literal expressions

R5RS syntax

(quote <datum>)
'<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
R5RS requires to quote constant lists and constant vectors. This is not necessary with STklos.

2.2. Procedures

STklos syntax

(lambda <formals> <body>)

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)

STklos procedure

(closure? obj)

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

Note that primitive procedures (those which are written in C) are not closures:

(define (cube x) (* x x x))
(closure? cube)               => #t

(define square-root sqrt)
(eq? square-root sqrt)        => #t
(closure? square-root)        => #f

(closure? 10)                 => #f
(closure? display)            => #f
(closure? (lambda (x) (- x))) => #t
(closure? any)                => #t

STklos syntax

(case-lambda <clause> …​)

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

R5RS syntax

(set! <variable> <expression>)
(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.

STklos procedure

(setter proc)

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)
(set! (list-ref x i) v)       == (list-set! x i v)

Furhermore, Parameter Objects (see Section 4.21) are their own setter:

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

2.4. Conditionals

R5RS syntax

(if <test> <consequent> <alternate>)
(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

R5RS syntax

(cond <clause1> <clause2> …​)

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

R7RS syntax

(case <key> <clause1> <clause2> …​)

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.

If the selected <clause> or else clause uses the alternate form, then the expression is evaluated. It is an error if its value is not a procedure accepting one argument. This procedure is then called on the value of the hkeyi and the values returned by this procedure are returned by the case expression.

  (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
  (case (car '(c d))
    ((a e i o u) 'vowel)
    ((w y) 'semivowel)
    (else  => (lambda (x) (x))))  =>  c

R5RS syntax

(and <test1> …​)

The <testi> 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

R5RS syntax

(or <test1> …​)

The <testi> 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)

STklos syntax

(when <test> <expression1> <expression2> …​)

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.

STklos syntax

(unless <test> <expression1> <expression2> …​)

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.

R5RS syntax

(let <bindings> <body>)
(let <variable> <bindings> <body>)

In a let, <bindings> should have the form

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

where each <initi> 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 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))

R5RS syntax

(let* <bindings> <body>)

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

R5RS syntax

(letrec <bindings> <body>)

<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

R7RS syntax

(letrec* <bindings> <body>)

<bindings> should have the form as in let and body is a sequence of zero or more definitions followed by one or more expressions.

The <variable>s are bound to fresh locations, each variable is assigned in left-to-right order to the result of evaluating the corresponding init, the body is evaluated in the resulting environment, and the values of the last expression in body are returned. Despite the left-to-right evaluation and assignment order, each binding of a variable has the entire letrec* expression as its region, making it possible to define mutually recursive procedures. If it is not possible to evaluate each init without assigning or referring to the value of the corresponding variable or the variable of any of the bindings that follow it in bindings, it is an error.

(letrec* ((p (lambda (x)
               (+ 1 (q (- x 1)))))
          (q(lambda (y)
              (if (zero? y)
                  0
                  (+ 1 (p (- y 1))))))
          (x (p 5))
          (y x))
  y)  => 5

R7RS syntax

(let-values ((<formals> <expression>) …​) <body>)

Each <formals> should be a formal arguments list as for a lambda expression.

The <expression>s are evaluated in the current environment, the variables of the <formals> are bound to fresh locations, the return values of the <expression>s are stored in the variables, the <body> is evaluated in the extended environment, and the values of the last expression of <body> are returned.

The matching of each <formals> to values is as for the matching of <formals> to arguments in a lambda expression, and it is an error for an <expression> to return a number of values that does not match its corresponding <formals>.

(let-values (((root rem) (exact-integer-sqrt 32)))
   (* root rem))            =>  35

(let ((a 'a) (b 'b) (x 'x) (y 'y))
   (let-values (((a b) (values x y))
                ((x y) (values a b)))
     (list a b x y)))      => (x y a b)

R7RS syntax

(let-values ((<formals> <expression>) …​) <body>)

Each <formals> should be a formal arguments list as for a lambda expression.

let*-values is similar to let-values, but the bindings are performed sequentially from left to right, and the region of a binding indicated by (<formals> <expression>) is that part of the let*-values 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 ((a 'a) (b 'b) (x 'x) (y 'y))
   (let*-values (((a b) (values x y))
                ((x y) (values a b)))
     (list a b x y)))      => (x y x y)

R7RS procedure

(define-values formals expression)

The form define-values creates multiple definitions from a single expression returning multiple values. Here, expression is evaluated, and the formals are bound to the return values in the same way that the formals in a lambda expression are matched to the arguments in a procedure call.

(let ()
  (define-values (x y) (exact-integer-sqrt 17))
  (list x y))                   => (4 1)

(let ()
   (define-values (x y) (values 1 2))
   (+ x y))                     => 3

(let ()
   (define-values (x . y) (values 1 2 3))
   (list x y)                  => (1 (2 3))

STklos syntax

(fluid-let <bindings> <body>)

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

R5RS syntax

(begin <expression1> <expression2> …​)

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

STklos syntax

(tagbody <expression1> <expression2> …​)
(→ tag)

The <expression>s are evaluated sequentially from left to right, and the value(s) of the last <expression> is(are) returned as in a begin form. Within a tagbody form expressions which are keywords are considered as tags and the special form (→ tag) is used to transfer execution to the given tag. This is a very low level form which is inspired on tabgody Common Lisp’s form. It can be useful for defining new syntaxes, and should probably not be used as is.

(tagbody               ;; an infinite loop
   #:1 (display ".")
       (-> #:1))

(let ((v 0))
  (tagbody
   #:top (when (< v 5)
           (display v)
           (set! v (fx+ v 1))
           (-> #:top))))                      |- 01234

(tagbody (display 1)
         (tagbody (display 2)
                  (-> #:inner)
                  (display "not printed")
           #:inner
                 (display 3)
                 (-> #:outer)
                 (display "not printed too"))
   #:outer
         (display "4"))                        |- 1234

2.7. Iterations

R5RS syntax

(do [[<var1> <init1> <step1>] …​] [<test> <expr> …​] <command> …​)

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

STklos syntax

(dotimes [var count] <expression1> <expression2> …​)
(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)

STklos syntax

(repeat count <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). The result of repeat is undefined.

This form could be easily simulated with dotimes. Its interest is that it is faster.

(repeat 3 (display "."))     => prints "..."
(repeat 0 (display "."))     => prints nothing

STklos syntax

(while <test> <expression1> <expression2> …​)

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

STklos syntax

(until <test> <expression1> <expression2> …​)

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

STklos procedure

(dolist (x lst) body)
(dolist (x lst result) body)

This macro will iterate over lst, executing body for each element x. The symbol x will be captured and bound in body. If result is provided, and is not x, this form returns result; otherwise, its result is undefined.

(dolist (x '(10 20 30))
  (display (- x)) (newline))
-10
-20
-30
(let ((sum 0))
  (dolist (x '(1 2 3 4 5) sum)
    (inc! sum (square x))))       => 55

The form dolist is borrowed from Common Lisp. However, using dolist is not the preferred way to process a list. For instance, the last example could be better rewritten in:

(apply + (map square '(1 2 3 4 5))

2.8. Delayed Evaluation

R5RS syntax

(delay <expression>)

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 for a more complete description of delay.

R7RS syntax

(delay-force <expression>)
(lazy <expression>)

The expression (delay-force expression) is conceptually similar to (delay (force expression)), with the difference that forcing the result of delay-force will in effect result in a tail call to (force expression), while forcing the result of (delay (force expression)) might not. Thus iterative lazy algorithms that might result in a long series of chains of delay and force can be rewritten using delay-force to prevent consuming unbounded space during evaluation.

The special form delay-force appears with name lazy in SRFI-45 (Primitives for Expressing Iterative Lazy Algorithms).

R5RS procedure

(force promise)

Forces the value of promise (see primitive delay). If no value has been computed for the promise, then a value is computed and returned. The value of the promise is cached (or "memoized") so that if it is forced a second time, the previously computed value is returned.

(force (delay (+ 1 2)))        =>  3
(let ((p (delay (+ 1 2))))
  (list (force p) (force p)))  =>  (3 3)

(define a-stream
  (letrec ((next (lambda (n)
                   (cons n (delay (next (+ n 1)))))))
    (next 0)))
(define head car)
(define tail (lambda (stream) (force (cdr stream))))

(head (tail (tail a-stream)))  =>  2

Force and delay are mainly intended for programs written in functional style. The following examples should not be considered to illustrate good programming style, but they illustrate the property that only one value is computed for a promise, no matter how many times it is forced.

(define count 0)
(define p (delay (begin (set! count (+ count 1))
                        (if (> count x)
                            count
                            (force p)))))
(define x 5)
p                     =>  a promise
(force p)             =>  6
p                     =>  a promise, still
(begin (set! x 10)
       (force p))     =>  6
See R5RS for details on a posssible way to implement force and delay.

R7RS procedure

(promise? obj)

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

R7RS procedure

(make-promise obj)
(eager obj)

The make-promise procedure returns a promise which, when forced, will return obj . It is similar to delay, but does not delay its argument: it is a procedure rather than syntax. If obj is already a promise, it is returned.

The primitve make-promise appears with name eager in SRFI-45.

2.9. Quasiquotation

R5RS syntax

(quasiquote <template>)
<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)) . ,(car '(cons)))
                    =>  ((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.

STklos syntax

(define-macro (<name> <formals>) <body>)
(define-macro <name> (lambda <formals> <body>))

define-macro can be used to define low-level macro (i.e. ,(emph "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)

R5RS syntax

(define-syntax <identifier> <transformer-spec>)

<Define-syntax> extends the top-level syntactic environment by binding the <identifier> to the specified transformer.

<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 ...))))

R5RS syntax

(syntax-rules <literals> <syntax-rule> …​)

<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.

For a complete description of the Scheme pattern language, refer to R5RS.

R5RS syntax

(let-syntax <bindings> <body>)

<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.

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

R5RS syntax

(letrec-syntax <bindings> <body>)

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.

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

STklos procedure

(macro-expand form)
(macro-expand* form)

macro-expand returns the macro expansion of form if it is a macro call, otherwise form is returned unchanged.

(define-macro (add1 x) `(+ ,x 1))
(macro-expand '(add1 foo)) => (+ foo 1)
(macro-expand '(car bar))  => (car bar)

macro-expand returns the full macro expansion of form, that is it repeats the macro-expansion, while the expanded form contains macro calls.

(define-macro (add2 x) `(add1 (add1 ,x)))
(macro-expand '(add2 foo)) => (add1 (add1 foo))
(macro-expand* '(add2 foo)) => (+ (+ foo 1) 1)
macro-expand and macro-expand* expand only the global macros.

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