Title

A more general cond clause

Author

Taylor Campbell

Status

This SRFI is currently in ``draft'' status. To see an explanation of each status that a SRFI can hold, see here. It will remain in draft status until 2005/03/04, or as amended. To provide input on this SRFI, please mail to <srfi-61@srfi.schemers.org>. See instructions here to subscribe to the list. You can access previous messages via the archive of the mailing list.

Abstract

This SRFI proposes an extension to the cond syntax to allow a more general clause, one that allows binding the results of tests as in the => clauses and user-defined meaning of the success & failure of tests.

Rationale

The present set of cond clauses is based on simple boolean testing. It is prohibitively inexpressive in that the condition part of a cond clause that uses => may pass only a single value to the receiver, and it enforces a semantics whereby #f implies failure of the condition. Programmers frequently use different tokens to imply failure, such as in R5RS's I/O readers which return a distinguished 'EOF object' to denote failure, and a successful condition may produce more than one useful value. This simple extension allows any meaning of 'failure' to be assigned on a per-clause basis, and it also allows the condition to return multiple values to be passed to the receiver.

Specification

The <cond clause> production in the formal syntax of Scheme as written by R5RS in section 7.1.3 is extended with a new option:

  <cond clause> --->
      ...
    | (<generator> <guard> => <receiver>)

where <generator>, <guard>, & <receiver> are all <expression>s.

Clauses of this form have the following semantics: <generator> is evaluated. It may return arbitrarily many values. <Guard> is applied to an argument list containing the values in order that <generator> returned. If <guard> returns a true value for that argument list, <receiver> is applied with an equivalent argument list. If <guard> returns a false value, however, the clause is abandoned and the next one is tried.

Examples

This port->char-list procedure accepts an input port and returns a list of all the characters it produces until the end.

  (define (port->char-list port)
    (cond ((read-char port) char?
           => (lambda (c) (cons c (port->char-list port))))
          (else '())))

Consider now a hypothetical table-entry procedure that accepts two arguments, a table (perhaps a hash table) and a key to an entry that may be in the table; it returns two values: a boolean that denotes whether or not an entry with the given key was in the table and, if it was, the value associated with the key. Also, a hypothetical proj0 combinator (projection of argument 0) returns its 0th argument and ignores all others. One might conditionally branch to a certain body of code if the table contains the desired entry like so with the new type of cond clause:

  (cond ...
        ((table-entry <table> <key>) proj0
         => (lambda (present? value)
              ...[VALUE is bound to the value of the entry]...))
        ...)

Implementation

The entirety of a syntax transformer for the new cond syntax is given here. It uses an auxiliary macro, cond/maybe-more, to simplify the construction of if expressions with or without more cond clauses. The code is in the public domain.

(define-syntax cond
  (syntax-rules (=> ELSE)

    ((COND (ELSE else1 else2 ...))
     ;; The (IF #T (BEGIN ...)) wrapper ensures that there may be no
     ;; internal definitions in the body of the clause.  R5RS mandates
     ;; this in text (by referring to each subform of the clauses as
     ;; <expression>) but not in its reference implementation of COND,
     ;; which just expands to (BEGIN ...) with no (IF #T ...) wrapper.
     (IF #T (BEGIN else1 else2 ...)))

    ((COND (test => receiver) more-clause ...)
     (LET ((T test))
       (COND/MAYBE-MORE T
                        (receiver T)
                        more-clause ...)))

    ((COND (generator guard => receiver) more-clause ...)
     (CALL-WITH-VALUES (LAMBDA () generator)
       (LAMBDA T
         (COND/MAYBE-MORE (APPLY guard    T)
                          (APPLY receiver T)
                          more-clause ...))))

    ((COND (test) more-clause ...)
     (LET ((T test))
       (COND/MAYBE-MORE T T more-clause ...)))

    ((COND (test body1 body2 ...) more-clause ...)
     (COND/MAYBE-MORE test
                      (BEGIN body1 body2 ...)
                      more-clause ...))))

(define-syntax cond/maybe-more
  (syntax-rules ()
    ((COND/MAYBE-MORE test consequent)
     (IF test
         consequent))
    ((COND/MAYBE-MORE test consequent clause ...)
     (IF test
         consequent
         (COND clause ...)))))

Copyright

Copyright (C) 2004 Taylor Campbell. All rights reserved.

This document and translations of it may be copied and furnished to others, and derivative works that comment on or otherwise explain it or assist in its implementation may be prepared, copied, published and distributed, in whole or in part, without restriction of any kind, provided that the above copyright notice and this paragraph are included on all such copies and derivative works. However, this document itself may not be modified in any way, such as by removing this copyright notice or references to the Scheme Request for Implementation process or editors, except as needed for the purpose of developing SRFIs in which case the procedures for copyrights defined in the SRFI process must be followed, or as required to translate it into languages other than English.

The limited permissions granted above are perpetual and will not be revoked by the authors or their successors or assigns.

This document and the information contained herein is provided on an "AS IS" basis and THE AUTHORS AND THE SRFI EDITORS DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION HEREIN WILL NOT INFRINGE ON ANY RIGHTS OR ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.


Editor: Mike Sperber