Syntactic extensions

Course links

Exercise 1

Define a syntactic extension unless with a test and one or more actions as its subexpressions, so that the actions will be evaluated (in order) if, but only if, the value of the test is #f. For instance, the unless-expression

(unless (eof-object? next-character)
  (write-char next-character target)
  (kernel (read-char source)))

should have the same effect as

(if (not (eof-object? next-character))
    (begin
      (write-char next-character target)
      (kernel (read-char source))))

Exercise 2

An assertion is a Boolean expression that is tested at a particular point in the execution of a program to make sure that it is true (usually because it constitutes a precondition for the successful execution of the next step in the program); if the expression turns out to be false, execution of the program is halted and an error is reported. For instance, the display-calendar procedure at the end of the program that we used in project #4 began with two assertions:

(define display-calendar
  (lambda (month year)

    ;; Test the preconditions.

    (if (not (month-number? month))
        (error 'first-of-month
               "The first argument must be the number of a month"))
    (if (not (Gregorian-year? year))
        (error 'first-of-month
               "The second argument must be a year of the Gregorian calendar"))

    ...))

The assertions are (month-number? month) and (Gregorian-year? year). If either of these assertions fails, the program crashes.

Define a syntactic extension assert that has one expression, the assertion, and does nothing if the value of the assertion is true, but invokes error if the assertion is false. With the help of assert-expressions, we could write the beginning of display-calendar thus:

(define display-calendar
  (lambda (month year)
    (assert (month-number? month))
    (assert (Gregorian-year? year))
    ...))

> (display-calendar 38 2005)
(bug) Assertion failed:  (month-number? month)
> (display-calendar 7 1362)
(bug) Assertion failed:  (Gregorian-year? year)
> (display-calendar 7 1951)
July 1951

 Su  M Tu  W Th  F Sa
  1  2  3  4  5  6  7
  8  9 10 11 12 13 14
 15 16 17 18 19 20 21
 22 23 24 25 26 27 28
 29 30 31

Exercise 3

Edward Kiser has proposed a replace-expression type for Scheme, as yet another way to write simple recursions. In its simplest form, a replace-expression would include a variable, an initializing expression for that variable, an updating expression for that variable, and an exit-test expression to indicate when the updating process should stop. Each of the subexpressions would be introduced by an internal keyword -- the initializer with initially, the updater with with, and the exit test with until. So, for instance, to find the least power of two greater than 1000, one writes

(replace x initially 1 with (* x 2) until (> x 1000))

The value of this expression is 1024.

Similarly, to build a list containing five occurrences of the symbol foo, one could write

(replace ls initially '() with (cons 'foo ls) until (= (length ls) 5))

Define replace as a syntactic extension.

Exercise 4

Use a replace-expression to define a Scheme procedure that computes the sum of the elements of a given list. (Hint: the value of the variable in the replace-expression should be a data structure that contains a running total of the list elements so far inspected and a list of the elements that have not yet been added to the total.)

Exercise 5

Adapt the syntax definition for replace in exercise 3 so that it also allows a second variant, using the internal keyword while rather than until, in which the loop keeps running as long as the value of the test expression is truish. (In other words, the test is a re-entry test rather than an exit test.)

> (replace x initially 1 with (* x 2) while (<= x 1000))
1024