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.
> (gensym) #:g0
> (printf "The ~s procedure is very ~a.~%" "printf" "handy") The "printf" procedure is very handy. > (printf "~~dum, ~cdee~%" #\~) ~dum, ~dee
> (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 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.
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.
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.