[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Scheme RFCs

> In my proposal for the "if-implements" construct I allow the
> feature-identifier to be the name of the SRFI, e.g. SRFI-123, or an
> alias that is specified with the SRFI, such as Unicode-IO.  Thus an
> alias field should be added to the SRFI document format.

Since the SRFI-0 proposal seems relevant to the discussion of the
SRFI process, I am attaching the current draft of the SRFI-0 proposal.


Title: Feature-based conditional expansion construct

Author: Marc Feeley

Feature name: SRFI-0

Feature alias: none attributed yet

Extends: R4RS, R5RS

Status: Pending (Tue Nov 17 10:55:32 EST 1998)


This SRFI describes a mechanism to check at macro-expansion time for
the existence of a particular feature of the Scheme system and to
enable and/or disable a piece of Scheme code based on this.  This
allows a Scheme program to be written in such a way that it adapts its
code to the features provided by the Scheme system.


Most Scheme systems extend the language with some additional features
(such as the ability to manipulate Unicode characters and strings, to
do binary I/O, or to handle asynchronous interrupts).  Such features
may be provided in a variety of ways including new procedures, new
program syntax, and extended behavior of standard procedures and
special-forms.  A particular functionality may exist in several or
even most Scheme systems but its API may be different (use of a
procedure or special-form, name, number of parameters, etc).  To write
code that will run on several Scheme systems, it is useful to have a
common construct to enable or disable sections of code based on the
existence or absence of a feature in the Scheme system being used.
For example, the construct could be use to check if a particular
binary I/O API is available, and if not, load a portable library which
implements that API.

The construct is parameterized by a feature-identifier which denotes
the feature that is being checked.  In order for the semantics of this
construct to be well defined, the feature-identifier must of course
refer to a feature which has a well defined meaning.  There is thus a
need for a registry, independent of this SRFI, to keep track of the
formal specification associated with each valid feature-identifier.
The SRFI registry (currently maintained at www.schemers.org) is used
for this purpose.

Another issue is the binding time of this construct (i.e. the moment
when it operates).  It is important that the binding time be early so
that a compiler can discard the sections of code that are not needed,
and perform better static analyses.  Expressing this construct through
a procedure returning a boolean, such as "(feature-implemented? 'SRFI-5)",
would not achieve this goal, as its binding time is too late
(i.e. program run-time).  A read-time construct, such as CommonLisp's
"#+" read-macro, is very early but would require non-trivial changes
to the reader of existing Scheme systems and the syntax is not
particularly human friendly.  Instead, a macro-expansion-time
construct is used.


The following special-form is added to the Scheme syntax:

  <definition> --> (if-implements <feature-id> <definition> [ <definition> ] )
  <expression> --> (if-implements <feature-id> <expression> [ <expression> ] )
  <feature-id> --> a symbol which is the name or alias of a SRFI

At macro-expansion time the "if-implements" form expands into the
second argument if the Scheme system implements the feature as
specified in the SRFI registry, otherwise the "if-implements" form
expands into the third argument.  Note that the third argument is
optional.  If it is not given it is as though an empty "begin" form
was specified when "if-implements" is used as a definition, otherwise
it is as though a side-effect free expression yielding an unspecified
value was specified.


  (write (if-implements SRFI-0 (* 1 2) (+ 3 4)))  => writes 2

  (if-implements Gambit-C-3.0
    (define (command-line-arguments) (argv))
    (define (command-line-arguments) '()))

The second example assumes that "Gambit-C-3.0" is an alias for the
SRFI associated with the specification of Gambit-C version 3.0 (i.e.
the documentation for that Scheme system).

When writing portable code, the case used for the feature identifier
should match the one in the SRFI registry.  This is to ensure that the
feature identifier will be correctly recognized whether or not the
Scheme system is case-sensitive.  To support case-insensitive Scheme
systems, the feature identifiers in the SRFI registry are guaranteed
to be unique even when ignoring the case.


The Scheme system must maintain a table of the feature-identifiers
that it implements.  At macro-expansion time this table is checked to
decide what the "if-implements" form expands into.  Except for the
table which is obviously system dependent, the "if-implements"
construct could be defined as a macro along these lines (assuming
SRFI-0 and SRFI-5 are the only implemented features):

(define-syntax if-implements
  (syntax-rules (SRFI-0 SRFI-5)
    ((if-implements SRFI-0 yes)    yes)
    ((if-implements SRFI-0 yes no) yes)
    ((if-implements SRFI-5 yes)    yes)
    ((if-implements SRFI-5 yes no) yes)
    ((if-implements other yes)     (begin)) ; note: (begin) not an expression
    ((if-implements other yes no)  no)))

If Scheme defined that "(begin)" was a valid expression that returned
an unspecified value then this definition would be correct.  However,
in Scheme, "(begin)" is not a valid expression.  For example this is
an error in R5RS, if SRFI-9 is not implemented:

(lambda (x)
  (write x)
  (if-implements SRFI-9 (write-char (integer->char 999))) ; expands to (begin)

So to properly implement the "if-implements" construct, the Scheme
system must also know at macro-expansion time if the form is used as a
definition or expression, just like the implementation of the "begin"
construct must know this.