C311 Scheme Extensions

This document describes the extensions of standard Scheme used in, or related to, the C311 course, but not described in the EOPL text. Transcripts beginning with > illustrate these extensions.

  1. Gensym
  2. Printf
  3. Error
  4. Record-case
  5. Grammar
  6. Define-record and variant-case

Gensym

The procedure gensym, provided by Chez Scheme, returns returns a fresh symbol (a newly generated symbol) when called with no arguments. In Chez Scheme, gensyms (symbols returned by gensym) print as #:g followed by a natural number, as indicated in the example above. In EOPL they print as g followed by a natural number.

> (gensym)
#:g0

Printf

The procedure printf, provided by Chez Scheme, is patterned loosely after the C++ procedure of the same name. It takes a format string followed optionally by other arguments. The characters of the format string are printed in order from left to right until the end of the string is reached, except that if during this process one of the two character sequences that follow is encountered, instead of printing the two characters the specified action is taken.
~s
print the value of the next argument using the procedure write
~a
print the value of the next argument using the procedure display
~c
print the value of the next argument using the procedure write-char
~%
print a newline
~~
print a single tilde character
> (printf "The ~s procedure is very ~a.~%" "printf" "handy")
The "printf" procedure is very handy. 
> (printf "~~dum, ~cdee~%" #\~)
~dum, ~dee

Error

The procedure error, provided by Chez Scheme, takes a symbol, S, followed by a format string and optionally other arguments. It prints a newline, followed by "Error in S: text", where text is the result of invoking printf with the format string and following arguments, followed by another newline, and resets Scheme. When Scheme is reset, control is returned to the waiter (read-evaluate-print loop) and a new top-level prompt is printed.

> (begin (display 'before) (error 'testing "one, ~s" 'two) (display after))
before
Error in testing: one, two
>
Observe that after was never displayed.

By convension the symbol passed as the first argument to error names the procedure that calls it. EOPL uses a version of the procedure error that is somewhat different.

Record-case

The record-case form supports a very restricted form of pattern matching, which dispatches on a symbol in the car of a list and binds the elements of the cdr of a matching list to given formal parameters. This allows dispatch on the form of expressions in Scheme-like syntax, in the manner of variant-case in EOPL, but without requiring a parser. The syntax of a record-case expression is
   (record-case expression 
      clause 
      +..
      [(else expression +..])
where each clause is of the form
   (symbol formals expression +..)

+.. indicates a sequence of one or more of the preceding syntactic elements.

Each clause should have a different symbol.

The formals is a formal parameter specification of any form that might be used in a lambda expression immediately following the keyword lambda.

The expression following record-case is evaluated first; call its value V, which should be a pair. If the car of V is the same as that of one of the clauses, then that clause is selected. It is then almost as if

    (apply (lambda formals expression +..) (cdr V))

were evaluated with V bound to V and formals and expression ... taken from the selected clause, with the resulting value returned as the value of the record-case expression. The diffence is that if the cdr of V contains more values than match the formals, the extra values are ignored, and if it contains fewer values, an error will be reported by car or cdr. (Car-cdr chains are used instead of apply for efficiency.)

If the car of V does not match any of the clause symbols, the else clause is selected and evaluated as in a cond clause, and otherwise an unspecified value is returned.

For example:

> (define test
    (lambda (e)
      (record-case e
	(a (b c) (list b c))
	(b c c)
	(c (a . b) (list a b))
	(else 'whatever))))
> (test '(a 1 2))
(1 2)
> (test '(b 1 2 3 4))
(1 2 3 4)
> (test '(c 1 2 3 4))
(1 (2 3 4))
> (test '(d 1 2 3 4))
whatever
> (test '(a 1 2 3 4))
(1 2)
> (test '(a 1))

Error in car: () is not a pair.

Grammar

Unless one goes to a lot of extra trouble, programs such as expand and eval-exp written using record-case are very fragil. If the given expression is not of the correct form, you are likely to get a mysterious error message (leaving you to suspect your program, not the test case). In other cases part of the input expression may simply be ingored.

The grammar form, described elsewhere, makes it easy to define a syntax checker for the Scheme-like source languages used in this course. For example, the following defines a predicate that accepts expressions in a Scheme-like version of the EOPL section 5.5 source language syntax (assuming let is provided by an expander), and suggests how it could be wired into the run procedure.

> (define exp?
    (grammar expression
      (variable
	(predicate
	  (lambda (x)
	    (and (symbol? x)
	      (not (memq x '(quote if lambda let set!)))))))
      (literal (predicate number?))
      (datum (predicate (lambda (x) #t)))
      (declaration (lst variable expression))
      (procedure-call
	(predicate;; this could have been (lst (plus expression))
	  (lambda (x);; but then a spurious bad keyword is reported
	    (and (pair? x)	    
	      (not (and (symbol? (car x)) (not (variable x))))
	      ((seq (plus expression))
	       x)))))
      (expression
	(report-if-bad 'expression
	  (alt variable literal procedure-call
	    (lst 'quote datum)
	    (lst 'lambda (lst (star variable)) expression)
	    (lst 'if expression expression expression)
	    (lst 'set! variable expression)
	    (lst 'let (lst (star declaration)) expression))))))
> (define run
    (lambda (e)
      (if (exp? e) (eval-exp (expand e) init-env))))
> (run '(let ((a 3)) if (zero? a) 1 2))
Bad expression: if
Bad expression: (let ((...)) if (zero? a) 1 2)
For deeply nested errors, all expressions containing a bad expression are reported.

Define-record and variant-case

The define-record and variant-case syntactic extensions used in the book will not be used in class or our assignments, but they are available if desired by loading record.ss.

chaynes@indiana.edu