1. Larceny

Larceny implements the Scheme programming language as defined by IEEE Standard 1178-1990, the Revised5 Report, the Revised6 Report, and ERR5RS. Those language standards serve as Larceny's primary documentation.

This manual describes aspects of Larceny that are not described by the IEEE standard, the Revised Reports, or ERR5RS. For the most current version of this manual, please visit Larceny's main web page. That page also links to the Common Larceny User Manual and to the Larceny mailing list.

To report bugs, please send email to the Larceny developers at <larceny@ccs.neu.edu>, or submit a bug ticket using Larceny's Trac system.

2. Installing Larceny

2.1. Varieties of Larceny

There are three main varieties of Larceny.

Native Larceny is the fastest and most convenient variety of Larceny. It compiles directly to native machine code for Intel x86-32 or SPARC microprocessors running Windows, Linux, MacOS X, or Solaris operating systems.

Petit Larceny compiles to C instead of machine code. It runs on most Unix machines, including PowerPC Macintoshes with MacOS X.

Common Larceny compiles to JIT-compiled IL on Microsoft's Common Language Runtime (CLR) or Mono. It provides access to the .NET libraries from Scheme.

2.2. Downloading

The current versions of Larceny are available for download at Larceny's main web page.

Twobit and Larceny are distributed in two forms: as a precompiled binary, or as source code that can be used to reconstruct any of the precompiled binary distributions. Unless you intend to modify Larceny yourself, you do not need to download the source code.

2.3. Installing the programs

If you are installing or running Common Larceny, please consult the Common Larceny User Manual instead of becoming confused by the instructions in this manual.

Unpack the distribution files with an appropriate command such as one of the following:

tar -xzf larceny-X.Y-bin-native-sparc-solaris.tar.gz
tar -xzf larceny-X.Y-bin-native-ia32-macosx.tar.gz
tar -xzf larceny-X.Y-bin-native-ia32-linux86.tar.gz
tar -xzf larceny-X.Y-bin-native-ia32-win32.tar.gz
tar -xzf larceny-X.Y-bin-petit-stdc-macosx.tar.gz
tar -xzf larceny-X.Y-src.tar.gz

This will create a directory called larceny in your current working directory. Assuming you have unpacked a binary distribution, that directory will contain the following files:

larceny.bin         Run-time system
larceny.heap        Heap image with all libraries, FFI, and compiler
twobit.heap         Heap image with some libraries and compiler
larceny             Shell script that runs larceny.heap
twobit              Shell script that runs twobit.heap
scheme-script       Shell script that runs Scheme scripts
compile-stale       Scheme script that compiles ERR5RS/R6RS libraries
startup.sch         Pathnames for the require and autoload features

If you unpacked the source code there will be many other files and directories, but larceny.bin, larceny.heap, and twobit.heap will not be present. You can reconstruct those files from their source code, but that process requires a working version of Larceny or MzScheme; unless you're porting Larceny or Petit Larceny to a brand new target architecture, it's easier to obtain those three files from a binary distribution of Larceny.

The scripts and heap images are discussed in the following section.

You should decide where you want to install Larceny; let's suppose you want to install it in /usr/local/bin and /usr/local/lib/larceny. Copy larceny, twobit, and scheme-script to /usr/local/bin and edit the definition of LARCENY_PATH at the head of each file to point to the correct directory:

LARCENY_PATH=/usr/local/lib/larceny

Then move or copy larceny.bin, larceny.heap, twobit.heap, startup.sch, and the lib directory to /usr/local/lib/larceny.

You should now be able to run Larceny by typing "larceny" at a Unix prompt. On Windows, type "larceny.bat".

2.3.1. Compiling the R6RS standard libraries

Before you can run Larceny in ERR5RS or R6RS modes, you may have to compile the ERR5RS/R6RS runtime and standard libraries, which is accomplished as follows:

    $ ./larceny
    Larceny v0.96 "Fluoridation" (...)
    > (require 'r6rsmode)
    > (larceny:compile-r6rs-runtime)
    > (exit)

This step is definitely required if you are using Petit Larceny or building any variety of Larceny from source code. With the prebuilt native varieties of Larceny, however, this step should not be necessary.

Warning

Compiling the ERR5RS/R6RS runtime as shown above causes all previously compiled ERR5RS/R6RS libraries and top-level programs to become stale. That means those previously compiled files will need to be recompiled or removed.

Tip

If the lib/R6RS directory and its subdirectories are read-only, then the standard libraries will not be touched, modified, or recompiled by accident.

2.3.2. Twobit

Unless you are doing compiler development, you should use the "larceny" script, not the "twobit" script. The difference is that larceny runs the heap image named "larceny.heap" while twobit runs the heap image named "twobit.heap".

Both of these heap images contain the Twobit compiler and the compile-file procedure that is used to compile files of Scheme code, but they differ in other ways.

In "larceny.heap", every expression is compiled before it is evaluated, but the internals of the Twobit compiler are hidden (except for compilation switches).

In "twobit.heap", some of the libraries that are present in "larceny.heap" are missing, and all internals of Twobit are exposed: every top-level name in Twobit is bound in the interaction environment and may be changed interactively, with immediate effect. Since compiler development can be a risky business, "twobit.heap" uses an interpreter to evaluate the Scheme code you load and type — the interpreter is unaffected by changes to Twobit.

If you are using "twobit.heap" for compiler development, you will need the source code as well as a binary distribution of Larceny.

3. Running Larceny

Larceny runs in any of four distinct modes:

R5RS              traditional read/eval/print loop (the default)
ERR5RS            ERR5RS read/eval/print loop
R6RS              batch execution of R6RS top-level programs
Scheme script     batch execution of R6RS Scheme scripts

These modes correspond to the four distinct kinds of Scheme programs that are described by the current de facto standards for Scheme. (IEEE/ANSI Std 1178 is so similar to the R5RS standard that Larceny implements both languages in R5RS mode.)

3.1. R5RS mode

When you start Larceny in R5RS mode (the default), you will be presented with a banner message and the read-eval-print loop's prompt:

    % larceny
    Larceny vX.Y "<version_name>" (MMM DD YYYY HH:MM:SS, ...)
    larceny.heap, built ...

    >

You can enter a Scheme expression at the prompt. After a complete expression has been read, it will be evaluated and its results printed.

Note

In native Larceny, the expression is evaluated by compiling it to native machine code, which is then executed. In Petit Larceny, the expression is evaluated by an interpreter because compiling to C, running the C compiler, and loading the compiled C code would take too long. Interpreted code behaves like compiled code, so most of what this manual says about the compiler is also true of Petit Larceny's interpreter.

By default, Larceny's Twobit compiler makes several assumptions that allow it to generate faster code; for example, the compiler assumes Scheme's standard procedures will not be redefined. To obtain strict conformance to R5RS semantics at the expense of slower code, evaluate the expression

    (compiler-switches 'standard)

For more information on compiler switches, see the section of this user manual devoted to performance.

3.2. ERR5RS mode

To interact with Larceny's ERR5RS read/eval/print loop, specify the -err5rs option on Larceny's command line:

    % larceny -err5rs
    Larceny v0.95 "First Safety" (...)
    ERR5RS mode (no libraries have been imported)

Since no libraries have been imported, the only forms you can evaluate are constant literals, variable references (but no variables have been imported!), procedure calls (but no procedure values are accessible!), library definitions, and import forms. The first thing you'll want to do is to import some libraries, such as:

    > (import (rnrs base)
              (rnrs io simple)
              (err5rs records syntactic))

Once you have imported (rnrs base) or a composite library that includes it, you can evaluate definitions and use all other syntax and variables you have imported.

3.2.1. Automatic loading

As an extension to ERR5RS, Larceny attempts to load libraries automatically when they are first imported. Autoloading makes interactive development and separate compilation much more convenient.

All of Larceny's predefined libraries can be autoloaded.

To enable autoloading of other ERR5RS/R6RS libraries, you can:

3.2.2. Dynamic loading

Larceny automatically loads ERR5RS/R6RS libraries when they are first imported. This is usually the most convenient way to load a library, but autoloading can't be used to load a top-level program. Explicit loading is needed for top-level programs, for libraries that don't reside in Larceny's current-require-path, and for libraries that are defined in files whose names do not follow Larceny's standard naming conventions.

Explicit loading is the only portable way for ERR5RS programs to load a library.

Explicit loading also makes it possible to write portable programs whose source files conform to both the R5RS and ERR5RS. Two different configuration files, one for the R5RS and one for ERR5RS, can perform all of the imports and loads needed to run the program.

For explicit loading of nonstandard libraries, top-level programs, or unadorned R5RS-style code from a file, you must first import a suitable load procedure:

    > (import (err5rs load))

Loading a library does not automatically import it. To use the variables and syntax that are exported by a library, you must import that library explicitly:

    > (load "lib/R6RS/larceny/benchmarking.sls")
    > (import (larceny benchmarking))
    > (time (vector-for-each + (make-vector 1000000 0)))
    Words allocated: 3095752
    Words reclaimed: 0
    Elapsed time...: 111 ms (User: 104 ms; System: 8 ms)
    Elapsed GC time: 4 ms (CPU: 4 in 8 collections.)

In Larceny, you may omit the call to load because the (larceny benchmarking) library will be autoloaded when it is imported. In most other ERR5RS systems, however, you must load all of the nonstandard libraries that will be imported by a top-level program or library before you load that top-level program or library.

You do not have to import those libraries into the ERR5RS top level, however, unless you want to use the variables and syntax exported by those libraries in the expressions and definitions you evaluate at the top level.

3.2.3. Predefined libraries

Larceny predefines several nonstandard libraries in addition to the standard ERR5RS and R6RS libraries, and autoloads them for your convenience. The predefined, autoloadable libraries include:

R6RS standard libraries:

(rnrs base (6))                  ; R6RS chapter 9
(rnrs unicode (6))               ; R6RS library chapter 1
(rnrs bytevectors (6))           ; R6RS library chapter 2
(rnrs lists (6))                 ; R6RS library chapter 3
(rnrs sorting (6))               ; R6RS library chapter 4
(rnrs control (6))               ; R6RS library chapter 5
(rnrs exceptions (6))            ; R6RS library section 7.1
(rnrs conditions (6))            ; R6RS library sections 7.2 and 7.3
(rnrs io ports (6))              ; R6RS library sections 8.1 and 8.2
(rnrs io simple (6))             ; R6RS library sections 8.1 and 8.3
(rnrs files (6))                 ; R6RS library chapter 9
(rnrs programs (6))              ; R6RS library chapter 10
(rnrs arithmetic fixnums (6))    ; R6RS library section 11.2
(rnrs arithmetic flonums (6))    ; R6RS library section 11.3
(rnrs arithmetic bitwise (6))    ; R6RS library section 11.4
(rnrs syntax-case (6))           ; R6RS library chapter 12
(rnrs hashtables (6))            ; R6RS library chapter 13
(rnrs enums)                     ; R6RS library chapter 14
(rnrs (6))                       ; R6RS library chapter 15
(rnrs eval (6))                  ; R6RS library chapter 16
(rnrs mutable-pairs (6))         ; R6RS library chapter 17
(rnrs mutable-strings (6))       ; R6RS library chapter 18
(rnrs r5rs (6))                  ; R6RS library chapter 19

R6RS standard libraries that are autoloadable but deprecated in Larceny:

(rnrs records procedural (6))    ; R6RS library section 6.3 (deprecated)
(rnrs records inspection (6))    ; R6RS library section 6.4 (deprecated)
(rnrs records syntactic (6))     ; R6RS library section 6.2 (deprecated)

Other autoloadable libraries:

(err5rs records procedural)      ; ERR5RS records (procedural API)
(err5rs records inspection)      ; ERR5RS records (inspection API)
(err5rs records syntactic)       ; ERR5RS records (syntactic API)
(err5rs load)                    ; ERR5RS load procedure
(rnrs load)                      ; equivalent to (err5rs load)
(larceny load)                   ; extension of (err5rs load)
(larceny compiler)               ; separate compilation for ERR5RS/R6RS
(larceny benchmarking)           ; timing facilities
(larceny records printer)        ; custom printing of records
(r5rs)                           ; approximates the R5RS top level
(explicit-renaming)              ; macro system with explicit renaming

3.2.4. Library path

Larceny's autoload feature locates ERR5RS/R6RS libraries by performing a depth-first search of the directories that belong to Larceny's current-require-path. Libraries will not be autoloaded unless they are defined in files whose names follow Larceny's standard conventions.

The current-require-path is initialized by the startup.sch file in Larceny's root directory.

Larceny's -path command-line option adds a single directory to the directories in the current-require-path.

3.3. R6RS mode

To execute a top-level R6RS program that is contained within a file named pgm, type:

    larceny -r6rs -program pgm

The -program option can be omitted, in which case Larceny will read the top-level program from standard input:

    larceny -r6rs < pgm

If you omit the -program option and do not redirect standard input, then Larceny will wait patiently for you to type a complete top-level program into standard input, terminating it with an end-of-file.

You probably don't want to do that. Had you wanted to type R6RS code at Larceny, you'd be using ERR5RS mode instead.

3.3.1. Predefined libraries

The R6RS standard does not specify any way for a top-level program to define its own libraries. Portable R6RS programs are therefore limited to importing a subset of the R6RS standard libraries.

As an extension to the R6RS, Larceny allows R6RS top-level programs and Scheme scripts to import any libraries that are predefined in Larceny's ERR5RS mode.

3.3.2. Library path

As another extension to the R6RS, Larceny allows R6RS top-level programs to import any libraries that can be found in the directory specified by the -path option on Larceny's command line using Larceny's standard translation from library names to file names. Note that the -path option cannot be used by Scheme scripts, because command-line options are passed along to the Scheme script without being interpreted by the scheme-script processor.

We emphasize that this extension is non-portable. It is not supported by Ikarus, which is currently the only other system that implements a substantial fraction of the R6RS. (Ikarus may provide a similar feature, but Ikarus's mapping from library names to file names is incompatible with Larceny's mapping.)

3.3.3. Defining libraries

As an extension to the R6RS, Larceny allows a top-level program or Scheme script to define R6RS libraries within the file that contains the top-level program or Scheme script, before the import form that begins the top-level program. These libraries must be arranged so that no library depends upon libraries that come later in the file.

We emphasize that this extension is non-portable. It is not supported by Ikarus.

3.4. Scheme scripts

On most Unix systems (including MacOS X and Linux), Larceny's scheme-script will execute Scheme scripts as described in R6RS non-normative appendix D, with or without the optional script header. To make Scheme scripts executable in their own right, without executing scheme-script directly, add Larceny's root directory to your path as described in doc/HOWTO-INSTALL.

Suppose, for example, that /home/myself/hello is an R6RS Scheme script whose first line is the optional script header (#!/usr/bin/env scheme-script). If you do not have execute permission for this script, or Larceny's root directory is not in your path, you can still run the script from Larceny's root directory as follows:

    % ./scheme-script /home/myself/hello

If you have execute permission for the script, and Larceny's root directory is in your path, you can also run the script as follows:

    % /home/myself/hello

If, in addition, the directory that contains the script is in your path, you can run the script as follows:

    % hello

3.5. R5RS scripting

Suppose hello.sch contains the following R5RS code:

    (display "Hello world!")
    (newline)
    (exit)

You can run hello.sch as a script by executing Larceny as follows:

    % larceny -nobanner -- hello.sch

You can redirect Larceny's standard input, in which case you may want to eliminate the herald announcement and the read/eval/print loop's prompt:

    % larceny -nobanner -- -e "(begin (herald #f) (repl-prompt values))" \
              < hello.sch

For an explanation of why that works, which may suggest other creative uses of Larceny, ask for help:

    % larceny -help

3.6. Errors

In R6RS modes, errors should result in an error message followed by a clean exit from the program, but some errors may enter the debugger; this is a known bug.

If your program encounters an error in an interactive mode (R5RS or ERR5RS), it will enter the debugger; this is believed to be a feature.

Despite its crudity, and to some extent because of it, Larceny's debugger works at least as well with optimized compiled code as with interpreted code.

If you type a question mark at the debugger prompt, the debugger will print a help message. That message is more helpful if you understand the Twobit compiler and Larceny's internal representations and invariants, but this manual is not the place to explain those things.

The debugging context is saved so you can exit the debugger and re-enter it from the main read/eval/print loop's prompt:

    > (debug)

The debugger is pretty much a prototype; you don't need to tell us how bad it is.

3.7. Troubleshooting

3.7.1. Errors when starting Larceny

When attempting to run an R6RS program, you may see a warning about "loading source in favor of stale fasl file", following by a long series of error messages about syntactic keywords used as a variable, ending with the kind of error you'd expect to see when a large R6RS program is fed to a Scheme compiler that was expecting to see R5RS-compatible code. That means the R6RS runtime and standard libraries were not installed correctly, or their source files have been touched or modified since they were last compiled. To fix the problem, recompile the R6RS standard libraries as described in step 4 of doc/HOWTO-BUILD.

The precompiled binary forms of Larceny should run on most machines with the appropriate processor and operating system, but the executable program "larceny.bin" may be incompatible with very old or with very new versions of the processor or operating system. If that appears to be the case, you should see whether a newer version of Larceny fixes the problem. If not, you may be able to use MzScheme v37x to recompile larceny.bin from source. First, though, you should report the problem to us (larceny@ccs.neu.edu). Please report success stories as well.

3.7.2. Errors when compiling the ERR5RS/R6RS runtime

If something goes wrong while compiling the ERR5RS/R6RS runtime, make sure you are running the copy of Larceny you think you are running and have read and write permission for lib/R6RS and all its subdirectories and files. If you get an error message about something being "expanded against a different build of this library", then one or more of the compiled files in lib/R6RS and its subdirectories has gone stale. Removing all .slfasl files from lib/R6RS and its subdirectories will eliminate the stale file(s).

Warning

Don't remove the .sch or .sls files.

3.7.3. Autoloading errors

If Larceny attempts to autoload an imported ERR5RS/R6RS library but cannot find the library, then the library may be defined in a file that doesn't follow Larceny's standard naming conventions. Another possibility is that the -path option was omitted or incorrect.

If an ERR5RS/R6RS library is compiled, then all compiled libraries and top-level programs that depend upon it must also be recompiled. In particular, recompiling the standard R6RS runtime will invalidate all compiled libraries and top-level programs. Larceny's compile-stale script and the compile-stale-libraries procedure of (larceny compiler) make it convenient to recompile all of the libraries and top-level programs within any given directory in an order consistent with their dependencies.

3.7.4. Crashes

Please report all crashes with as much information is possible; a backtrace from a debugger or a core dump is ideal (but please do not mail the core dump without contacting us first). Larceny's run-time system is compiled with full debugging information by default and a debugger like GDB should be able to provide at least some clues.

3.8. Performance

By default, Larceny's Twobit compiler assumes that Scheme's standard procedures will not be redefined, which allows the compiler to generate inline code for many calls to standard procedures. If you want to be able to redefine standard procedures, then you should evaluate the expression

        (integrate-procedures #f)

After this expression has been evaluated, the compiler will generate slower code.

To make the compiler generate faster code, you can promise not to redefine standard procedures and not to redefine any top-level procedure while it is running. To make this promise, evaluate

        (compiler-switches 'fast-safe)

To view the current settings of Twobit's numerous compiler switches, evaluate

        (compiler-switches)

All of Twobit's compiler switches are procedures whose setting can be changed by passing the new value of the switch as an argument.

For more information, evaluate

        (help)
Note

Some of the help information that will be printed may be irrelevant to the heap image you are using.

4. Lexical syntax

Larceny's default lexical syntax extends the lexical syntax required by the R5RS, R6RS, and ERR5RS standards.

The R6RS forbids most lexical extensions, however, so Larceny provides several mechanisms for turning its lexical extensions on and off.

4.1. Flags

By default, Larceny recognizes several Larceny-specific flags of the form permitted by the R6RS. The flag you are most likely to encounter represents one of Larceny's unspecified values:

    #!unspecified

Certain other flags have special meanings to Larceny's read and get-datum procedures. They are described below.

4.2. Case-sensitivity

By default, Larceny is case-sensitive. This global default can be overridden by specifying —foldcase or —nofoldcase on Larceny's command line, or by changing the value of Larceny's case-sensitive? parameter.

The case-sensitivity of a particular textual input port is affected by reading one of the following flags from the port using the read or get-datum procedures:

    #!fold-case
    #!no-fold-case

The #!fold-case flag enables case-folding on data read from the port by the read and get-datum procedures, while the #!no-fold-case flag disables case-folding. The behavior established by one of these flags extends to the next flag read from the port by read or get-datum.

Both #!fold-case and #!no-fold-case evaluate to an unspecified value. To obtain the effect of one of these flags while treating it as a comment, place #; in front of the flag.

4.3. Lexical extensions

When a port is first opened, the Larceny-specific lexical extensions that are accepted on the port are determined by Larceny's lexical parameters.

The following flags change the case-sensitivity and lexical extensions on the specific port from which they are read:

    #!r6rs         ; implies #!no-fold-case, negates other flags
    #!r5rs         ; implies #!fold-case, #!err5rs
    #!err5rs       ; allows Larceny-specific extensions
    #!larceny      ; implies #!no-fold-case, #!err5rs

The #!r6rs flag is a comment, while all of Larceny's other flags evaluate to an unspecified value. To obtain the effect of a flag other than #!r6rs while treating it as a comment, place #; in front of the flag.

Warning

The R6RS requires implementations to treat #!r6rs as a comment; it is the only flag that implementations of the R6RS are required to treat as a comment. Since the #!r6rs flag behaves differently from all other flags, it is deprecated.

4.4. Lexical parameters

When given no argument, these parameters return the current default for some aspect of the lexical syntax that will be accepted on newly created ports. When given an argument, these procedures change the default as specified by the argument.

Procedure case-sensitive?

(case-sensitive? ) => boolean

Determines whether newly created textual ports default to case-sensitive.

Procedure read-larceny-weirdness?

(read-larceny-weirdness? ) => boolean

Determines whether newly created textual ports allow Larceny's usual extensions to R5RS lexical syntax. This parameter also determines whether newly created ports allow # as an insignificant digit; this is required by the R5RS, but disallowed by the R6RS.

Procedure read-traditional-weirdness?

(read-traditional-weirdness? ) => boolean

Determines whether newly created textual ports allow certain lexical extensions that are deprecated in Larceny. These extensions include symbols enclosed by vertical bars and read-time evaluation.

For the current semantics of these parameters, please consult the Larceny developers' web page that describes Larceny's lexical syntax.

5. File naming conventions

5.1. Suffixes

In Larceny, file names generally follow Unix conventions, even on Windows. The following suffixes have special meanings to some components of Larceny.

.sls is the preferred suffix for files that consist of ERR5RS/R6RS-compatible library definitions.

.sch is the preferred suffix for files that contain R5RS source code.

.scm is an alternative suffix for files that contain R5RS source code.

.slfasl is the suffix for files that contain the pre-compiled form of ERR5RS/R6RS-compatible code.

.fasl is the suffix for files that contain the pre-compiled form of R5RS code.

.mal is the preferred suffix for files that contain MacScheme assembly language in symbolic form.

.lap is the suffix for files that contain MacScheme assembly language.

.lop is the suffix for files that contain machine code segments in the form expected by Larceny's heap linker.

.heap is the suffix for files that contain an executable heap image (must be combined with the larceny.bin runtime).

5.2. Directories

Larceny's root directory should contain the following files:

    larceny
    twobit
    scheme-script
    larceny.bin
    larceny.heap
    twobit.heap
    startup.sch

The following subdirectories are also essential for correct operation of some features of some modes in some varieties of Larceny:

    include
    lib
    lib/Base
    lib/Debugger
    lib/Ffi
    lib/MzScheme
    lib/R6RS
    lib/SRFI
    lib/Standard
    lib/TeachPacks

The include subdirectory is used when compiling files with Petit Larceny.

The startup.sch file tells Larceny's require procedure to search some of the lib subdirectories for libraries that are loaded dynamically.

5.3. Resolving references to libraries

The R6RS does not specify any mapping from library names to files or to other locations at which the code for a library might be found. As R6RS non-normative appendix E puts it:

Implementations may take radically different approaches to storing source code for libraries, among them: files in the file system where each file contains an arbitrary number of library forms, files in the file system where each file contains exactly one library form, records in a database, and data structures in memory….Implementations may provide a means for importing libraries….

In other words, implementations are allowed to extend the R6RS with arbitrary mechanisms for resolving references to imported libraries, but R6RS programs that rely on such mechanisms are not portable. In particular, R6RS libraries are not portable.

Larceny provides four distinct Larceny-specific mechanisms that non-portable R6RS programs can use to import or to define libraries:

  1. ERR5RS and R6RS standard libraries may be imported. Their code is located automagically.

  2. Nonstandard libraries, such as (larceny compiler), may be placed in one of the directories that are searched by Larceny's autoload feature, provided those libraries are located in files that follow Larceny's standard naming conventions as described in the next section.

  3. R6RS top-level programs may use Larceny's -path option to specify a directory that contains other libraries the program may import, provided those libraries are located in files that follow Larceny's standard naming conventions as described in the next section.

  4. R6RS top-level programs and Scheme scripts may define their own libraries in the same file that contains the top-level program or Scheme script.

ERR5RS programs may use any of those four mechanisms, and may also use a fifth mechanism: An ERR5RS program can be written as a little configuration program that loads the program's libraries from files before any libraries are imported. This fifth mechanism is portable, but is not available to R6RS programs.

5.4. Mapping library names to files (ERR5RS/R6RS)

Suppose Larceny's -path option is used to specify a certain directory, and the program imports a nonstandard library whose name is of the form (name1 name2lastname). Larceny will search for that library in the following files:

The search starts with the first of those file names, continues with the following file names in order, and ends when a file with one of those names is found. The imported library must be one of the libraries defined within the first file found by this search, since the search is not continued after that first file is found (except as noted in the next paragraph).

If the search ends by finding a file whose name ends with .slfasl, then Larceny checks to see whether there is a file in the same directory with the same root name but ending with .sls instead of .slfasl. If the .sls file has been modified since the .slfasl file was last modified, then a warning is printed and the .sls file is loaded instead of the .slfasl file. Otherwise the .slfasl file is loaded.

Note

The R6RS allows arbitrary mappings from library names to library code. Larceny takes advantage of this by ignoring version numbers when mapping library names to files, and by (virtually) rewriting any version number that may be specified in the definition of a library so it matches any version specification that appears within the import form. Furthermore Larceny allows different versions of the same library to be imported, but Larceny's algorithm for resolving library references ensures that the different versions of a library will be identical except for their version numbers, which have no meaningful semantics. Although Larceny's treatment of versions conforms to the R6RS specification, it should be clear that version numbers serve no purpose in Larceny. Since the R6RS version feature has no usefully portable semantics, it is deprecated.

5.5. Mapping library names to files (R5RS)

In R5RS mode, Larceny's -path option may be used to specify a directory to be searched by the require procedure, which takes a single symbol libname as its argument. The require procedure will search for the following files in every directory that is part of the current require path, starting with the directory specified by the -path option:

These files are expected to contain R5RS code, not library definitions. Otherwise the search proceeds much the same as when searching for an ERR5RS/R6RS library.

Note

The require path is specified by startup.sch in Larceny's root directory, but may be changed dynamically using the current-require-path parameter. Changing the require path is not recommended, however, because Larceny relies on the require path for dynamic loading of libraries used by several important features of Larceny, notably ERR5RS and R6RS modes.

Procedure require

(require libname)

libname must be a symbol that names an R5RS-compatible library within the current require path.

If the library has not already been loaded, then it is located and loaded. If the library is found and loaded successfully, then require returns true; otherwise an error is signalled.

If the library has already been loaded, then require returns false without loading the library a second time.

Procedure current-require-path

(current-require-path ) => stringlist

The optional argument is a list of directory names (without slashes at the end) that should be searched by require and (in ERR5RS/R6RS modes) by Larceny's autoload feature. Returns the list of directory names that will be searched.

6. Compiling files and libraries

This chapter explains how you can use Larceny to compile Scheme source code to native machine code.

The native varieties of Larceny have a just-in-time compiler that compiles to native code automatically whenever you evaluate an expression, load a source file, or import a source library. Even so, files will load faster if they are compiled ahead of time.

Petit Larceny does not have a just-in-time compiler, so compiling ahead of time is the only way to enjoy the speed of native machine code in Petit Larceny.

Common Larceny uses an interpreter for expressions that are typed at the interactive read/eval/print loop, but files will be compiled as they are loaded if you specify Larceny.fasl on the command line. For more information on compiling files in Common Larceny, please consult the Common Larceny User Manual.

The main disadvantage of compiling files and libraries is that compiled code goes stale when its original source code is changed or when a library on which the compiled code depends is changed or recompiled. Stale compiled code can be dangerously inconsistent with libraries on which it depends, so Larceny checks for staleness and refuses to execute a stale library or program.

6.1. Compiling ERR5RS/R6RS libraries

On Unix machines, the most convenient way to compile a group of ERR5RS/R6RS libraries and top-level programs is to use the compile-stale script in Larceny's root directory. If Larceny's root directory is in your execution path, then there are just two steps:

  1. Use cd to change to the directory that contains the ERR5RS/R6RS files you want to compile. (Files that lie within subdirectories of that directory will be compiled also.)

  2. Run the compile-stale script.

For example:

    % cd lib/R6RS
    % compile-stale

On non-Unix machines, you can accomplish the same thing using Larceny's ERR5RS mode and the (larceny compiler) library:

    % pushd lib\R6RS
    % ..\..\larceny.bat -err5rs
    Larceny v0.96 "Fluoridation"
    ERR5RS mode (no libraries have been imported)

    > (import (larceny compiler))

    > (compile-stale-libraries)

To compile individual files, use the compile-file or compile-library procedures that are exported by (larceny compiler).

6.2. Compiling R5RS source files

Procedure compile-file

(compile-file sourcefile)

Compiles sourcefile, which must be a string naming a file that contains R5RS source code. If faslfile is supplied as a second argument, then it must be a string naming the file that will contain the compiled code; otherwise the name of the compiled file is obtained from sourcefile by replacing the ".sch" or ".scm" suffix with ".fasl".

For ERR5RS/R6RS libraries and top-level programs, see above.

7. ERR5RS standard libraries

ERR5RS is still being developed, so the specifications described below are subject to change as its standard libraries are revised.

7.1. Load

This section describes the (err5rs load) library.

Procedure load

(load filename)

Loads ERR5RS code from filename, evaluating each form as though it had been entered at the interactive read/eval/print loop.

Warning

The load procedure should be used only at an interactive top level and in files that will be loaded into an interactive top level. Calls to the load procedure have no effect at compile time, and should not appear in files that will be compiled separately; use the library and import syntaxes instead.

7.2. Records

When a procedure is said to be equivalent to an R6RS procedure, the equivalence holds only when all arguments have the properties required of them by the R6RS specification. ERR5RS does not mandate R6RS exception semantics for programs that violate the specification.

7.2.1. Procedural layer

This section describes the (err5rs records procedural) library.

Procedure make-rtd

(make-rtd name fieldspecss)

(make-rtd name fieldspecss parent-rtd)

(make-rtd name fieldspecss parent-rtd option …)

name is a symbol, which matters only to the rtd-name procedure of the inspection layer. fieldspecs is a vector of field specifiers, where each field specifier is one of

The optional parent is an rtd or #f. It is an error for any of the symbols in fieldspecs to name more than one of the fields specified by fieldspecs, but the field names in fieldspecs may shadow field names in the parent rtd.

make-rtd returns an R6RS-compatible record-type descriptor.

Larceny allows the following optional arguments to follow the optional parent-rtd argument:

These Larceny-specific options may be used in any combination, giving Larceny's ERR5RS records the same expressive power as R6RS records, with which they are fully interoperable.

Procedure rtd?

(rtd? obj)

This predicate returns true if and only if its argument is a record-type descriptor. rtd? is equivalent to the record-type-descriptor? procedure of the R6RS.

Procedure rtd-constructor

(rtd-constructor rtd)

(rtd-constructor rtd fieldspecs)

rtd is a record-type descriptor, and fieldspecs is an optional vector of symbols.

If no fieldspecs argument is supplied, then rtd-constructor returns a procedure that expects one argument for each field of the record-type described by rtd and returns an instance of that record-type with its fields initialized to the corresponding arguments. Arguments that correspond to the fields of the record-type's parent (if any) come first.

If fieldspecs is supplied, then rtd-constructor returns a procedure that expects one argument for each element of fieldspecs and returns an instance of the record-type described by rtd with the named fields initialized to the corresponding arguments.

It is an error if some symbol occurs more than once in fieldspecs. Fields of a derived record-type shadow fields of the same name in its parent; the fieldspecs argument cannot be used to initialize a shadowed field.

Procedure rtd-predicate

(rtd-predicate rtd)

Equivalent to the record-predicate procedure of the R6RS.

Procedure rtd-accessor

(rtd-accessor rtd field)

field is a symbol that names a field of the record-type described by the record-type descriptor rtd. Returns a unary procedure that accepts instances of rtd (or any record-type that inherits from rtd) and returns the current value of the named field.

Fields in derived record-types shadow fields of the same name in a parent record-type.

Procedure rtd-mutator

(rtd-mutator rtd field)

field is a symbol that names a field of the record-type described by the record-type descriptor rtd. Returns a binary procedure that accepts instances of rtd (or any record-type that inherits from rtd) and a new value to be stored into the named field, performs that side effect, and returns an unspecified value.

Fields in derived record-types shadow fields of the same name in a parent record-type.

7.2.2. Inspection layer

This section describes the (err5rs records inspection) library.

Procedure record?

(record? obj)

Equivalent to its R6RS namesake.

Procedure record-rtd

(record-rtd record)

Equivalent to its R6RS namesake.

Procedure rtd-name

(rtd-name rtd)

Equivalent to the record-type-name procedure of the R6RS.

Procedure rtd-parent

(rtd-parent rtd)

Equivalent to the record-type-parent procedure of the R6RS.

Procedure rtd-field-names

(rtd-field-names rtd)

Equivalent to the record-type-field-names procedure of the R6RS. (That is, it returns a vector of the symbols that name the fields of the record-type represented by rtd, excluding the fields of parent record-types.)

Procedure rtd-all-field-names

(rtd-all-field-names rtd)

Returns a vector of the symbols that name the fields of the record-type represented by rtd, including the fields of its parent record-types, if any, with the fields of parent record-types coming before the fields of its children, with each subsequence in the same order as in the vectors that would be returned by calling rtd-field-names on rtd and on all its ancestral record-type descriptors.

Procedure rtd-field-mutable?

(rtd-field-mutable? rtd field)

rtd is a record-type descriptor, and field is a symbol naming a field of the record-type described by rtd. Returns #t if the named field is mutable; otherwise returns #f.

7.2.3. Syntactic layer

This section describes the (err5rs records syntactic) library.

The syntactic layer consists of SRFI 9 extended with single inheritance and (optional) implicit naming.

All ERR5RS record-type definitions are generative (unless Larceny's optional uid feature is used), but ERR5RS drops the SRFI 9 restriction to top level, mainly because the R6RS allows generative definitions wherever a definition may appear.

The syntax of an ERR5RS record-type definition is

    <definition>
      -> <record type definition>           ; addition to 7.1.6 in R5RS

    <record type definition>
      -> (define-record-type <type spec>
           <constructor spec>
           <predicate spec>
           <field spec> ...)

    <type spec>  -> <type name>
                 -> (<type name> <parent>)

    <constructor spec>
                 -> #f
                 -> #t
                 -> <constructor name>
                 -> (<constructor name> <field name> ...)

    <predicate spec>
                 -> #f
                 -> #t
                 -> <predicate name>

    <field spec> -> <field name>
                 -> (<field name>)
                 -> (<field name> <accessor name>)
                 -> (<field name> <accessor name> <mutator name>)

    <parent>           -> <expression>

    <type name>        -> <identifier>
    <constructor name> -> <identifier>
    <predicate name>   -> <identifier>
    <accessor name>    -> <identifier>
    <mutator name>     -> <identifier>
    <field name>       -> <identifier>

The semantics of a record type definition is the same as in SRFI 9: the record type definition macro-expands into a cluster of definitions that

An ERR5RS record type definition extends SRFI 9 with the following additional options:

7.2.4. Record identity

Two ERR5RS records with fields are eqv? if and only if they were created by the same (dynamic) call to some record constructor. Two ERR5RS records are eq? if and only if they are eqv?.

Apart from the usual constraint that equivalence according to eqv? implies equivalence according to equal?, the behavior of equal? on ERR5RS records is unspecified. (This is compatible with the R6RS.)

A define-record-type form macro-expands into code that calls make-rtd each time the expanded record-type definition is executed. Two ERR5RS record-type descriptors are eqv? if and only if they were created by the same (dynamic) call to make-rtd.

8. R6RS standard libraries

This chapter explains which features of the R6RS standard libraries are available in each of Larceny's major modes of execution.

Larceny was the first substantially complete implementation of the R6RS. Any features that are missing from R6RS modes are missing because of bugs or because the features are deprecated in Larceny. The most up-to-date listing of Larceny's known deviations from the R6RS standard can be found on the web page that describes the current status of Larceny's R6RS-compatible mode.

Larceny is R6RS-compatible but not R6RS-conforming. When Larceny is said to support a feature of the R6RS, that means the feature is present and will behave as specified by the R6RS so long as no exception is raised. Larceny does not always raise the specific conditions specified by the R6RS, and does not perform all of the checking for portability problems that is mandated by the R6RS. These deviations do not affect the execution of production code, and do not compromise Larceny's traditional safety.

8.1. Base library

ERR5RS and R6RS modes support all procedures and syntaxes exported by the (rnrs base) library.

Larceny's R5RS mode does not support library, import, or identifier-syntax.

The following deviations from R6RS semantics are known bugs that should be fixed in a future release.

In Larceny v0.96, the imaginary part of an inexact real is inexact. Some of the algebraic and transcendental functions do not yet behave as specified by the R6RS, especially with regard to branch cuts.

The number->string and string->number procedures do not yet support mantissa widths.

The semantics of quasiquote, let-syntax, and letrec-syntax differ between the R5RS and the R6RS. Larceny's R5RS mode still supports the R5RS semantics.

8.2. Unicode

All of Larceny's modes support all features of the (rnrs unicode) library.

Larceny v0.96 conforms to The Unicode Standard, Version 5.0.

8.3. Bytevectors

ERR5RS and R6RS modes support all procedures and syntaxes exported by (rnrs bytevectors), but the endianness syntax is deprecated because it is redundant with quote. Larceny's R5RS mode does not support endianness.

In Larceny, any symbol names a supported endianness. The symbols big and little have their expected meanings. All other symbols mean (native-endianness) with respect to integer operations, but mean the opposite of (native-endianness) with respect to IEEE-754 operations. For string operations, the endianness must be the symbol big or the symbol little. All of these extensions are permitted by the R6RS standard.

Larceny's utf16->string and utf32->string accept one, two, or three arguments. The R6RS specification of these procedures does not allow them to accept a single argument, but that is believed to be an error in the R6RS.

8.4. Lists

All of Larceny's modes support all features of the (rnrs lists) library.

8.5. Sorting

All of Larceny's modes support all features of the (rnrs sorting) library.

8.6. Control

All of Larceny's modes support all features of the (rnrs control) library.

8.7. Records

ERR5RS and R6RS modes support all procedures and syntaxes exported by (rnrs records procedural), (rnrs records inspection), and (rnrs records syntactic).

Those libraries are deprecated, however; the make-record-constructor-descriptor procedure does not simplify unusually complex cases enough to justify the complexity it adds to typical cases, and the entire syntactic layer is gratuitously incompatible with the procedural layer.

Larceny's R5RS mode supports all features of the deprecated (rnrs records procedural) and (rnrs records inspection) libraries. R5RS mode does not support (rnrs records syntactic).

All of Larceny's modes support all features of the (err5rs records procedural) and (err5rs records inspection) libraries. ERR5RS and R6RS modes also support the (err5rs records syntactic) library.

The record definition syntax of SRFI 9 is a proper subset of the syntax provided by the (err5rs records syntactic) library. In R5RS mode, SRFI 9 can be loaded dynamically using the require procedure:

    > (require 'srfi-9)

We recommend the ERR5RS and/or SRFI 9 libraries be used instead of the corresponding R6RS libraries.

Warning

The R6RS spouts some tendentious nonsense about procedural records being slower than syntactic records, but this is not true of Larceny's records, and is unlikely to be true of other implementations either.

Warning

Larceny continues to support its old-style records, which are almost but not quite compatible with ERR5RS and R6RS records. This can be confusing, since some of Larceny's procedures have the same names as R6RS procedures. That has made it necessary to overload those procedures to work with both old-style and R6RS records. We apologize for the mess.

8.8. Exceptions and conditions

All of Larceny's modes support all features of the (rnrs exceptions) and (rnrs conditions) libraries.

8.9. Input and output

ERR5RS and R6RS modes support all names exported by the (rnrs io ports), (rnrs io simple), and (rnrs files) libraries.

The buffer-mode, eol-style, and error-handling-mode syntaxes are deprecated because they are redundant with quote. These deprecated syntaxes may be provided in the form of procedures rather than syntax, but this deviation from R6RS semantics cannot be detected by portable R6RS programs.

Larceny's R5RS mode supports all non-deprecated features of those libraries.

Larceny supports four distinct buffer modes: none, line, datum, and block. The R6RS requires the buffer-mode syntax to raise an exception for the datum buffer mode, which is the buffer mode Larceny uses for interactive output ports.

In Larceny, any symbol names a supported end-of-line style. All end-of-line and error-handling-mode symbols whose meanings are not described by the R6RS have locale-dependent meanings, which is an extension permitted by the R6RS standard.

Although Larceny supports the UTF-16 codec, it is not really useful on Windows machines (where it should be most useful) because Larceny's low-level file system mimics a byte-oriented Unix file system even on Windows. This problem should be addressed in some future version of Larceny.

The most up-to-date list of known deviations from R6RS io semantics can be found on the web page that describes the current status of Larceny's R6RS-compatible mode.

8.10. Programs

ERR5RS and R6RS modes support the (rnrs programs) library.

Larceny's R5RS mode provides the exit procedure but not the command-line procedure of that library. Larceny's traditional command-line-arguments procedure can be used to implement an approximation to command-line. For a definition, see lib/R6RS/rnrs/programs.sls.

8.11. Arithmetic

All of Larceny's modes support all features of the (rnrs arithmetic fixnums), (rnrs arithmetic flonums), and (rnrs arithmetic bitwise) libraries.

Note

R6RS fixnum and flonum operations may be slower than the corresponding generic operations, since the fixnum and flonum operations are required to check their arguments and may also have to check their results. Isolated operations in small micro-benchmarks are likely to be slower than groups of similar operations in larger programs, because the Twobit compiler removes redundant checks and propagates type information.

8.12. Syntax-case

ERR5RS and R6RS modes support the (rnrs syntax-case) library. Larceny's R5RS mode does not.

8.13. Hashtables

All of Larceny's modes support all features of the (rnrs hashtables) library.

Note

Larceny's traditional make-hashtable procedure has been renamed to make-oldstyle-hashtable. In Larceny v0.96, make-hashtable prints a warning when it is called. To avoid that warning, call make-r6rs-hashtable instead.

8.14. Enumeration sets

ERR5RS and R6RS modes support the (rnrs enums) library. Larceny's R5RS mode provides all of the procedures exported by (rnrs enums) but does not provide the define-enumeration syntax.

8.15. Eval

ERR5RS and R6RS modes are believed to support the (rnrs eval) library, but the environment procedure has not been tested. Larceny's R5RS mode provides an R5RS-compatible eval procedure, not an R6RS-compatible eval procedure, and does not provide the environment procedure.

8.16. Mutable pairs and strings

All of Larceny's modes support all features of the (rnrs mutable-pairs) and (rnrs mutable-strings) libraries.

8.17. R5RS

All of Larceny's modes support all features of the (rnrs r5rs) library.

9. Larceny's ERR5RS/R6RS libraries

Larceny provides libraries for loading and compiling ERR5RS/R6RS libraries and for timing benchmarks. Future versions of Larceny will offer more ERR5RS/R6RS libraries.

9.1. Load

The (larceny load) library exports both the load procedure of (err5rs load) and Larceny's require procedure.

In Larceny's ERR5RS mode, the load and require procedures can load R5RS libraries and programs as well as ERR5RS/R6RS libraries.

Warning

These procedures should be used only at an interactive top level and in files that will be loaded into an interactive top level. Calls to these procedures have no effect at compile time, and should not appear in files that will be compiled separately; use the library and import syntaxes instead.

9.2. Compiler

The (larceny compiler) library exports the load and require procedures of (larceny load), the current-require-path procedure, and the compile-file, compile-library, and compile-stale-libraries procedures described below.

These procedures can be used to compile ERR5RS/R6RS libraries and top-level programs before they are imported or executed. This is especially important for Petit Larceny, which would otherwise use an interpreter. For native Larceny, whose just-in-time compiler generates native machine code as source libraries and programs are loaded, imported, or executed, the main advantage of separate compilation is that compiled libraries and programs will load much faster than source libraries and programs.

The main disadvantage of separate compilation is that compiled libraries and programs go stale when their source code is changed or when a library on which they depend is changed or recompiled. Stale libraries and programs can be dangerously inconsistent with libraries on which they depend, so Larceny checks for staleness and refuses to execute a stale library or program. The compile-stale-libraries procedure provides a convenient way to recompile stale libraries and programs.

(compile-file sourcefile)

Compiles sourcefile, which must be a string naming a file that contains source code for one or more ERR5RS/R6RS libraries or a top-level program. If slfaslfile is supplied as a second argument, then it must be a string naming the file that will contain the compiled code; otherwise the name of the compiled file is obtained from sourcefile by replacing the ".sls" suffix with ".slfasl".

Procedure compile-library

(compile-library sourcefile)

Compiles sourcefile, which must be a string naming a file that contains source code for one or more ERR5RS/R6RS libraries. Apart from its unwillingness to compile top-level programs, compile-library behaves the same as compile-file above.

Procedure compile-stale-libraries

(compile-stale-libraries )

If no argument is supplied, then all ".sls" files that lie within the current directory or a subdirectory are recompiled.

If changedfile is supplied, then it must be a string giving the absolute pathname of a file. (In typical usage, changedfile is a source file that has been modified, making it necessary to recompile all files that depend upon it.) Compiles all ERR5RS/R6RS library files that lie within the same directory as changedfile or a subdirectory, and have not yet been compiled or whose compiled files are older than changedfile.

Note

In future versions of Larceny, compile-stale-libraries might compile only the source files that depend upon changedfile.

9.3. Benchmarking

The (larceny benchmarking) library exports the time syntax and run-benchmark procedure described below.

Syntax time

(time expression)

Evaluates expression and returns its result after printing approximations to the storage allocated and time taken during evaluation of expression.

    > (time (fib 30))
    Words allocated: 0
    Words reclaimed: 0
    Elapsed time...: 49 ms (User: 48 ms; System: 0 ms)
    Elapsed GC time: 0 ms (CPU: 0 in 0 collections.)
    832040

(run-benchmark name iterations thunk predicate)

Given the name of a benchmark, the number of iterations to be performed, a zero-argument procedure thunk that runs the benchmark, and a unary predicate that checks the result of thunk, prints approximations to the storage allocated and time taken by iterations calls to thunk.

    > (run-benchmark "fib30"
                     100
                     (lambda () (fib 30))
                     (lambda (x) (= x 832040)))

    --------------------------------------------------------
    fib30
    Words allocated: 0
    Words reclaimed: 0
    Elapsed time...: 4828 ms (User: 4824 ms; System: 4 ms)
    Elapsed GC time: 0 ms (CPU: 0 in 0 collections.)

9.4. Records printer

The (larceny records printer) library exports the two procedures described below. These procedures can be used to override Larceny's usual printing of records and opaque types that were defined using the records libraries.

Procedure rtd-printer

(rtd-printer rtd) => maybe-procedure

Given a record type descriptor, returns its custom print procedure, or returns false if the rtd has no custom print procedure.

Procedure rtd-printer-set!

(rtd-printer-set! rtd printer)

Given a record type descriptor rtd and a printer for instances of that rtd, installs printer as a custom print procedure for rtd. The printer should be a procedure that, given an instance of the rtd and a textual output port, writes a representation of the instance to the port.

10. Larceny's R5RS libraries

The procedures described in this chapter are nonstandard. Some are deprecated after being rendered obsolete by ERR5RS or R6RS standard libraries. Others still provide useful capabilities that the standard libraries don't.

10.1. Strings

Larceny provides Unicode strings with R6RS semantics.

The string-downcase and string-upcase procedures perform Unicode-compatible case folding, which can result in a string whose length is different from that of the original.

Larceny may still provide string-downcase! and string-upcase! procedures, but they are deprecated.

10.2. Bytevectors

A bytevector is a data structure that stores bytes — exact 8-bit unsigned integers. Bytevectors are useful in constructing system interfaces and other low-level programming. In Larceny, many bytevector-like structures — bignums, for example — are implemented in terms of a lower-level bytevector-like data type. The operations on generic bytevector-like structures are particularly fast but useful largely in code that manipulates Larceny's data representations.

The (rnrs bytevectors) library now provides a large set of procedures that, in Larceny, are defined using the procedures described below.

Integrable procedure make-bytevector

(make-bytevector length) => bytevector

(make-bytevector length fill) => bytevector

Returns a bytevector of the desired length. If no second argument is given, then the bytevector has not been initialized and most likely contains garbage.

Operations on bytevector structures

(bytevector? obj) => boolean

(bytevector-length bytevector) => integer

(bytevector-ref bytevector offset) => byte

(bytevector-set! bytevector offset byte) => unspecified

(bytevector-equal? bytevector1 bytevector2) => boolean

(bytevector-fill! bytevector byte) => unspecified

(bytevector-copy bytevector) => bytevector

These procedures do what you expect. All are integrable, except bytevector-equal? and bytevector-copy. The bytevector-equal? name is deprecated, since the R6RS calls it bytevector=?.

Operations on bytevector-like structures

(bytevector-like? obj) => boolean

(bytevector-like-length bytevector) => integer

(bytevector-like-ref bytevector offset) => byte

(bytevector-like-set! bytevector offset byte) => unspecified

(bytevector-like-equal? bytevector1 bytevector2) => boolean

(bytevector-like-copy bytevector) => bytevector

A bytevector-like structure is a low-level representation for indexed arrays of uninterpreted bytes. Bytevector-like structures are used to represent types such as bignums and flonums.

There is no way to construct a "generic" bytevector-like structure; use the constructors for specific bytevector-like types.

The bytevector-like operations operate on all bytevector-like structures. All are integrable, except bytevector-like-equal? and bytevector-like-copy. All are deprecated because they violate abstraction barriers and make your code representation-independent; they are useful mainly to Larceny developers, who might otherwise be tempted to write some low-level operations in C or assembly language.

10.3. Vectors

Procedure vector-copy

(vector-copy vector) => vector

Returns a shallow copy of its argument.

Operations on vector-like structures

(vector-like? object) => boolean

(vector-like-length vector-like) => fixnum

(vector-like-ref vector-like k) => object

(vector-like-set! vector-like k object) => unspecified

A vector-like structure is a low-level representation for indexed arrays of Scheme objects. Vector-like structures are used to represent types such as vectors, records, symbols, and ports.

There is no way to construct a "generic" vector-like structure; use the constructors for specific data types.

The vector-like operations operate on all vector-like structures. All are integrable. All are deprecated because they violate abstraction barriers and make your code representation-independent; they are useful mainly to Larceny developers, who might otherwise be tempted to write some low-level operations in C or assembly language.

10.4. Procedures

Operations on procedures

(make-procedure length) => procedure

(procedure-length procedure) => fixnum

(procedure-ref procedure offset) => object

(procedure-set! procedure offset object) => unspecified

These procedures operate on the representations of procedures and allow user programs to construct, inspect, and alter procedures.

Procedure procedure-copy

(procedure-copy procedure) => procedure

Returns a shallow copy of the procedure.

The procedures above are deprecated because they violate abstraction barriers and make your code representation-independent; they are useful mainly to Larceny developers, who might otherwise be tempted to write some low-level operations in C or assembly language.

The rest of this section describes some procedures that reach through abstraction barriers in a more controlled way to extract heuristic information from procedures for debugging purposes.

Note

The following text is copied from a straw proposal authored by Will Clinger and sent to rrr-authors on 09 May 1996. The text has been edited lightly. See the end for notes about the Larceny implementation.

The procedures that extract heuristic information from procedures are permitted to return any result whatsoever. If the type of a result is not among those listed below, then the result represents an implementation-dependent extension to this interface, which may safely be interpreted as though no information were available from the procedure. Otherwise the result is to be interpreted as described below.

Procedure procedure-arity

(procedure-arity proc)

Returns information about the arity of proc. If the result is #f, then no information is available. If the result is an exact non-negative integer k, then proc requires exactly k arguments. If the result is an inexact non-negative integer n, then proc requires n or more arguments. If the result is a pair, then it is a list of non-negative integers, each of which indicates a number of arguments that will be accepted by proc; the list is not necessarily exhaustive.

Procedure procedure-documentation-string

(procedure-documentation-string proc)

Returns general information about proc. If the result is #f, then no information is available. If the result is a string, then it is to be interpreted as a "documentation string" (see Common Lisp).

Procedure procedure-name

(procedure-name proc)

Returns information about the name of proc. If the result is #f, then no information is available. If the result is a symbol or string, then it represents a name. If the result is a pair, then it is a list of symbols and/or strings representing a path of names; the first element represents an outer name and the last element represents an inner name.

Procedure procedure-source-file

(procedure-source-file proc)

Returns information about the name of a file that contains the source code for proc. If the result is #f, then no information is available. If the result is a string, then the string is the name of a file.

Procedure procedure-source-position

(procedure-source-position proc)

Returns information about the position of the source code for proc whithin the source file specified by procedure-source-file. If the result is #f, then no information is available. If the result is an exact integer k, then k characters precede the opening parenthesis of the source code for proc within that source file.

Procedure procedure-expression

(procedure-expression proc)

Returns information about the source code for proc. If the result is #f, then no information is available. If the result is a pair, then it is a lambda expression in the traditional representation of a list.

Procedure procedure-environment

(procedure-environment proc)

Returns information about the environment of proc. If the result is #f, then no information is available. In any case the result may be passed to any of the environment inquiry functions.

Notes on the Larceny implementation

Twobit does not yet produce data for all of these functions, so some of them always return #f.

10.5. Pairs and Lists

The (rnrs lists) library now provides a set of procedures that may supersede some of the procedures described below. If one of Larceny's procedures duplicates the semantics of an R6RS procedure whose name is different, then Larceny's name is deprecated.

Procedure append!

(append! list1 list2 … obj) => object

append! destructively appends its arguments, which must be lists, and returns the resulting list. The last argument can be any object. The argument lists are appended by changing the cdr of the last pair of each argument except the last to point to the next argument.

Procedure every?

(every? procedure list1 list2 …) => object

every? applies procedure to each element tuple of list_s in first-to-last order, and returns #f as soon as _procedure returns #f. If procedure does not return #f for any element tuple of list_s, then the value returned by _procedure for the last element tuple of _list_s is returned.

Procedure last-pair

(last-pair list-structure) => pair

last-pair returns the last pair of the list structure, which must be a sequence of pairs linked through the cdr fields.

Procedure list-copy

(list-copy list-copy) => list

list-copy makes a shallow copy of the list and returns that copy.

Procedure remove

(remove key list) => list

Procedure remq

(remq key list) => list

Procedure remv

(remv key list) => list

Procedure remp

(remp pred? list) => list

Each of these procedures returns a new list which contains all the elements of list in the original order, except that those elements of the original list that were equal to key (or that satisfy pred?) are not in the new list. Remove uses equal? as the equivalence predicate; remq uses eq?, and remv uses eqv?.

Procedure remove!

(remove! key list) => list

Procedure remq!

(remq! key list) => list

Procedure remv!

(remv! key list) => list

Procedure remp!

(remp! pred? list) => list

These procedures are like remove, remq, remv, and remp, except they modify list instead of returning a fresh list.

Procedure reverse!

(reverse! list) => list

reverse! destructively reverses its argument and returns the reversed list.

Procedure some?

(some? procedure list1 list2 …) => object

some? applies procedure to each element tuple of list_s in first-to-last order, and returns the first non-false value returned by _procedure. If procedure does not return a true value for any element tuple of _list_s, then some? returns #f.

10.6. Sorting

The (rnrs sorting) library now provides a small set of procedures that supersede most of the procedures described below. All of the procedures described below are therefore deprecated.

Procedures sort and sort!

(sort list less?) => list

(sort vector less?) => vector

(sort! list less?) => list

(sort! vector less?) => vector

These procedures sort their argument (a list or a vector) according to the predicate less?, which must implement a total order on the elements in the data structures that are sorted.

sort returns a fresh data structure containing the sorted data; sort! sorts the data structure in-place.

10.7. Records

Note

Larceny's records have been extended to implement all ERR5RS and R6RS procedures from

(err5rs records procedural)
(err5rs records inspection)
(rnrs records procedural)
(rnrs records inspection)

We recommend that Larceny programmers use the ERR5RS APIs instead of the R6RS APIs. This should entail no loss of portability, since the standard reference implementation of ERR5RS records should run efficiently in any implementation of the R6RS that permits new libraries to defined at all.

Larceny now has two kinds of records: old-style and ERR5RS/R6RS. Old-style records cannot be created in R6RS-conforming mode, so our extension of R6RS procedures to accept old-style records does not affect R6RS conformance.

Note

The following specification describes Larceny's old-style record API, which is now deprecated. It is based on a proposal posted by Pavel Curtis to rrrs-authors on 10 Sep 1989, and later re-posted by Norman Adams to comp.lang.scheme on 5 Feb 1992. The authorship and copyright status of the original text are unknown to me.

This document differs from the original proposal in that its record types are extensible, and that it specifies the type of record-type descriptors.

10.7.1. Specification

Procedure make-record-type

(make-record-type type-name field-names)

Returns a "record-type descriptor", a value representing a new data type, disjoint from all others. The type-name argument must be a string, but is only used for debugging purposes (such as the printed representation of a record of the new type). The field-names argument is a list of symbols naming the "fields" of a record of the new type. It is an error if the list contains any duplicates.

If the parent-rtd argument is provided, then the new type will be a subtype of the type represented by parent-rtd, and the field names of the new type will include all the field names of the parent type. It is an error if the complete list of field names contains any duplicates.

Record-type descriptors are themselves records. In particular, record-type descriptors have a field printer that is either #f or a procedure. If the value of the field is a procedure, then the procedure will be called to print records of the type represented by the record-type descriptor. The procedure must accept two arguments: the record object to be printed and an output port.

Procedure record-constructor

(record-constructor rtd)

Returns a procedure for constructing new members of the type represented by rtd. The returned procedure accepts exactly as many arguments as there are symbols in the given list, field-names; these are used, in order, as the initial values of those fields in a new record, which is returned by the constructor procedure. The values of any fields not named in that list are unspecified. The field-names argument defaults to the list of field-names in the call to make-record-type that created the type represented by rtd; if the field-names argument is provided, it is an error if it contains any duplicates or any symbols not in the default list.

Procedure record-predicate

(record-predicate rtd)

Returns a procedure for testing membership in the type represented by rtd. The returned procedure accepts exactly one argument and returns a true value if the argument is a member of the indicated record type or one of its subtypes; it returns a false value otherwise.

Procedure record-accessor

(record-accessor rtd field-name)

Returns a procedure for reading the value of a particular field of a member of the type represented by rtd. The returned procedure accepts exactly one argument which must be a record of the appropriate type; it returns the current value of the field named by the symbol field-name in that record. The symbol field-name must be a member of the list of field-names in the call to make-record-type that created the type represented by rtd, or a member of the field-names of the parent type of the type represented by rtd.

Procedure record-updater

(record-updater rtd field-name)

Returns a procedure for writing the value of a particular field of a member of the type represented by rtd. The returned procedure accepts exactly two arguments: first, a record of the appropriate type, and second, an arbitrary Scheme value; it modifies the field named by the symbol field-name in that record to contain the given value. The returned value of the updater procedure is unspecified. The symbol field-name must be a member of the list of field-names in the call to make-record-type that created the type represented by rtd, or a member of the field-names of the parent type of the type represented by rtd.

(record? obj)

Returns a true value if obj is a record of any type and a false value otherwise. Note that record? may be true of any Scheme value; of course, if it returns true for some particular value, then record-type-descriptor is applicable to that value and returns an appropriate descriptor.

Procedure record-type-descriptor

(record-type-descriptor record)

Returns a record-type descriptor representing the type of the given record. That is, for example, if the returned descriptor were passed to record-predicate, the resulting predicate would return a true value when passed the given record. Note that it is not necessarily the case that the returned descriptor is the one that was passed to record-constructor in the call that created the constructor procedure that created the given record.

Procedure record-type-name

(record-type-name rtd)

Returns the type-name associated with the type represented by rtd. The returned value is eqv? to the type-name argument given in the call to make-record-type that created the type represented by rtd.

Procedure record-type-field-names

(record-type-field-names rtd)

Returns a list of the symbols naming the fields in members of the type represented by rtd.

Procedure record-type-parent

(record-type-parent rtd)

Returns a record-type descriptor for the parent type of the type represented by rtd, if that type has a parent type, or a false value otherwise. The type represented by rtd has a parent type if the call to make-record-type that created rtd provided the parent-rtd argument.

Procedure record-type-extends?

(record-type-extends? rtd1 rtd2)

Returns a true value if the type represented by rtd1 is a subtype of the type represented by rtd2 and a false value otherwise. A type s is a subtype of a type t if s=t or if the parent type of s, if it exists, is a subtype of t.

10.7.2. Implementation

The R6RS spouts some tendentious nonsense about procedural records being slower than syntactic records, but this is not true of Larceny's records, and is unlikely to be true of other implementations either. Larceny's procedural records are fairly efficient already, and will become even more efficient in future versions as interlibrary optimizations are added.

10.8. Input, Output, and Files

The (rnrs io ports) and (rnrs files) libraries now provide a set of procedures that may supersede some of the procedures described below. If one of Larceny's procedures duplicates the semantics of an R6RS procedure whose name is different, then Larceny's name is deprecated.

Procedure close-open-files

(close-open-files none) => unspecified

Closes all open files.

Procedure console-input-port

(console-input-port none) => input-port

Returns a character input port such that no read from the port has signalled an error or returned the end-of-file object.

Rationale: console-input-port and console-output-port are artifacts of Unix interactive I/O conventions, where an interactive end-of-file does not mean "quit" but rather "done here". Under these conventions the console port should be reset following an end-of-file. Resetting conflicts with the semantics of ports in Scheme, so console-input-port and console-output-port return a new port if the current port is already at end-of-file.

Since it is convenient to handle errors in the same manner as end-of-file, these procedures also return a new port if an error has been signalled during an I/O operation on the port.

Console-input-port and console-output-port simply call the port generators installed in the parameters console-input-port-factory and console-output-port-factory, which allow user programs to install their own console port generators.

Procedure console-output-port

(console-output-port none) => output-port

Returns a character output port such that no write to the port has signalled an error.

See console-input-port for a full explanation.

Parameter console-input-port-factory

The value of this parameter is a procedure that returns a character input port such that no read from the port has signalled an error or returned the end-of-file object.

See console-input-port for a full explanation.

Parameter console-output-port-factory

The value of this parameter is a procedure that returns a character output port such that no write the port has signalled an error.

See console-input-port for a full explanation.

Parameter current-input-port

The value of this parameter is a character input port.

Parameter current-output-port

The value of this parameter is a character output port.

Procedure delete-file

(delete-file filename) => unspecified

Deletes the named file. No error is signalled if the file does not exist.

Procedure eof-object

(eof-object none) => end-of-file object

Eof-object returns an end-of-file object.

Procedure file-exists?

(file-exists? filename) => boolean

File-exists? returns #t if the named file exists at the time the procedure is called.

Procedure file-modification-time

(file-modification-time filename) => vector or #f

File-modification-time returns the time of last modification of the file as a vector, or #f if the file does not exist. The vector has six elements: year, month, day, hour, minute, second, all of which are exact nonnegative integers. The time returned is relative to the local timezone.

(file-modification-time "larceny") => #(1997 2 6 12 51 13)

(file-modification-time "geekdom") => #f

Procedure flush-output-port

(flush-output-port none) => unspecified

(flush-output-port port) => unspecified

Write any buffered data in the port to the underlying output medium.

Procedure get-output-string

(get-output-string string-output-port) => string

Retrieve the output string from the given string output port.

Procedure open-input-string

(open-input-string string) => input-port

Creates an input port that reads from string. The string may be shared with the caller. A string input port does not need to be closed, although closing it will prevent further reads from it.

Procedure open-output-string

(open-output-string none) => output-port

Creates an output port where any output is written to a string. The accumulated string can be retrieved with [get-output-string] at any time.

Procedure port?

(port? object) => boolean

Tests whether its argument is a port.

Procedure port-name

(port-name port) => string

Returns the name associated with the port; for file ports, this is the file name.

Procedure port-position

(port-position port) => fixnum

Returns the number of characters that have been read from or written to the port.

Procedure rename-file

(rename-file from to) => unspecified

Renames the file from and gives it the name to. No error is signalled if from does not exist or to exists.

Procedure reset-output-string

(reset-output-string port) => unspecified

Given a port created with open-output-string, deletes from the port all the characters that have been output so far.

Procedure with-input-from-port

(with-input-from-port input-port thunk) => object

Calls thunk with current input bound to input-port in the dynamic extent of thunk. Returns whatever value was returned from thunk.

Procedure with-output-to-port

(with-output-to-port output-port thunk) => object

Calls thunk with current output bound to output-port in the dynamic extent of thunk. Returns whatever value was returned from thunk.

10.9. Operating System Interface

Procedure command-line-arguments

(command-line-arguments none) => vector

Returns a vector of strings: the arguments supplied to the program by the user or the operating system.

Procedure dump-heap

(dump-heap filename procedure) => unspecified

Dump a heap image to the named file that will start up with the supplied procedure. Before procedure is called, command line arguments will be parsed and any init procedures registered with add-init-procedure! will be called.

Note: Currently, heap dumping is only available with the stop-and-copy collector (-stopcopy command line option), although the heap image can be used with all the other collectors.

Procedure dump-interactive-heap

(dump-interactive-heap filename) => unspecified

Dump a heap image to the named file that will start up with the standard read-eval-print loop. Before the read-eval-print loop is called, command line arguments will be parsed and any init procedures registered with add-init-procedure! will be called.

Note: Currently, heap dumping is only available with the stop-and-copy collector (-stopcopy command line option), although the heap image can be used with all the other collectors.

Procedure getenv

(getenv key) => string or #f

Returns the operating system environment mapping for the string key, or #f if there is no mapping for key.

Procedure system

(system command) => status

Send the command to the operating system's command processor and return the command's exit status, if any. On Unix, command is a string and status is an exact integer.

10.10. Fixnum primitives

Fixnums are small exact integers that are likely to be represented without heap allocation. Larceny never represents a number that can be represented as a fixnum any other way, so programs that can use fixnums will do so automatically. However, operations that work only on fixnums can sometimes be substantially faster than generic operations, and the following primitives are provided for use in those programs that need especially good performance.

The (rnrs arithmetic fixnums) library now provides a large set of procedures that, in Larceny, are defined using the procedures described below. If one of Larceny's procedures duplicates the semantics of an R6RS procedure whose name is different, then Larceny's name is deprecated.

All arguments to the following procedures must be fixnums.

Procedure fixnum?

(fixnum? obj) => boolean

Returns #t if its argument is a fixnum, and #f otherwise.

Procedure fx+

(fx+ fix1 fix2) => fixnum

Returns the fixnum sum of its arguments. If the result is not representable as a fixnum, then an error is signalled (unless error checking has been disabled).

Procedure fx-

Returns the fixnum difference of its arguments. If the result is not representable as a fixnum, then an error is signalled.

Procedure fx—

(fx— fix1) => fixnum

Returns the fixnum negative of its argument. If the result is not representable as a fixnum, then an error is signalled.

Procedure fx*

(fx* fix1 fix2) => fixnum

Returns the fixnum product of its arguments. If the result is not representable as a fixnum, then an error is signalled.

Procedure fx=

(fx= fix1 fix2) => boolean

Returns #t if its arguments are equal, and #f otherwise.

Procedure fx<

(fx< fix1 fix2) => boolean

Returns #t if fix1 is less than fix2, and #f otherwise.

Procedure fx<=

(fx<= fix1 fix2) => boolean

Returns #t if fix1 is less than or equal to fix2, and #f otherwise.

Procedure fx>

(fx> fix1 fix2) => boolean

Returns #t if fix1 is greater than fix2, and #f otherwise.

Procedure fx>=

(fx>= fix1 fix2) => boolean

Returns #t if fix1 is greater than or equal to fix2, and #f otherwise.

Procedure fxnegative?

(fxnegative? fix) => boolean

Returns #t if its argument is less than zero, and #f otherwise.

Procedure fxpositive?

(fxpositive? fix) => boolean

Returns #t if its argument is greater than zero, and #f otherwise.

Procedure fxzero?

(fxzero? fix) => boolean

Returns #t if its argument is zero, and #f otherwise.

Procedure fxlogand

(fxlogand fix1 fix2) => fixnum

Returns the bitwise and of its arguments.

Procedure fxlogior

(fxlogior fix1 fix2) => fixnum

Returns the bitwise inclusive or of its arguments.

Procedure fxlognot

(fxlognot fix) => fixnum

Returns the bitwise not of its argument.

Procedure fxlogxor

(fxlogxor fix1 fix2) => fixnum

Returns the bitwise exclusive or of its arguments.

Procedure fxlsh

(fxlsh fix1 fix2) => fixnum

Returns fix1 shifted left fix2 places, shifting in zero bits at the low end. If the shift count exceeds the number of bits in the machine's word size, then the results are machine-dependent.

Procedure most-positive-fixnum

(most-positive-fixnum none) => fixnum

Returns the largest representable positive fixnum.

Procedure most-negative-fixnum

(most-negative-fixnum none) => fixnum

Returns the smallest representable negative fixnum.

Procedure fxrsha

(fxrsha fix1 fix2) => fixnum

Returns fix1 shifted right fix2 places, shifting in a copy of the sign bit at the left end. If the shift count exceeds the number of bits in the machine's word size, then the results are machine-dependent.

Procedure fxrshl

(fxrshl fix1 fix2) => fixnum

Returns fix1 shifted right fix2 places, shifting in zero bits at the high end. If the shift count exceeds the number of bits in the machine's word size, then the results are machine-dependent.

10.11. Numbers

Larceny has six representations for numbers: fixnums are small, exact integers; bignums are unlimited-precision exact integers; ratnums are exact rationals; flonums are inexact rationals; rectnums are exact complexes; and compnums are inexact complexes.

Number-representation predicates

(fixnum? obj) => boolean

(bignum? obj) => boolean

(ratnum? obj) => boolean

(flonum? obj) => boolean

(rectnum? obj) => boolean

(compnum? obj) => boolean

These predicates test whether an object is a number of a particular representation and return #t if so, #f if not.

Procedure random

(random limit) => exact integer

Returns a pseudorandom nonnegative exact integer in the range 0 through limit-1.

10.12. Hashtables and hash functions

Hashtables represent finite mappings from keys to values. If the hash function is a good one, then the value associated with a key may be looked up in constant time (on the average).

Note

The R6RS hashtables library are a big improvement over Larceny's traditional hash tables, and should be used instead of the API described below.

Note

To resolve a clash of names and semantics with the R6RS make-hashtable procedure, Larceny's traditional make-hashtable procedure has been renamed to make-oldstyle-hashtable.

10.12.1. Hash tables

Procedure make-oldstyle-hashtable

(make-oldstyle-hashtable hash-function bucket-searcher size) => hashtable

Returns a newly allocated mutable hash table using hash-function as the hash function and bucket-searcher, e.g. assq, assv, assoc, to search a bucket with size buckets at first, expanding the number of buckets as needed. The hash-function must accept a key and return a non-negative exact integer.

(make-oldstyle-hashtable hash-function bucket-searcher) => hashtable

Equivalent to (make-oldstyle-hashtable hash-function bucket-searcher n) for some value of n chosen by the implementation.

(make-oldstyle-hashtable hash-function) => hashtable

Equivalent to (make-oldstyle-hashtable hash-function assv).

(make-oldstyle-hashtable none) => hashtable

Equivalent to (make-oldstyle-hashtable object-hash assv).

Procedure hashtable-contains?

(hashtable-contains? hashtable key) => bool

Returns true iff the hashtable contains an entry for key.

Procedure hashtable-fetch

(hashtable-fetch hashtable key flag) => object

Returns the value associated with key in the hashtable if the hashtable contains key; otherwise returns flag.

Procedure hashtable-get

(hashtable-get hashtable key) => object

Eq