David A. Wheeler

Alan Manuel K. Gloria




Related SRFIs




A curly-infix list introduces a list whose visual presentation is in infix order instead of prefix order. For example, {n > 5}(> n 5), and {a + b + c}(+ a b c). By intent, there is no precedence, but e.g., {x + {y * z}} maps cleanly to (+ x (* y z)). Forms with mixed infix operators and other complications have “nfx” prepended to enable later macro processing, e.g., {4 + 5 * 6}(nfx 4 + 5 * 6). Also, inside a curly-infix list (recursively), expressions of the form f(...) are simply an abbreviation for (f ...).








More examples of c-expressions and their mappings are given below in the specification. See the design rationale for details on why the notation is designed the way it is.



Curly-infix-expressions” (aka “c-expressions”) are s-expressions with an additional supported notation: The curly-infix list. A curly-infix list is syntactically almost identical to a regular list, but it is surrounded by a matched pair of braces instead of by a pair of parentheses, and instead of a sequence of s-expressions it contains a sequence of neoteric-expressions (which add support for formats like f(x)). Once a curly-infix list is read, it is mapped differently than a regular list by a curly-infix reader:

  1. A simple curly-infix list has an odd number of parameters, at least three parameters, and all even parameters are “equal?”. If there is more than one even parameter, and an even parameter contains a cycle, then the equal? comparison must terminate if equal? terminates (otherwise the comparison may terminate). A simple curly-infix list is mapped by the reader into a list with the first even parameter followed by the odd parameters. E.g., {n <= 5}(<= n 5), and {4 * 5 * 6}(* 4 5 6).


curly‑infix‑list → {” <whitespace>* [ <n-expression> [ <whitespace>+ <n-expression> ]* [ <whitespace>+  .  <whitespace>+ <n-expression> ] <whitespace>* ] “}

A “neoteric-expression” (aka “n-expression”) is a curly-infix-expression, with the following additional syntaxes for a datum where e is any datum expression:

  1. e(...)(e ...). E.g., cos(x)(cos x), f(a b)(f a b), exit()(exit), and read(. options)(read . options).
  3. e[...]($bracket-apply$ e ...)

Note that any s-expression is also an n-expression, because n-expressions include c-expressions and c-expressions include s-expressions.

Here are some examples of c-expressions (note that all operators in curly-infix are delimited):

A curly-infix reader is a datum reader that can correctly read and map curly-infix-expressions. A curly-infix reader MUST include the braces “{” and “}” as delimiters.

An implementation of this SRFI MUST accept the marker #!curly-infix followed by a whitespace character. This marker (including the trailing whitespace character) MUST be consumed and considered whitespace. After reading this marker, the reader MUST accept curly-infix expressions in subsequent datums until it reaches an end-of-file or some other conflicting marker (no conflicting marker is specified here).

Implementations of this SRFI SHOULD implement curly-infix expressions in their datum readers by default, even when the marker is not received. Portable applications SHOULD include this marker before using curly-infix expressions, typically near the top of a file. Portable applications SHOULD NOT use this marker as the very first characters of a file (e.g., a newline should precede it), to prevent misinterpretation as an executable script header.

An implementation MUST NOT bind the symbols “nfx” or “$bracket-access$” by default to a procedure, macro, or syntax that cannot be overridden. An implementation SHOULD NOT bind the symbols “nfx” or “$bracket-access$” to a procedure, macro, or syntax in the default environment, with the exception that it may bind them by default to something that produces an error. These two symbols are reserved for use by library writers (in the case of a library-based implementation of this SRFI, these symbols are reserved for use by other libraries) and application writers.

However, an implementation MAY provide one or more libraries that when imported bind the “nfx” and/or “$bracket-access$” symbols (as it is then a library, this case actually falls under the “reserved for use by library writers” clause above). Application writers and other library writers using that implementation are then free to use or not use the implementation’s provided “nfx” and/or “$bracket-access$” as provided by those libraries.

Implementations MAY provide the procedure curly-infix-read as a curly-infix reader. If provided, this procedure SHOULD support an optional port parameter.

Security implication: If the implementation does not check for circularity when doing equality comparisons, and a supplier of malicious data can specify a circularity, the reader could fail to terminate when comparing infix operators. In the worst case this could cause a denial of service. A solution is to check for circularity when comparing operators.

Note that, by definition, this SRFI modifies lexical syntax.

Design Rationale

This SRFI design rationale is unusually long, especially when you compare it to the simplicity of its specification. However, the notation described in this SRFI builds on the lessons learned from the many previous infix mechanisms that have been developed for Scheme and related Lisp-based languages. The authors believe that it is important to document why various decisions were made, in particular, to show why this approach is an improvement over past approaches and more likely to gain wide acceptance. We have separated the design rationale from the overall rationale, as was previously done by SRFI-26, because it is easier to understand the design rationale after reading the specification.

Why not macros? Why modify the reader?


(define-syntax o
  (syntax-rules ()
    ({f o g}
     (lambda args
       (f (apply g args))))
    ({f o g o h o ...}
     {(lambda (x)


A reader notation that maps to a simple and obvious s-expression structure also allows notations such as (map . {as + bs})(map + as bs). For example, in combination with SRFI-26, you can express templated procedures: (cut . {<> < 42})

Why use brace characters for infix?



In contrast, curly braces are in the ASCII character set and are already available for this purpose. They do not have a standardized meaning in any Scheme specification. They are also widely available in many other Lisp-derived languages, such as Common Lisp (as we’d like this notation to be widely useful across Lisps, even beyond Scheme).



Why not use a completely different notation inside the expression?


In contrast, this curly-infix-expression proposal avoids these problems. The lists inside use almost exactly the same syntax inside and outside. For example, in curly-infix, `{,a + ,b} maps cleanly to `(+ ,a ,b), which works as expected with all macros. The main difference is that, in a curly-infix expression, the position of the operator in its surface syntax in a different location (infix) than its actual final location.

Why not autodetect infix?




Why must infix operators be delimited?

Curly-infix requires that the infix operators be delimited (e.g., by spaces). This is consistent with Lisp history and current practice. Currently, in Lisp, operators are always delimited in traditional s-expressions (typically by left parentheses on the left, and by whitespace on the right). It’s impractical to do otherwise today; most Lisps, including Scheme, allow and predefine symbols that include characters (like “-”) that are typically used for infix operators. If infix operators were not delimited, it would be impractical or complicated to refer to standard Scheme identifiers. By requiring delimiters (as is already true for the rest of Scheme), any procedure may be used as an infix operator, not just a fixed list. Many developers put space around infix operators even in languages that don’t require them, so syntactically requiring them is no burden. There are even other existing languages that also require infix operators be delimited, such as SNOBOL4, PLOT, and REBOL. In short, it is difficult to allow infix operators without delimiters, and the visual results are the same as many real-world uses in other languages, so the result appears quite customary to typical software developers.

Why isn’t precedence part of this SRFI?


Yet the complexity of precedence systems is often unnecessary. In practice, we’ve found that simple infix is all that’s needed most of the time in Lisp-based languages. Even in other languages, many developers unnecessarily use grouping symbols with infix operators to make their order clear. An examination of two Scheme programs written using curly-infix (posted 2012-09-14) found that 55/78 (71%) of the top-level c-expressions do not embed an opening brace; this means that precedence is irrelevant for more than two-thirds of these top-level c-expressions. Thus, requiring grouping symbols is less of a hardship than it might appear.




At first David A. Wheeler, who started this project, considered reporting an error if a simple infix expression isn’t provided. However, prepending “nfx” is much more flexible.

Could precedence be added?




  2. Exponentiation/superscript/up-arrow: exp..., **, ^, sup {Unicode: ↑, ⇑}
  4. Addition/subtraction: +, -
  7. Comparison:

Think that is too complicated? Many other variations are possible. A “simple math” ruleset could be devised instead, e.g., perhaps it has a shorter list of built-in operators. The extant code using curly-infix tends to combine these: *, /; +, -; < , <= , >=, >, =, <>, eq...; and; and or; adding exponentiation (e.g., ** and exp...) at a higher precedence level, and implication (e.g., -> and =>) would make sense for a short list. (The <> isn’t in many Scheme specifications, but it is odd to omit it from a precedence list.)


Why is nfx not predefined?

Implementations should not predefine a meaning for nfx, other than possibly to something that always produces an error (e.g., raises an exception).

If anyone wrote code that depended on some local implementation of nfx, then by definition it would become implementation-dependent. Yet the point of the “nfx” macro is to allow application authors the ability to control what to do in that case, not to make them unwittingly dependent on an implementation.

Of course, an implementation could provide a pre-canned macro that could be used as a definition of nfx. But in that case, importing the library would be an explicit act, easily seen in the code, instead of being hidden. That way, it is easy to determine when an implementation-dependent capability is being used.

Why are 0, 1, and 2 parameters special?




Why a marker starting with #! and a letter?






We recommend that #!curly-infix not be the very first characters in a file (e.g., put a newline in front of it). If the file began with #!curly-infix, is made executable, and then execution is attempted, this might confuse some systems into trying to run the program srfi-105.


Why the marker #!curly-infix?


The #!srfi-105 was recommended during discussion of SRFI-105, in part because it makes it clear where more information can be gathered. Also, it suggests that srfi- should be the namespace for SRFIs, a plausible convention.

However, #!curly-infix marker has the advantage of being more obvious about what is being enabled. Modern search engines make it easy to find where information is, using either naming convention. Finally, if this capability were accepted into future Scheme specification, its name would not need to change.

What about the Racket “infix convention”?



What about the Racket infix.plt package?


In this approach, the 3-character sequence “@${“ begins an infix expression, which switches to a completely different language that ends with a matching “}”. The parser.ss source code defines the grammar of this language as follows:

<e> :== <num>
     |  <id>                   variable reference
     |  <e> [ <args> ]         application
     |  { <args> }             list construction
     |  <e> + <e>              addition
     |  <e> - <e>              subtraction
     |  <e> * <e>              multiplication
     |  <e> / <e>              division
     |  <e> ^ <e>              exponentiation
     |  - <e>                  negation
     | ( <e> )                 grouping

<id>   An identifier begins with a letter,

       optionally followed by a period followed by a series of digits.


  <e> := <e>  assignment
  <e> ; <e>   sequence
  (λ ids . expr)     anonymous function
  √<e>         square root (Unicode character)
  ¬<e>         logical not


However, there are many negatives to the infix.plt approach:




What about Gambit’s “Scheme Infix eXtension (SIX)”?





What about Guile 1.4’s “reading infix” module?


The guile 1.4 reading infix module has a more complex grammar requiring a more complex implementation and understanding. Its registration system creates serious problems when trying to use it for larger systems. This infix notation has not been accepted into the version of guile used by most people, so it is not even portable among most guile users. But perhaps the biggest problem is that this notation is fundamentally not homoiconic; it is harder to determine where lists begin and end with it.



Why neoteric-expressions?

Lisp’s standard notation is different from “normal” notation in that the parentheses precede the function name, rather than follow it. Others have commented that it’d be valuable to be able to say name(x) instead of (name x):




Comma-separated parameters



Many other languages do use commas, but they are required because an expression that uses infix order need not be surrounded by any marker. Since an infix expression must be surrounded by {...} in our notation, there is no need for additional commas for parameter separation.


Other comments on neoteric-expressions


The symbol $bracket-apply$ was once bracketaccess, but it turns out that the Kawa Scheme implementation already used $bracket-apply$. The symbol $bracket-apply$ is somewhat more awkward to type directly, but this is actually a good thing; this means it is even more unlikely to be used unintentionally by user code.


The neoteric rules do introduce the risk of someone inserting a space between the function name and the opening character (e.g., an open parenthesis). But whitespace is already significant as a parameter separator; since this is how Scheme already works, this represents no change at all.

Obviously, this is trivial to parse. No power is lost, because this is completely optional; developers can use it when they want to, and they can use traditional s-expression notation if they want to. It’s trivially quoted... if you quote a symbol followed by (, just keep going until its matching ), which is essentially the same rule as before.

Other details


Curly-infix is designed so that it can work on other Lisps as well, which should simplify adoption elsewhere.


Reference implementation

The implementation below is portable, with the exception that Scheme provides no standard mechanism to override {...} in its built-in reader. Thus, implementations will typically have a modified reader that detects “{“, starts reading a list until its matching “}”, and then calls process-curly defined below. Implementations should always do this, but an implementation tht complies with this SRFI must at least activate this behavior when they read the #!curly-infix marker followed by whitespace.


For clarity, this is split into two parts: (1) code that implements the SRFI, and (2) a demo (with support procedures) to show its use. This SRFI is trivial to implement, so most of the code is actually in part 2.

Key code to implement this SRFI

  ; Curly-infix support procedures

      ((null? lyst) #t)
      ((not (pair? lyst)) #f)

  ; Return true if the lyst is in simple infix format
  ; (and thus should be reordered at read time).
  (define (simple-infix-list? lyst)
                             ; this way for performance)

  ; Return alternating parameters in a lyst (1st, 3rd, 5th, etc.)
  (define (alternating-parameters lyst)

  (define (transform-mixed-infix lyst)
     (cons 'nfx lyst))

  (define (process-curly lyst)
       (car lyst))

  ; Key procedures to implement neoteric-expressions

  (define (my-read-delimited-list my-read stop-char port)
      ((c   (peek-char port)))
        ((eqv? c #\;)
          (consume-to-eol port)
          (my-read-delimited-list my-read stop-char port))
        ((my-char-whitespace? c)
          (read-char port)
        ((char=? c stop-char)
          (read-char port)
          (read-char port)
          (read-error "Bad closing character"))
          (let ((datum (my-read port)))
               ((eq? datum '.)
                 (let ((datum2 (my-read port)))
                   (consume-whitespace port)
                     ((eof-object? datum2)
                       (read-char port)
                   (cons datum
                     (my-read-delimited-list my-read stop-char port)))))))))

  ; Implement neoteric-expression's prefixed (), [], and {}.
  ; At this point, we have just finished reading some expression, which
  ; MIGHT be a prefix of some longer expression.  Examine the next
  ; then the expression "prefix" is actually a prefix.
  (define (neoteric-process-tail port prefix)
          ((eof-object? c) prefix)
            (read-char port)
            (neoteric-process-tail port
                (cons prefix (my-read-delimited-list neoteric-read-real #\) port))))
            (read-char port)
            (neoteric-process-tail port
                  (cons '$bracket-apply$
                    (cons prefix
                      (my-read-delimited-list neoteric-read-real #\] port)))))
            (neoteric-process-tail port
                (if (eqv? tail '())
                  (list prefix tail)))))
          (#t prefix))))

  ; To implement neoteric-expressions, modify the reader so
  ; that [] and {} are also delimiters, and make the reader do this:
  ; (let* ((prefix
  ;           read-expression-as-usual
  ;       ))
  ;   (if (eof-object? prefix)
  ;     prefix
  ;     (neoteric-process-tail port prefix)))


Demo code

  ; Demo procedures to implement curly-infix and neoteric readers

  ; This implements an entire reader, as a demonstration, but if you can
  ; update your existing reader you should just update that instead.
  ; The "my-read" is called if it has to recurse.
  (define (underlying-read my-read port)
        ((eof-object? c) c)
        ((char=? c #\;)
          (consume-to-eol port)
          (my-read port))
        ((my-char-whitespace? c)
          (read-char port)
          (my-read port))
        ((char=? c #\( )
          (read-char port)
          (my-read-delimited-list my-read #\) port))
        ((char=? c #\[ )
          (read-char port)
          (my-read-delimited-list my-read #\] port))
        ((char=? c #\{ )
          (read-char port)
            (my-read-delimited-list neoteric-read-real #\} port)))
        ; Handle missing (, [, { :
        ((char=? c #\) )
          (read-char port)
          (read-error "Closing parenthesis without opening")
          (my-read port))
        ((char=? c #\] )
          (read-char port)
          (read-error "Closing bracket without opening")
          (my-read port))
        ((char=? c #\} )
          (read-char port)
          (read-error "Closing brace without opening")
          (my-read port))
          (default-scheme-read port))
        ((char=? c #\')
          (read-char port)
          (list 'quote (my-read port)))
        ((char=? c #\`)
          (read-char port)
          (list 'quasiquote (my-read port)))
        ((char=? c #\,)
          (read-char port)
                (read-char port)
                (list 'unquote-splicing (my-read port)))
                (list 'unquote (my-read port)))))
        ((ismember? c digits) ; Initial digit.
          (read-number port '()))
        ((char=? c #\#) (process-sharp my-read port))
          (read-char port)
            (read-number port (list c))
            (string->symbol (fold-case-maybe port
              (list->string (cons c
          (string->symbol (fold-case-maybe port

  (define (curly-infix-read-real port)
    (underlying-read curly-infix-read-real port))

  (define (curly-infix-read . port)
    (if (null? port)
      (curly-infix-read-real (current-input-port))
      (curly-infix-read-real (car port))))

  ; Here's a real neoteric reader.
  (define (neoteric-read-real port)
    (let* ((prefix (underlying-read neoteric-read-real port)))
      (if (eof-object? prefix)
        (neoteric-process-tail port prefix))))

  (define (neoteric-read . port)
    (if (null? port)

  ; Support procedures

  (define digits '(#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9))
  (define linefeed (integer->char #x000A))        ; #\newline aka \n.
  (define carriage-return (integer->char #x000D)) ; \r.
  (define tab (integer->char #x0009))
  (define line-tab (integer->char #x000b))
  (define form-feed (integer->char #x000c))
  (define line-ending-chars (list linefeed carriage-return))
  (define whitespace-chars
    (list tab linefeed line-tab form-feed carriage-return #\space))

  ; Should we fold case of symbols by default?
  ; #f means case-sensitive (R6RS); #t means case-insensitive (R5RS).
  ; Here we'll set it to be case-sensitive, which is consistent with R6RS
  ; and guile, but NOT with R5RS.  Most people won't notice, I
  ; _like_ case-sensitivity, and the latest spec is case-sensitive,
  ; so let's start with #f (case-sensitive).
  ; This doesn't affect character names; as an extension,
  ; We always accept arbitrary case for them, e.g., #\newline or #\NEWLINE.
  (define foldcase-default #f)

  ; Returns a true value (not necessarily #t) if char ends a line.
  (define (char-line-ending? char) (memq char line-ending-chars))

  ; Returns true if item is member of lyst, else false.
  (define (ismember? item lyst)
     (pair? (member item lyst)))

  ; Create own version, in case underlying implementation omits some.
  (define (my-char-whitespace? c)
    (or (char-whitespace? c) (ismember? c whitespace-chars)))

  ; If fold-case is active on this port, return string "s" in folded case.
  ; Otherwise, just return "s".  This is needed to support our
  ; foldcase-default configuration value when processing symbols.
  ; The "string-foldcase" procedure isn't everywhere,
  ; so we use "string-downcase".
  (define (fold-case-maybe port s)
    (if foldcase-default
      (string-downcase s)

  (define (consume-to-eol port)
    ; Consume every non-eol character in the current line.
    ; End on EOF or end-of-line char.
        ((not (or (eof-object? c)
                  (char-line-ending? c)))
          (read-char port)
          (consume-to-eol port)))))

  (define (consume-whitespace port)
    (let ((char (peek-char port)))
        ((eof-object? char) char)
        ((eqv? char #\;)
          (consume-to-eol port)
          (consume-whitespace port))
        ((my-char-whitespace? char)
          (read-char port)
          (consume-whitespace port)))))

  ; Identifying the list of delimiter characters is harder than you'd think.
  ; This list is based on R6RS section 4.2.1, while adding [] and {},
  ; but removing "#" from the delimiter set.
  ; NOTE: R6RS has "#" has a delimiter.  However, R5RS does not, and
  ; R7RS probably will not - http://trac.sacrideo.us/wg/wiki/WG1Ballot3Results
  ; shows a strong vote AGAINST "#" being a delimiter.
  ; Having the "#" as a delimiter means that you cannot have "#" embedded
  ; in a symbol name, which hurts backwards compatibility, and it also
  ; breaks implementations like Chicken (has many such identifiers) and
  ; Gambit (which uses this as a namespace separator).
  ; Thus, this list does NOT have "#" as a delimiter, contravening R6RS
  ; (but consistent with R5RS, probably R7RS, and several implementations).
  ; Also - R7RS draft 6 has "|" as delimiter, but we currently don't.
  (define neoteric-delimiters
     (append (list #\( #\) #\[ #\] #\{ #\}  ; Add [] {}
                   #\" #\;)                 ; Could add #\# or #\|

  (define (read-until-delim port delims)
    ; Read characters until eof or a character in "delims" is seen.
    ; Do not consume the eof or delimiter.
    ; Returns the list of chars that were read.
         ((eof-object? c) '())
         ((ismember? c delims) '())
         (#t (cons (read-char port) (read-until-delim port delims))))))

  (define (read-error message)
    (display "Error: ")
    (display message)
    (display "\n")

  (define (read-number port starting-lyst)
    (string->number (list->string
      (append starting-lyst

  ; detect #| or |#
  (define (nest-comment port)
    (let ((c (read-char port)))
        ((eof-object? c))
        ((char=? c #\|)
          (let ((c2 (peek-char port)))
            (if (char=? c2 #\#)
                (read-char port)
                (nest-comment port))))
        ((char=? c #\#)
            (if (char=? c2 #\|)
                  (read-char port)
                  (nest-comment port)))
            (nest-comment port)))
          (nest-comment port)))))

  (define (process-sharp my-read port)
    ; We've peeked a # character.  Returns what it represents.
    (read-char port) ; Remove #
      ((eof-object? (peek-char port)) (peek-char port)) ; If eof, return eof.
        ; Not EOF. Read in the next character, and start acting on it.
            ((char-ci=? c #\t)  #t)
            ((char-ci=? c #\f)  #f)
            ((ismember? c '(#\i #\e #\b #\o #\d #\x
                            #\I #\E #\B #\O #\D #\X))
              (read-number port (list #\# (char-downcase c))))
            ((char=? c #\( )  ; Vector.
              (list->vector (my-read-delimited-list my-read #\) port)))
            ((char=? c #\\) (process-char port))
            ; This supports SRFI-30 #|...|#
            ((char=? c #\|) (nest-comment port) (my-read port))
            ; If #!xyz, consume xyz and recurse.
            ; In a real reader, consider handling "#! whitespace" per SRFI-22,
            ; and consider "#!" followed by / or . as a comment until "!#".
            ((char=? c #\!) (my-read port) (my-read port))
            (#t (read-error "Unsupported # extension")))))))

  (define (process-period port)
    ; We've peeked a period character.  Returns what it represents.
    (read-char port) ; Remove .
        ((eof-object? c) '.) ; period eof; return period.
        ((ismember? c digits)
          (read-number port (list #\.)))  ; period digit - it's a number.
          ; At this point, Scheme only requires support for "." or "...".
          ; As an extension we can support them all.
            (fold-case-maybe port
              (list->string (cons #\.
                (read-until-delim port neoteric-delimiters)))))))))

  (define (process-char port)
    ; We've read #\ - returns what it represents.
        (let ((c (read-char port))
              (rest (read-until-delim port neoteric-delimiters)))
            ((null? rest) c) ; only one char after #\ - so that's it!
              (let ((rest-string (list->string (cons c rest))))
                  ; Implement R6RS character names, see R6RS section 4.2.6.
                  ; As an extension, we will ALWAYS accept character names
                  ; of any case, no matter what the case-folding value is.
                  ((string-ci=? rest-string "space") #\space)
                  ((string-ci=? rest-string "newline") #\newline)
                  ((string-ci=? rest-string "tab") tab)
                  ((string-ci=? rest-string "nul") (integer->char #x0000))
                  ((string-ci=? rest-string "alarm") (integer->char #x0007))
                  ((string-ci=? rest-string "backspace") (integer->char #x0008))
                  ((string-ci=? rest-string "linefeed") (integer->char #x000A))
                  ((string-ci=? rest-string "vtab") (integer->char #x000B))
                  ((string-ci=? rest-string "page") (integer->char #x000C))
                  ((string-ci=? rest-string "return") (integer->char #x000D))
                  ((string-ci=? rest-string "esc") (integer->char #x001B))
                  ((string-ci=? rest-string "delete") (integer->char #x007F))
                  ; Additional character names as extensions:
                  ((string-ci=? rest-string "ht") tab)
                  ((string-ci=? rest-string "cr") (integer->char #x000d))
                  ((string-ci=? rest-string "bs") (integer->char #x0008))
                  (#t (read-error "Invalid character name"))))))))))

  ; Record the original read location, in case it's changed later:
  (define default-scheme-read read)

  ; Demo of reader

  ; repeatedly read in curly-infix and write traditional s-expression.
  (define (process-input)
    (let ((result (curly-infix-read)))
        ((not (eof-object? result))
          (write result)
          (display "\n")
          ; (force-output) ; flush, so can interactively control something else





We thank all the participants on the “readable-discuss” and “SRFI-105” mailing lists, including John Cowan, Shiro Kawai, Per Bothner, and many others whose names should be here but aren’t.



Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
restriction, including without limitation the rights to use, copy,
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.