R6RS-compatible Execution
How to Execute R6RS Programs
Larceny's R6RS-compatible mode is entered when Larceny is called in any of the following ways:
larceny -r6rs -fast -program filename
larceny -r6rs -program filename
larceny -r6rs -fast < filename
larceny -r6rs < filename
On Unix platforms, invoking scheme-script will also enter Larceny's R6RS-compatible mode, but the first line of the Scheme script will be ignored (as recommended by the relevant non-normative appendix of the R6RS). Please note that the first line of an R6RS top-level program is not ignored unless it is executed using scheme-script.
Importing Non-standard Libraries
The R6RS does not specify any portable way for programs to use libraries other than the R6RS standard libraries. As extensions to the R6RS, however, Larceny provides four Larceny-specific ways for non-portable R6RS programs to use non-standard libraries:
- Top-level programs and Scheme scripts may import any of the libraries that Larceny supplies for ERR5RS programs.
- Top-level programs and Scheme scripts may import any of the libraries that Larceny's nonstandard autoload-on-import feature can locate in the directories listed by the startup.sch file in Larceny's root directory.
- Top-level programs (but not Scheme scripts) may import any of the libraries that Larceny's nonstandard autoload-on-import feature can locate in the directory specified by a -path option on Larceny's command line.
- Top-level programs and Scheme scripts may import any of the libraries that are defined within the same file as the top-level program or Scheme script, as described below.
We emphasize that all of those mechanisms are non-portable. Although portability was an alleged goal of the R6RS, and libraries are the most prominent new feature of the R6RS, there is no portable way for R6RS programs to import libraries other than those specified by the R6RS documents themselves.
Defining Non-standard Libraries
The R6RS does not specify any portable way for programmers to define their own libraries. As an extension to the R6RS, Larceny allows R6RS programmers to define non-standard libraries subject to the following restrictions:
- All nonstandard libraries must be written in R6RS Scheme as extended by Larceny.
- All nonstandard libraries must be defined in files that follow Larceny's standard naming conventions for ERR5RS/R6RS libraries.
- All files that define nonstandard libraries must reside in Larceny's library search path.
Note that Larceny uses Andre van Tonder's library system, which allows libraries to import arbitrary procedures from Larceny's underlying implementation of R5RS Scheme. Since Larceny's implementation of R5RS Scheme includes a powerful FFI, the above restrictions permit access to code written in many other programming languages and to a vast number of libraries.
Defining Non-standard Libraries Within a Program
As an extension to the R6RS, Larceny allows R6RS top-level programs and Scheme scripts to define non-standard libraries subject to the following restrictions:
- All nonstandard libraries must be written in Scheme, using R6RS syntax.
- All nonstandard libraries must be contained within the same file as the top-level program.
- If a nonstandard library imports other nonstandard libraries, then the imported libraries must be placed before the importing library.
Known Deviations from R6RS Conformance
Larceny's R6RS-compatible mode is almost, but not quite, R6RS-conforming. As of this writing, Larceny's major known deviations from R6RS-conformance are:
- Larceny does not conform to known errors in the R6RS.
- Larceny does not enforce deliberate weaknesses and restrictions whose primary purpose is to justify additional features.
- Larceny may not support all of the additional features whose primary purpose is to work around the deliberate weaknesses and restrictions of the R6RS. Such features, whether supported or not, are classified as deprecated.
- As a consequence of the above, Larceny doesn't always raise a violation exception when the R6RS says it must, and doesn't always create the precise condition object the R6RS says it must create when it raises those exceptions. Larceny raises most of the required exceptions, however, and is always memory-safe on programs that are said to be safe as defined by the R6RS.
- The R6RS specifies letrec* semantics for internal definitions, and also requires a violation exception to be raised if the letrec* restriction is violated. Enforcement of the letrec* restriction is costly in time and/or space, however, and will remain so unless we add a new flow analysis to Twobit. We do not expect users of R6RS-compatible mode to write code that deliberately violates the letrec* restriction, and we expect most users of R6RS-compatible mode would prefer we spend our time on more useful flow analyses. For users who insist upon enforcement of the letrec* restriction, we will eventually provide a compiler switch that enforces the letrec* restriction at some cost in performance.
- Twobit currently rejects programs that contain apparent errors of certain kinds. In many programming situations, we believe it is better to reject such programs at compile time than to let them execute, but we also recognize that there are situations in which the compiler should merely warn the programmer without rejecting the program altogether. We will offer this choice via a compiler switch, but it will take some time for us to modify Twobit so it can generate correct code for all programs, no matter how error-ridden they appear to Twobit.
- Larceny's arithmetic is R5RS-conforming but not R6RS-conforming. This class of bugs will be fixed in future releases.
- The current version of Larceny implements the no-fail file option even when it isn't specified, ignores the no-truncate file option, and usually ignores buffer-mode arguments. (Larceny has implemented no-fail semantics for a long time, and backward compatibility is more important (for now, anyway) than compatibility with other R6RS systems. Larceny may implement the no-truncate file option in future releases. The semantics of buffer-mode arguments is implementation-dependent anyway.)
- If a textual input/output (bidirectional) port has a transcoder, then the transcoder's codec must be Latin-1 and its end-of-line style must be none. (With variable-width encodings or other end-of-line styles, the semantics of input/output ports is not well-defined.)
- All ports support the port-position procedure, even though the R6RS mandates non-support of that procedure by custom ports unless they specify a custom implementation of the procedure. Furthermore custom implementations of the port-position procedure are ignored; Larceny will provide accurate position information (in bytes for binary ports, in characters for textual ports) even if a custom implementation of port-position is supplied. (Larceny's efficient transcoding architecture relies upon a complex invariant that requires Larceny to maintain accurate information about port positions, and binary input/output ports must have accurate implementations of both port-position and set-port-position! so they can correct for lookahead buffering when switching from input to output. Hence all of Larceny's non-custom ports support port-position internally, and do so accurately, as do all custom textual ports and custom binary input/output ports. It is easier and less confusing to extend Larceny's port-position invariant to custom binary input-only and output-only ports than it would be to treat them as exceptional cases.)
- The set-port-position! procedure is currently supported only by bytevector ports, input/output ports, and custom ports. If you need to use set-port-position! on a binary input-only port obtained from a file, read the file's entire contents as a bytevector and obtain your port by passing that bytevector to open-input-bytevector. For a binary output-only port, obtain a bytevector output port using get-output-bytevector and write its contents to the output file when output is complete. For binary input/output ports, use open-input/output-bytevector or open-file-input/output-port.
- The open-file-input/output-port procedure is implemented using the workaround described above, so the file will be opened twice: once for input, when the port is created, and once for output, when the port is closed.
Extensions to R6RS
Larceny offers several extensions to R6RS syntax and semantics:
- As noted above, Larceny allows R6RS programs to define libraries.
- Larceny recognizes the #!fold-case, #!no-fold-case, #!r5rs, #!err5rs, and #!larceny flags, which enable several extensions to the R6RS lexical syntax. See LexicalConversion.
- Larceny extends the domains of several standard procedures. R6RS programs that violate the R6RS specification and rely upon the R6RS exception semantics to handle those violations should use Larceny's R6RS-conforming mode (not yet implemented) instead of Larceny's R6RS-compatible mode.
Alternatives to R6RS
Larceny continues to support an R5RS mode of execution, which interoperates with other standards such as ERR5RS, SRFIs, SLIB, and Snow. Through its FFI, Larceny also provides access to libraries written in many other languages.
Some of the more desirable features of the R6RS are available via ERR5RS, which eliminates some of the less desirable misfeatures of the R6RS.
