The Other Ikarus Scheme User's Guide

Short Contents

Table of Contents


Next: , Up: (dir)

The Other Ikarus Scheme User's Guide

This document describes version 0.0.3+ (revision 1661) of Ikarus, an R6RS compliant implementation of the Scheme programming language. The package is distributed under the terms of the GNU General Public License (GPL) and can be downloaded from:

http://www.ikarus-scheme.org/

or:

https://launchpad.net/ikarus

Copyright © 2007, 2008, 2009, 2010 by Abdulaziz Ghuloum.

This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License version 3 as published by the Free Software Foundation.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/.

Trademarks used herein are the property of their respective owners.

This document is version 2008-12-01-17-00-51 of an unofficial assemblage of several documents reformatted in Texinfo; the reformatting author and maintainer is Marco Maggi marcomaggi@gna.org with contributions by Vincent Manis vmanis@telus.net (from the ikscheme-manual project http://github.com/vmanis/ikscheme-manual/). See the appendix “Credits” for the list of original documents and their authors.

Appendices

Indexes

External documents

--- The Detailed Node Listing ---

Overview of Ikarus Scheme

System requirements

Installation and uninstallation

Using Ikarus

Ikarus and R6RS libraries

The Ikarus library

Local modules

Usage example for modules

Guardians

Input/output library

Using network sockets

POSIX functions

The (ikarus foreign) library

Memory operations

Calling out to foreign procedures


Next: , Previous: Top, Up: Top

1 Overview of Ikarus Scheme

Ikarus Scheme is an implementation of the Scheme programming language. The preliminary release of Ikarus implements the majority of the features found in the current standard, the “Revised^6 Report on the algorithmic language Scheme” including full R6RS library and script syntax, syntax–case, unicode strings, bytevectors, user–defined record types, exception handling, conditions, and enumerations. More than 94% of the R6RS procedures and keywords are currently implemented and subsequent releases will proceed towards bringing Ikarus to full R6RS conformance.

The main purpose behind releasing Ikarus early is to give Scheme programmers the opportunity to experiment with the various new features that were newly introduced in R6RS. The most important of such features is the ability to structure large programs into libraries; where each library extends the language through procedural and syntactic abstractions.

Many useful libraries can be written using the currently supported set of R6RS features including text processing tools, symbolic logic systems, interpreters and compilers, and many mathematical and scientific packages. It is the author's hope that this release will encourage the Scheme community to write and to share their most useful R6RS libraries.


Next: , Up: overview

1.1 Technology overview

Ikarus Scheme provides the programmer with many advantages:

Optimizing code generator
The compiler's backend employs state of the art technologies in code generation that produce fast, efficient, machine code. When developing computationally intensive programs, one is not constrained by using a slow interpreter.
Fast incremental compilation
Every library and script is quickly compiled to native machine code. When developing large software, one is not constrained by how slow the batch compiler runs.
Robust and fine–tuned standard libraries
The standard libraries are written such that they perform as much error checking as required to provide a safe and fast runtime environment.
Multi–generational garbage collector
The BiBOP based garbage collector used in Ikarus allows the runtime system to expand its memory footprint as needed. The entire 32-bit virtual address space could be used and unneeded memory is released back to the operating system.
Supports many operating systems
Ikarus runs on the most popular and widely used operating systems for servers and personal computers. The supported systems include Mac OS X, GNU/Linux, FreeBSD, NetBSD, and Microsoft Windows (under Cygwin).


Next: , Previous: overview tech, Up: overview

1.2 Resources

The Ikarus website is found at http://ikarus-scheme.org; a project page on Launchpad (https://launchpad.net/ikarus) provides access to the bug tracker.

Newcomers to Scheme should refer to http://www.schemers.org, which contains listings of tutorials and books that are helpful in learning the language.

This document is a supplement to the Revised^6 Report on the Algorithmic Language Scheme, found at http://www.r6rs.org. Kent Dybvig's The Scheme Programming Language, Fourth Edition, is an excellent tutorial and reference for R6RS. Some extended features of Ikarus have been inspired by similar features in Chez Scheme; the Chez Scheme User's Guide can be found at the same site.

Many common extensions to Scheme have been documented as SRFIs (“Scheme Requests for Implementation”); all of these documents can be found at http://srfi.schemers.org.

Ikarus provides a core set of features, but a number of third–party libraries that work with Ikarus have been developed. Some of these libraries include:

Developers of other extension libraries are encouraged to contact us to have their libraries listed here.


Next: , Previous: overview resources, Up: overview

1.3 System requirements

To run Ikarus, you will need a system with a relatively recent Intel x86 processor; Ikarus requires a Unix/Linux operating system (or emulation), and some prerequisite libraries.


Next: , Up: overview system

1.3.1 Hardware requirements

Ikarus Scheme runs on the IA-32 (x86) architecture supporting SSE2 extensions. This includes the Athlon 64, Sempron 64, and Turion 64 processors from AMD and the Pentium 4, Xeon, Celeron, Pentium M, Core, and Core2 processors from Intel. The system does not run on Intel Pentium III or earlier processors.

The Ikarus compiler generates SSE2 instructions to handle Scheme's IEEE floating point representation (flonums) for inexact numbers.

Operating systems

Ikarus is tested under the following operating systems; in general, any later version of one of these systems should be usable:


Previous: overview system hardware, Up: overview system

1.3.2 Software requirements

You will need some additional libraries and tools to build Ikarus.

GMP
Ikarus uses the GNU Multiple Precision Arithmetic Library (GMP) for some bignum arithmetic operations. To build Ikarus from scratch, GMP version 4.2 or better must be installed along with the required header files. Pre–built GMP packages are available for most operating systems. Alternatively, GMP can be downloaded from http://gmplib.org/.

The GMP web page points out that GMP has revealed subtle bugs in many C compilers. Although the GMP build process includes a comprehensive self–test (which you absolutely must run if building it yourself), you may still prefer to obtain a prebuilt binary version for your system, if one is available.

Note: Ikarus runs in 32-bit mode only. To run it in 64-bit environments, you will have to obtain the 32-bit version of GMP, or compile it yourself after adding ABI=32 to its configuration options.

libffi
You will probably need libffi, a C library that simplifies run–time calls to arbitrary C functions. Ikarus will operate without libffi, but will not be able to call foreign procedures (those written in C). libffi can be downloaded from http://sourceware.org/libffi.
GCC
The GNU C Compiler is required to build the Ikarus executable (e.g. the garbage collector, loader, and OS–related runtime). GCC versions 4.1 and 4.2 were successfully used to build Ikarus. It may be possible to build Ikarus using other C compilers
Autoconf and Automake
The GNU Autoconf (version 2.61) and GNU Automake (version 1.10) tools are required if one wishes to modify the Ikarus source base. They are not required to build the official release of Ikarus.

Prebuilt packages for these tools have been created for most Unix/Linux systems; if you prefer, you can download the source code from http://www.gnu.org/software/autoconf and http://www.gnu.org/software/automake respectively, and build them yourself.

XeLaTeX
The XeLaTeX typesetting system is required for building the documentation. XeLaTeX (and XeTeX) is an implementation of the LaTeX (and TeX) typesetting system. XeLaTeX can be obtained from http://scripts.sil.org/xetex and is included with TeX–Live (http://tug.org/texlive/) and and Mac–TeX (http://tug.org/mactex/) distributions.
TeX and Texinfo
(Not needed for a routine install.) This document is provided in PDF, Info, and HTML formats. If you wish to rebuild the document from its Texinfo source, you will need TeX (for creating the PDF version) and Texinfo (for creating the other versions).

Many Unix and Linux systems have installable TeX packages; if you cannot find one for your system, you can install TeX Live (http://tug.org/texlive/), for Unix/Linux and Windows; MacTeX (http://tug.org/mactex/) is a comparable system for Macintosh OS X.

Texinfo is also available as an installable package for most Unix/Linux systems. Alternatively, you can download it from http://www.gnu.org/software/texinfo and build it yourself.


Previous: overview system, Up: overview

1.4 Installation and uninstallation

There are two ways to install Ikarus on your system: you can either install a prebuilt package, if one is available for your operating system, or you can build from source. There are two reasons for preferring the “build from source” approach. First, not all Unix/Linux package repositories have an Ikarus package at all. Second, prebuilt packages often lag current revisions to the system by a considerable amount. Therefore, the version you install from a package repository might not even have some of the features documented in this manual.

We will assume in this section that you are building Ikarus yourself. Ikarus uses the standard installation method found in most other Unix software. Thus you will follow the usual steps.

  1. Download the software.
  2. Set configuration options.
  3. Build and install


Next: , Up: overview install

1.4.1 Quick installation guide

If familiar with installing Unix software on our system, then all we need to know is that Ikarus uses the standard installation method found in most other Unix software. Simply run the following commands from the shell:

     $ tar -zxf ikarus-n.n.n.tar.gz
     $ cd ikarus-n.n.n
     $ ./configure [--prefix=path] [CFLAGS=-I/dir] [LDFLAGS=-L/dir]
     $ make
     $ make install

overview install details for how to enable the foreign functions interface.

The rest of this section gives more detail on building, installing, and uninstalling Ikarus.

To uninstall Ikarus, use the following steps:

     $ cd path/to/ikarus-n.n.n
     $ make uninstall


Next: , Previous: overview install quick, Up: overview install

1.4.2 Installing the prerequisites

The first step is to ensure that you have GMP and libffi installed. As mentioned earlier, you should generally use your system's package manager for these. Note where the include (.h) and library (.a) files for each of these packages are installed. (Typically this will be in either /usr/include and /usr/lib, or /usr/local/include and /usr/local/lib.)

If you choose to download these as source and build them yourselves, refer to the installation instructions found in each package. If you are installing GMP from source, it is essential that you run the self–tests after building, and verify that no errors were detected. You do this by the command make check; see the documentation for details.


Next: , Previous: overview install prerequisites, Up: overview install

1.4.3 Installation details

You have two choices for downloading the Ikarus source: you can either download a distribution from http://ikarus-scheme.org, or you can refer to devel for information on downloading a development (“bleeding edge”) version.

Configure the build system by running the configure script located in the base directory. To do this, type the following commands:

     $ cd ikarus-n.n.n
     $ ./configure
     checking build system type... i386-apple-darwin8.10.1
     checking host system type... i386-apple-darwin8.10.1
     ...
     configure: creating ./config.status
     config.status: creating Makefile
     config.status: creating src/Makefile
     config.status: creating scheme/Makefile
     config.status: creating doc/Makefile
     config.status: executing depfiles commands

This configures the system to be built then installed in the system–wide location (binaries are normally installed in /usr/local/bin). To install it in another location (e.g. in your home directory), provide a --prefix location to configure as follows.

     $ ./configure --prefix=/opt/sw

This will install the executable in /opt/sw/bin, libraries in /opt/sw/lib/ikarus, and documentation in /opt/sw/share/doc/ikarus.

To install Ikarus for your own use (thus not requiring root permissions to install), specify --prefix=$HOME/local, which installs everything in a local directory of your home directory.

configure allows you to fine–tune where things are installed, though this is rarely necessary or useful. Do ./configure --help for a full list of options.

The most common configure options are as follows.

--prefix
Specify the location in the file system where Ikarus will be installed.
--enable-ffi
Include Ikarus's Foreign Function Interface, so that Ikarus code can invoke C code, and vice versa. Requires libffi.
CFLAGS
Specify options to be used while compiling Ikarus's C code.
LDFLAGS
Specify options to be used while linking Ikarus.

configure will fail if it cannot find the location where GMP is installed. The script will also fail if you have specified --enable-ffi and it can't find libffi. If running configure fails to locate either of these libraries, you will need to provide their locations. Refer back to your notes on location of the GMP and libffi files, and use the CFLAGS and LDFLAGS options to specify the locations of the header and library files accordingly.

For example, assume that you have installed GMP and libffi in subdirectories of /opt/sw, and you wish to support foreign functions.1

     ./configure                                                \
       --prefix=$HOME/local                                     \
       --enable-ffi                                             \
       CFLAGS="-I/opt/sw/gmp/include -I/opt/sw/libffi/include"  \
       LDFLAGS="-L/opt/sw/gmp/lib -L/opt/sw/libffi/lib"

You can use the CFLAGS and LDFLAGS variables to select more specialized compilation and linking options; refer to your compiler documentation for more details.

You can now build the system by running the command make, with no arguments. This performs two tasks:

  1. It builds the ikarus executable from the C files located in the src directory.
  2. It uses the ikarus executable and the pre–built ikarus.boot.orig boot file to rebuild the Scheme boot image file ikarus.boot from the Scheme sources located in the scheme directory.

The final stage is to install Ikarus via the command make install. If you're installing Ikarus in a system–wide location, you'll probably need to have administrator privileges (use the sudo or su commands). If that's not feasible, then reconfigure to install within a directory tree under your home directory.

Finally, try a small session, to verify that everything installed properly.

     $ ikarus
     Ikarus Scheme version 0.0.4-rc1+ (revision 1865, build 2009-11-17)
     Copyright (c) 2006-2009 Abdulaziz Ghuloum
     
     > (display "hello, world!\n")
     hello, world!
     > (define twice (lambda (f) (lambda (x) (f (f x)))))
     > ((twice add1) 3)
     5

If you get the first > prompt, then Ikarus was successfully installed on the system. You may need to update the PATH variable in environment to contain the directory in which the ikarus executable was installed.

Finally, do make clean to get rid of executables, object files, and other build products in your ikarus-n.n.n directory. Do not delete the directory itself: it will be needed if you ever want to uninstall Ikarus.


Previous: overview install details, Up: overview install

1.4.4 Uninstalling Ikarus

To uninstall Ikarus, go to the directory you built Ikarus in (not the directory where you installed it), and do make uninstall.

     $ cd path/to/ikarus-n.n.n
     $ make uninstall


Next: , Previous: overview, Up: Top

2 Using Ikarus

Once Ikarus is properly installed, you can invoke it either in an interactive terminal session or as the interpreter for invoking a script.


Next: , Up: using

2.1 Interactive sessions

If you want an interactive session, use a bare ikarus command, with no options.

     $ ikarus
     Ikarus Scheme version 0.0.4-rc1+ (revision 1865, build 2009-11-17)
     Copyright (c) 2006-2009 Abdulaziz Ghuloum
     
     > (+ 2 2)
     4

end–of–file (<Ctrl-D> in a typical terminal window) will terminate the session.


Next: , Previous: using interactive, Up: using

2.2 Command line arguments

The ikarus executable recognizes a few command line switches that influence how Ikarus starts.

ikarus -h
The presence of the -h flag causes ikarus to display a help message then to exit. The help message summarizes the command line switches. No further action is performed.
ikarus -b path/to/boot/file.boot
The -b flag (which requires an extra argument) directs ikarus to use the specified boot file as the initial system boot file. The boot file is a binary file that contains all the code and data of the Scheme system. In the absence of -b flag, the executable will use the default boot file. Running ikarus -h shows the location where the default boot file was installed.

The rest of the command line arguments are recognized by the standard Scheme run time system. They are processed after the boot file is loaded.

ikarus --r6rs-script script-file-name [arguments ...]
The --r6rs-script argument instructs Ikarus that the supplied file is an R6RS script. The script file name and any additional optional arguments can be obtained by calling the command-line procedure.
          $ cat test.ss
          (import (rnrs))
          (write (command-line))
          (newline)
          
          $ ikarus --r6rs-script test.ss hi there
          ("test.ss" "hi" "there")

ikarus files ... [-- arguments ...]
The lack of an --r6rs-script argument causes Ikarus to start in interactive mode. Each of the files is first loaded, in the interaction environment. The interaction environment initially contains all the bindings exported from the (ikarus) library.

The optional arguments following the -- marker can be obtained by calling the command-line procedure. In interactive mode, the first element of the returned list will be the string *interactive*, corresponding to the script name in R6RS–script mode.

NOTE R6RS lives in a somewhat uneasy tension with a standard Scheme REPL (read-eval-print loop), which the Report effectively does not define. In particular, the effect of re–importing a module, as you might do when reloading and rerunning a program, isn't well–defined by R6RS.

This is a limitation caused by the particular design choices made in R6RS; it's hoped that a future revision to the language will repair this particular problem. Even so, we hope to achieve better interaction between loading and libraries in the future.

Accordingly, the interactive mode is intended for quick experimenting with the built–in features. It is intended neither for developing applications nor for writing any substantial piece of code.


Next: , Previous: using invoking, Up: using

2.3 Using scheme-script

Scheme scripts can be executed using the following command:

     ikarus --r6rs-script script-name

as described in the previous section. For convenience, Ikarus follows the R6RS recommendations and installs a wrapper program called scheme-script.

Here is a sample script (Pig Latin—“Igpay Atinlay”—is a code that was at one time popular among very young North American children).

     (import (rnrs))
     
     ;;; Convert a string to its Pig Latin equivalent.
     ;;;
     ;;; If the first character is a vowel, append "yay".
     ;;; "egg" -> "eggyay"
     ;;;
     ;;; If the first character is a consonant, remove it,
     ;;; and append it plus "ay" to the string.
     ;;; "foo" -> "oofay"
     
     (define pig-latin
       (lambda (str)
         (let ((first (string-ref str 0)))
           (if (memv first '(#\a #\e #\i #\o #\u))
     	  (string-append str "yay")
     	  (string-append
      	    (substring str 1 (string-length str))
     	    (string first) "ay")))))
     
     (display
      (map pig-latin
          (cdr (command-line))))
     (newline)

Assuming we have stored the script in the file demo, we can invoke this script via scheme-script.

     $ scheme-script demo ice cream after dinner
     (iceyay reamcay afteryay innerday)

On Unix/Linux systems, scripts generally start with a “shebang” line (sharp plus bang) which names an interpreter for the script. All modern systems have the env command which searches for a command in the user's path.

     #!/usr/bin/env scheme-script
     (import (rnrs))
     
     ...

Now we can make the file executable, and use it directly.

     $ chmod +x demo
     $ ./demo ice cream after dinner
     (iceyay reamcay afteryay innerday)

There are a few fine points.


Previous: using script, Up: using

2.4 Ikarus and R6RS libraries

One of the major features added in R6RS was a standardized concept of libraries: modules that export features that can be imported into programs. A library is named by a list, e.g., (yoyodyne block-transfer-computations); the core library of the language is provided in the library (rnrs).

Unfortunately, libraries have many quirks. Partially, this comes from the way libraries are defined in R6RS; some fine–tuning in the future will probably tighten up the semantics. Partially, this comes from the fact that no language standard can or should define how external code modules are stored and retrieved; there are many decisions that an implementer must be free to make (a hypothetical Scheme implementation might want to retrieve libraries from a database system, for example).

This section discusses the Ikarus implementation of libraries.


Next: , Up: using libraries

2.4.1 Introduction to libraries

A library consists of a “file” (some assemblage of text) that contains a library form, which might look like this:

     (library (sample)
       (export cube)
       (import (rnrs))
       (define cube
         (lambda (x)
           (* x x x))))

notice the import clause; libraries have no predefined namespace, and therefore at least (rnrs) must be imported.

We can import (sample) into another library, a script, or an Ikarus top–level session:

     > (import (sample))
     > (cube 3)
     27

Library names are lists, thus allowing the writer of one or more libraries to group related libraries together. For example, as well as (yoyodyne block-transfer-computations), we might also have (yoyodyne tardis navigation) and (yoyodyne tardis diagnostics). Leaving the parentheses off a single–component library name is a common error.


Next: , Previous: using libraries intro, Up: using libraries

2.4.2 How Ikarus finds libraries

In Ikarus, a library is a file with the same name as the last component of the library name. For example, the library (sample) will correspond to a file named sample.sls somewhere in the file system. The file extension sls stands for “Scheme library source”; although Ikarus will also accept the extensions ss and scm, these really indicate scripts, rather than libraries.

Ikarus has a search algorithm for libraries that is similar to those used in other languages. Its search path comprises a series of directories. When you try to import a library whose name contains just one component, Ikarus will examine each directory in the search path for a file with a matching name. Importing a library whose name is a list of components causes Ikarus to match to a series of subdirectories with matching names.

Here is an example of each case:

Ikarus always places its own libraries at the end of the search path. Other library directories can be added to the search path in two ways: by modifying an environment variable, or by setting Ikarus's library-path parameter. Ikarus consults the environment variable IKARUS_LIBRARY_PATH at the beginning of execution, and places each of the directories there into its search path. The search path is then set to the value of the parameter library-path, which can be updated as necessary.

     $ export IKARUS_LIBRARY_PATH=$HOME/scheme:/tmp/library
     $ echo $IKARUS_LIBRARY_PATH
     /home/someuser/scheme:/tmp/library
     $ ikarus
     Ikarus Scheme version 0.0.4-rc1+ (revision 1865, build 2009-11-17)
     Copyright (c) 2006-2009 Abdulaziz Ghuloum
     
     > (library-path)
     ("/home/someuser/scheme" "/tmp/library"
       "/home/someuser/local/lib/ikarus")
     > (library-path (cons "/opt/sw/extra-libraries" (library-path)))
     > (library-path)
     ("/opt/sw/extra-libraries" "/home/someuser/scheme"
       "/tmp/library" "/home/someuser/local/lib/ikarus")

In this example, the user sets IKARUS_LIBRARY_PATH before the session; the settings are reflected in the value of (library-path). A further directory is then set during the session.

In programs intended to be distributed, you should resist the urge to set (library-path), as the user's file hierarchies might not match yours. For most libraries, simply telling the user to install the library in one of the libraries on the search path is good enough.

There are two refinements of the library search algorithm.


Next: , Previous: using libraries searching, Up: using libraries

2.4.3 Library search algorithm summary

Ikarus attempts to import the library (a b c) as follows, for each directory in the search path:

  1. Build the rootname a/b/c.
  2. Append each of the following strings to the rootname, in order, until a matching file is found:
              /main.ikarus.sls
              /main.ikarus.ss
              /main.ikarus.scm
              /main.sls
              /main.ss
              /main.scm
              .ikarus.sls
              .ikarus.ss
              .ikarus.scm
              .sls
              .ss
              .scm
    

If there is no match, then the import is in error.


Previous: using libraries summary, Up: using libraries

2.4.4 Examples of library usage

Consider a program using pretty-print procedure to format some code, and suppose further that pretty printing is just a nice add–on (e.g. using write suffices, but pretty–printing is just prettier).

Ikarus exports a good pretty–printing facility in its (ikarus) library. However, since pretty-print is not a standard procedure, a program that uses it would be rendered unportable to other R6RS Scheme implementations.

The programmer can put the .ikarus.* extensions to use in this situation, by writing two versions of a (pretty-printing) library: one for use by Ikarus, and one portable for other implementations.

     ;; pretty-printing.ikarus.ss --
     ;;
     ;; Can be used only by Ikarus Scheme.
     
     (library (pretty-printing)
       (export pretty-print)
       (import (only (ikarus) pretty-print)))
     
     ;;; end of file
     ;; pretty-printing.sls --
     ;;
     ;; For any other Scheme implementation, portable though
     ;; not very pretty.
     
     (library (pretty-printing)
       (export pretty-print)
       (import (rnrs))
       (define (pretty-print x port)
         (write x port)
         (newline port)))
     
     ;;; end of file


Next: , Previous: using, Up: Top

3 The Ikarus library

In addition to the libraries listed in the R6RS standard, Ikarus contains the (ikarus) library which provides additional useful features. The (ikarus) library is a composite library—it exports a superset of all the supported bindings of R6RS. While not all of the exports of (ikarus) are documented at this time, this chapter attempts to describe a few of these useful extensions. Extensions to Scheme's lexical syntax are also documented.


Next: , Up: ikaruslib

3.1 Writing cross–implementation libraries

When searching for a library, Ikarus appends a suffix to the appropriate file pathname; the initial set of suffixes is:

     /main.ikarus.sls
     /main.ikarus.ss
     /main.ikarus.scm
     /main.sls
     /main.ss
     /main.scm
     .ikarus.sls
     .ikarus.ss
     .ikarus.scm
     .sls
     .ss
     .scm

This list of file suffixes is iterated sequentially. As a consequence, files ending with the .ikarus.* suffixes are given precedence over files that have generic Scheme extensions. The rationale for this behaviour is to facilitate writing cross–implementation libraries: ones that take advantage of implementation–specific features, while at the same time to provide a fail–safe alternative for other R6RS implementations.

Consider for example a program which would like to use the pretty-print procedure to format some code, and suppose further that pretty printing is just a nice add–on (e.g. using write suffices, but pretty–printing is just prettier) Ikarus exports a good pretty–printing facility in its (ikarus) library. However, since pretty-print is not a standard procedure, a program that uses it would be rendered unportable to other R6RS Scheme implementations.

The programmer can put the .ikarus.* extensions to use in this situation, by writing two versions of a (pretty-printing) library: one for use by Ikarus, and one portable for other implementations.

     ;; pretty-printing.ikarus.ss --
     ;;
     ;; Can be used only by Ikarus Scheme.
     
     (library (pretty-printing)
       (export pretty-print)
       (import (only (ikarus) pretty-print)))
     
     ;;; end of file
     ;; pretty-printing.sls --
     ;;
     ;; For any other Scheme implementation, portable though
     ;; not very pretty.
     
     (library (pretty-printing)
       (export pretty-print)
       (import (rnrs))
       (define (pretty-print x port)
         (write x port)
         (newline port)))
     
     ;;; end of file

The /main.* suffixes serve a different purpose. Often, a set of libraries is distributed in a single package and it is convenient for the programmer to group related files in directories. If a package contains the libraries (foo), (foo core), and (foo compat), then putting all such library files together in one directory makes it easier to package, install, and remove these libraries en masse. The layout of the package would look like:

     foo/README              :              ignored by Ikarus
     foo/COPYING             :
     foo/main.ss             : (foo)        implementation independent
     foo/core.ss             : (foo core)
     foo/compat.ss           : (foo compat) default R6RS library
     foo/compat.ikarus.ss    :              specific for Ikarus
     foo/compat.mzscheme.ss  :              specific for MzScheme


Next: , Previous: ikaruslib cross, Up: ikaruslib

3.2 Reader

— Reader Syntax: #!ikarus

Ikarus extends Scheme's lexical syntax (R6RS Chapter 4) in a variety of ways including:

The syntax extensions are made available by default on all input ports, until the #!r6rs token is read. Thus, reading the #!r6rs token disables all extensions to the lexical syntax on the specific port, and the #!ikarus enables them again.

When writing code that is intended to be portable across different Scheme implementations, we should add the #!r6rs token to the top of every script and library that you write; this allows Ikarus to alert us when using non–portable features. When writing code that's intended to be Ikarus–specific, we should add the #!ikarus token in order to get an immediate error when the code is run under other implementations.

— Procedure: port-mode input-port

Accept an input port as argument and return a symbol among r6rs-mode and ikarus-mode as result. All input ports start under ikarus-mode and thus accept Ikarus–specific reader extensions. When the #!r6rs token is read from a port, its mode changes to ikarus-mode.

          > (port-mode (current-input-port))
          ikarus-mode
          > #!r6rs (port-mode (current-input-port))
          r6rs-mode
          > #!ikarus (port-mode (current-input-port))
          ikarus-mode
— Procedure: set-port-mode! input-port mode

Modifiy the lexical syntax accepted by subsequent calls to read on the input port. The mode is a symbol among r6rs-mode and ikarus-mode. The effect of setting the port mode is similar to that of reading the #!r6rs or #ikarus from that port.

          > (set-port-mode! (current-input-port) 'r6rs-mode)
          > (port-mode (current-input-port))
          r6rs-mode
— Reader Syntax: #!eof

The end–of–file marker, #!eof, is an extension to the R6RS syntax. The primary utility of the #!eof marker is to stop the reader (e.g. read and get-datum) from reading the rest of the file.

          #!/usr/bin/env scheme-script
          (import (ikarus))
          <some code>
          (display "goodbye\n")
          
          #!eof
          <some junk>

The #!eof marker also serves as a datum in Ikarus, much like #t and #f, when it is found inside other expressions.

          > (eof-object)
          #!eof
          > (read (open-input-string ""))
          #!eof
          > (read (open-input-string "#!eof"))
          #!eof
          > (quote #!eof)
          #!eof
          > (eof-object? '#!eof)
          #t
          > #!r6rs #!eof
          Unhandled exception
          Condition components:
            1. &error
            2. &who: tokenize
            3. &message: "invalid syntax: #!e"
          > #!ikarus #!eof
          $


Next: , Previous: ikaruslib reader, Up: ikaruslib

3.3 Cafe

— Function: new-cafe eval

The procedure new-cafe starts a new read–eval–print loop inside the current cafe (if one exists). It prompts the user for an expression, evaluates it, prints the result back, and repeats the process. If new-cafe is called with an argument, eval, then that argument must be a procedure that takes a single argument. The eval procedure will be used to evaluate the expressions.

Every time a new cafe is started, the prompt is changed to reflect the depth of the current cafe (i.e. how many eof objects is takes to exit the outermost cafe).

Input and output performed by the cafe can be changed by the console-input-port and console-output-port parameters.

If a die occurs during reading, evaluating, or printing an expression, then the die is printed to the error–port and the operations of the cafe resume as normal.

To exit from a cafe we can write the #!eof object.


Next: , Previous: ikaruslib cafe, Up: ikaruslib

3.4 Environments

— Function: interaction-environment

Return an environment object representing the environment active at the REPL. To be used as argument for eval.

Undocumented feature: this feature is still undocumented in Ikarus revision 1661, so it may change in the future.


Next: , Previous: ikaruslib environment, Up: ikaruslib

3.5 Loading source files

Loading of source files can be done explicitly or by requesting a library with the import form. Here we see how to load files by specifying their pathname on the file system.

— Function: load pathname [eval-proc]

Read and evaluate the file selected by the string pathname.

If eval-proc is given: it must be a procedure that takes a single argument, an annotated form, and evaluates it. The default eval-proc looks like this:

          (define (load-handler x)
            (eval x (interaction-environment)))

The format of the annotated source is the one which is comprehensible by eval.


Next: , Previous: ikaruslib load, Up: ikaruslib

3.6 Local library imports

— Syntax: import import-spec* ...

The import keyword which is exported from the (ikarus) library can be used anywhere definitions can occur: at a script body, library's top–level, or in internal definitions context. The syntax of the local import form is similar to the import that appears at the top of a library or a script form, and carries with it the same restrictions: no identifier name may be imported twice unless it denotes the same identifier; no identifier may be both imported and defined; and imported identifiers are immutable.

Local import forms are useful for two reasons: (1) they minimize the namespace clutter that usually occurs when many libraries are imported at the top level, and (2) they limit the scope of the import and thus help modularize a library's dependencies.

Suppose you are constructing a large library and at some point you realize that one of your procedures needs to make use of some other library for performing a specific task. Importing that library at top level makes it available for the entire library. Consequently, even if that library is no longer used anywhere in the code (say when the code that uses it is deleted), it becomes very hard to delete the import without first examiniming the entire library body for potential usage leaks. By locally importing a library into the appropriate scope, we gain the ability to delete the import form when the procedure that was using it is deleted.


Next: , Previous: ikaruslib import, Up: ikaruslib

3.7 Local modules

Undocumented feature: this feature is still undocumented in Ikarus revision 1661, so it may change in the future.

Modules are somewhat like libraries in that they define a set of bindings in a “private namespace”; bindings can be exported from a module and imported into other modules and libraries. The main difference between modules and R6RS libraries is that modules are defined in a single form nested into a library; so, in a way, they are sub–libraries.

Note: In the official documentation of Ikarus 0.0.3+ (revision 1648) modules are yet not documented. Please refer to Section 10.5 of Chez Scheme User's Guide, Chapter 3 of Oscar Waddel's Ph.D Thesis, and its POPL99 paper for details on using the module and import keywords. Ikarus's internal module system is similar in spirit to that of Chez Scheme.


Next: , Up: ikaruslib modules

3.7.1 Usage example for modules


Next: , Up: ikaruslib modules examples
3.7.1.1 Anonymous modules
Undocumented feature: this feature is still undocumented in Ikarus revision 1661, so it may change in the future.

The following example defines an anonymous module, without expressions, and invokes its functions from the enclosing environment (which is the top level):

     (import (ikarus))
     
     [module (one two three)
         (define (one)       'one)
         (define (two)       'two)
         (define (three)     (cons 'three (hidden)))
         (define (hidden)    'hidden)]
     
     (printf "calling anonymous: ~s ~s ~s~%" (one) (two) (three))

notice that the hidden function is visible inside the module but not in the enclosing environment.

The following example defines a module, with expressions, and invokes its functions from the enclosing environment (which is the top level):

     (import (ikarus))
     
     [module (one two three)
         (define (one)       'one)
         (define (two)       'two)
         (define (three)     (cons 'three (hidden)))
         (define (hidden)    'hidden)
     
         (printf "defining an anonymous module~%")]
     
     (printf "calling anonymous: ~s ~s ~s~%" (one) (two) (three))

notice that when the enclosing environment is the top level: the expressions at the end of a module are evaluated after the expressions at the top level; so the output from the example is:

     calling anonymous: one two (three . hidden)
     defining an anonymous module

this can lead to undesired results if the module's expressions are meant to initialise the state of the module, and the expressions at the top level invoke the module's functions (before initialisation). On the other hand: this behaviour allows the module to access definitions from the enclosing environment and the top level environment despite their placement in the file; see below for an example. The same behaviour is shown by named modules.

The following example defines a module, with expressions, and invokes its functions from the enclosing environment (which is a let form):

     (import (ikarus))
     
     (let ()
       [module (one two three)
           (define (one)       'one)
           (define (two)       'two)
           (define (three)     (cons 'three (hidden)))
           (define (hidden)    'hidden)
     
           (printf "defining an anonymous module~%")]
     
       (printf "calling anonymous: ~s ~s ~s~%" (one) (two) (three)))

notice that when the enclosing environment is not the top level: the expressions at the end of a module are evaluated before the expressions in the body of the enclosing environment; so the output from the example is:

     defining an anonymous module
     calling anonymous: one two (three . hidden)

so the state of the module can be correctly initialised before its functions are invoked. The same behaviour is shown by named modules.

The following example shows that bindings from the enclosing environment and the top level environment are accessible from the modules:

     (import (ikarus))
     
     (define (top-level-before) 'top-level-before)
     
     (let ()
     
       (define (outer-before) 'outer-before)
     
       [module (one two three)
         (define (one)       'one)
         (define (two)       'two)
         (define (three)     (list 'three
                                   (outer-before)
                                   (outer-after)
                                   (top-level-before)
                                   (top-level-after)))]
     
       (define (outer-after) 'outer-after)
     
       (printf "calling anonymous: ~s ~s ~s~%" (one) (two) (three)))
     
     (define (top-level-after) 'top-level-after)

this happens both when the enclosing environment is the top level and when the enclosing environment is not the top level. The same behaviour is shown by named modules.


Next: , Previous: ikaruslib modules examples anonymous, Up: ikaruslib modules examples
3.7.1.2 Named modules

The following example defines a module named blue and invokes functions from it in the top level environment:

     (import (ikarus))
     
     [module blue (blue-one blue-two blue-three)
         (define (blue-one)          'blue-one)
         (define (blue-two)          'blue-two)
         (define (blue-three)        (cons 'blue-three (hidden)))
         (define (hidden)            'blue-hidden)]
     
     (import blue)
     (printf "calling blue: ~s ~s ~s~%"
             (blue-one) (blue-two) (blue-three))

notice that bindings from a named module are accessible only if imported in the enclosing environment.

The following example defines two modules named green and red, then it imports their bindings in different environments:

     (import (ikarus))
     
     (let ()
     
       [module green (one two three)
         (define (one)       'green-one)
         (define (two)       'green-two)
         (define (three)     (cons 'green-three (hidden)))
         (define (hidden)    'green-hidden)]
     
       [module red (one two three)
         (define (one)       'red-one)
         (define (two)       'red-two)
         (define (three)     (cons 'red-three (hidden)))
         (define (hidden)    'red-hidden)]
     
       (import green)
     
       [let ()
         (import red)
         (printf "calling red: ~s ~s ~s~%" (one) (two) (three))]
     
       (printf "calling green: ~s ~s ~s~%" (one) (two) (three)))

The following example shows that modules in the same enclosing environment can import their bindings:

     (import (ikarus))
     
     (let ()
     
       [module green (one two)
         (define (one)       'one)
         (define (two)       'two)]
     
       [module red (f g)
         (import green)
         (define (f)         (cons 'f (one)))
         (define (g)         (cons 'g (two)))]
     
       (import red)
       (printf "calling red: ~s ~s~%" (f) (g)))

while the following example shows that bindings from an anonymous module are automatically available in modules defined in the same enclosing environment:

     (import (ikarus))
     
     (let ()
     
       [module (one two)
         (define (one)       'one)
         (define (two)       'two)]
     
       [module red (f g)
         (define (f)         (cons 'f (one)))
         (define (g)         (cons 'g (two)))]
     
       (import red)
       (printf "calling red: ~s ~s~%" (f) (g)))

and the following example shows that the order of module definitions does not matter in determining visibility of bindings from anonymous modules:

     (import (ikarus))
     
     (let ()
     
       [module red (f g)
         (define (f)         (cons 'f (one)))
         (define (g)         (cons 'g (two)))]
     
       [module (one two)
         (define (one)       'one)
         (define (two)       'two)]
     
       (import red)
       (printf "calling red: ~s ~s~%" (f) (g)))

the following is an error because it tries to import bindings from a named module before its definition:

     (import (ikarus))
     
     (let ()
     
       [module red (f g)
         (import green)
         (define (f)         (cons 'f (one)))
         (define (g)         (cons 'g (two)))]
     
       [module green (one two)
         (define (one)       'one)
         (define (two)       'two)]
     
       (import red)
       (printf "calling red: ~s ~s~%" (f) (g)))


Previous: ikaruslib modules examples named, Up: ikaruslib modules examples
3.7.1.3 Utilities examples

The following example shows how syntactic abstractions can be used to access definitions in a module without using the import form:

     (import (ikarus))
     
     (let ()
     
       (define-syntax in-module
         (syntax-rules ()
           [(_ ?module ?expr ...)
            (let () (import ?module) ?expr ...)]))
     
       [module red (one two)
         (define (one arg)   (cons 'red-one arg))
         (define (two arg)   (cons 'red-two arg))]
     
       [module green (one two)
         (define (one arg)   (cons 'green-one arg))
         (define (two arg)   (cons 'green-two arg))]
     
       (printf "calling ones: ~s ~s~%"
               [(in-module red one)   123]
               [(in-module green one) 456]))


Previous: ikaruslib modules examples, Up: ikaruslib modules

3.7.2 Modules programming interface

— Syntax: module interface definitions ... expressions ...
— Syntax: module name interface definitions ... expressions ...

Define a new local module. The first form defines an anonymous module, while the second form defines a named module called name, which must be a symbol.

interface is a list of symbols that select bindings from this module to be exported. Every listed symbol must be bound in this module's definitions, otherwise an error is raised.

definitions is a set of bindings like the one that may appear at the beginning of a let body, with the addition of import forms that may import bindings from libraries and other modules.

expressions is a set of expressions that is evaluated at module's definition time.

Modules “exist” only in the enclosing region, bindings exported from a module are not accessible outside of the enclosing region. Modules can be nested and import/export relations are possible according to the regions nesting hierarchy.

Bindings in the interface declared by anonymous modules are imported by default in the enclosing region: there is no need to use import. Bindings in the interface declared by named modules are visible only in regions that import the module.

definitions and expressions have no default access to the bindings in the enclosing region; only imported bindings are visible from the module.


Next: , Previous: ikaruslib modules, Up: ikaruslib

3.8 Parameters

Parameters in Ikarus2 are intended for customizing the behavior of a procedure during the dynamic execution of some piece of code. Parameters are first class entities (represented as procedures) that hold the parameter value. A parameter procedure accepts either zero or one argument. If given no arguments, it returns the current value of the parameter. If given a single argument, it must set the state to the value of the argument.

Parameters replace the older concept of using starred *global* customization variables. For example, instead of writing:

     (define *screen-width* 72)

and then mutating the variable *screen-width* with set!, we could wrap the variable *screen-width* with a screen-width parameter as follows:

     (define *screen-width* 72)
     (define screen-width
        (case-lambda
          [()  *screen-width*]
          [(x) (set! *screen-width* x)]))

The value of screen-width can now be passed as argument, returned as a value, and exported from libraries.

— Procedure: make-parameter x
— Procedure: make-parameter x f

As parameters are common in Ikarus, the procedure make-parameter is defined to model the common usage pattern of parameter construction.

(make-parameter x) constructs a parameter with x as the initial value. For example, the code above could be written succinctly as:

          (define screen-width (make-parameter 72))

(make-parameter x f) constructs a parameter which filters the assigned values through the procedure f. The initial value of the parameter is the result of calling (f x). Typical uses of the filter procedure include checking some constraints on the passed argument or converting it to a different data type. The screen-width parameter may be constructed more robustly as:

          (define screen-width
            (make-parameter 72
              (lambda (w)
                (assert (and (integer? w) (exact? w)))
                (max w 1))))

This definition ensures, through assert, that the argument passed is an exact integer. It also ensures, through max that the assigned value is always positive.

— Syntax: parameterize ([lhs* rhs*] ...) body body* ...

Parameters can be assigned to by simply calling the parameter procedure with a single argument. The parameterize syntax is used to set the value of a parameter within the dynamic extent of the body body* ... expressions.

The lhs* ... are expressions, each of which must evaluate to a parameter. Such parameters are not necessarily constructed by make-parameter—any procedure that follows the parameters protocol works.

The advantage of using parameterize over explicitly assigning to parameters (same argument applies to global variables) is that you're guaranteed that whenever control exits the body of a parameterize expression, the value of the parameter is reset back to what it was before the body expressions were entered. This is true even in the presence of call/cc, errors, and exceptions.

The following example shows how to set the text property of a terminal window. The parameter terminal-property sends an ANSI escape sequence to the terminal whenever the parameter value is changed. The use of terminal-property within parameterize changes the property before (display "RED!") is called and resets it back to normal when the body returns.

          (define terminal-property
            (make-parameter "0"
              (lambda (x)
                (display "\x1b;[")
                (display x)
                (display "m")
                x)))
          
          (display "Normal and ")
          (parameterize ([terminal-property "41;37"])
            (display "RED!"))
          (newline)


Next: , Previous: ikaruslib parameters, Up: ikaruslib

3.9 Gensyms

Gensym stands for generated symbol—a fresh symbol that is generated at run time and is guaranteed to be not eq? to any other symbol present in the system. Gensyms are useful in many applications including expanders, compilers, and interpreters when generating an arbitrary number of unique names is needed.

Ikarus is similar to Chez Scheme in that the readers (including the read procedure) and writers (including write and pretty-print) maintain the read/write invariance on gensyms. When a gensym is written to an output port, the system automatically generates a random unique identifier for the gensym. When the gensym is read back though the #{gensym} read syntax, a new gensym is not regenerated, but instead, it is looked up in the global symbol table.

A gensym's name is composed of two parts: a pretty string and a unique string. The Scheme procedure symbol->string returns the pretty string of the gensym and not its unique string. Gensyms are printed by default as #{pretty-string unique-string}.

— Procedure: gensym
— Procedure: gensym string
— Procedure: gensym symbol

Construct a new gensym. If passed no arguments, construct a gensym with no pretty name. The pretty name is constructed when and if the pretty name of the resulting gensym is needed. If gensym is passed a string, that string is used as the pretty name. If gensym is passed a symbol, the pretty name of the symbol is used as the pretty name of the returned gensym. See gensym-prefix and gensym-count for details.

          > (gensym)
          #{g0 |y0zf>GlFvcTJE0xw|}
          > (gensym)
          #{g1 |U%X&sF6kX!YC8LW=|}
          > (eq? (gensym) (gensym))
          #f

(gensym string) constructs a new gensym with string as its pretty name. Similarly, (gensym symbol) constructs a new gensym with the pretty name of symbol, if it has one, as its pretty name.

          > (gensym "foo")
          #{foo |>VgOllCM&$dSvRN=|}
          > (gensym 'foo)
          #{foo |!TqQLmtw2hoEYfU>|}
          > (gensym (gensym 'foo))
          #{foo |N2C>5O0>C?OROUBU|}
— Procedure: gensym? x

Return #t if its argument is a gensym, return #f otherwise.

          > (gensym? (gensym))
          #t
          > (gensym? 'foo)
          #f
          > (gensym? 12)
          #f
— Procedure: gensym->unique-string gensym

Return the unique name associated with the gensym argument.

          > (gensym->unique-string (gensym))
          "YukrolLMgP?%ElcR"
— Reader Syntax: #{gensym}
— Reader Syntax: #{unique-name}
— Reader Syntax: #{pretty-name unique-name}
— Reader Syntax: #:pretty-name

Ikarus's read and write procedures extend the lexical syntax of Scheme by the ability to read and write gensyms using one of these three forms.

#{unique-name} constructs, at read time, a gensym whose unique name is the one specified. If a gensym with the same unique name already exists in the system's symbol table, that gensym is returned.

          > '#{some-long-name}
          #{g0 |some-long-name|}
          > (gensym? '#{some-long-unique-name})
          #t
          > (eq? '#{another-unique-name} '#{another-unique-name})
          #t

The two–part #{pretty-name unique-name} gensym syntax is similar to the syntax shown above with the exception that if a new gensym is constructed (that is, if the gensym did not already exist in the symbol table), the pretty name of the constructed gensym is set to pretty-name.

          > '#{foo unique-identifier}
          #{foo |unique-identifier|}
          > '#{unique-identifier}
          #{foo |unique-identifier|}
          > '#{bar unique-identifier}
          #{foo |unique-identifier|}

The #:pretty-name form constructs, at read time, a gensym whose pretty name is pretty-name and whose unique name is fresh. This form guarantees that the resulting gensym is not eq? to any other symbol in the system.

          > '#:foo
          #{foo |j=qTGlEwS/Zlp2Dj|}
          > (eq? '#:foo '#:foo)
          #f

3.9.1 An example

The (rnrs syntax-case) library provides a generate-temporaries procedure, which takes a syntax object (representing a list of things) and returns a list of fresh identifiers. Using gensym, that procedure can be defined as follows:

     (define (generate-temporaries* stx)
       (syntax-case stx ()
         [(x* ...)
          (map (lambda (x)
                 (datum->syntax #'unimportant
                   (gensym
                     (if (identifier? x)
                         (syntax->datum x)
                         't))))
               #'(x* ...))]))

The above definition works by taking the input stx and destructuring it into the list of syntax objects x* .... The inner procedure maps each x into a new syntax object (constructed with datum->syntax). The datum is a gensym, whose name is the same name as x if x is an identifier, or the symbol t if x is not an identifier. The output of generate-temporaries* generates names similar to their input counterpart:

     > (print-gensym #f)
     > (generate-temporaries* #'(x y z 1 2))
     (#<syntax x> #<syntax y> #<syntax z> #<syntax t> #<syntax t>)


Next: , Previous: ikaruslib gensym, Up: ikaruslib

3.10 Printing

— Procedure: pretty-print datum
— Procedure: pretty-print datum output-port

The procedure pretty-print is intended for printing Scheme data, typically Scheme programs, in a format close to how a Scheme programmer would write it. Unlike write, which writes its input all in one line, pretty-print inserts spaces and new lines in order to produce more pleasant output.

          (define fact-code
            '(letrec ([fact (lambda (n)
                              (if (zero? n)
                                  1
                                (* n (fact (- n 1)))))])
               (fact 5)))
          
          > (pretty-print fact-code)
          (letrec ((fact
                    (lambda (n) (if (zero? n) 1 (* n (fact (- n 1)))))))
            (fact 5))

The second argument to pretty-print, if supplied, must be an output port. If not supplied, the current-output-port is used.

Limitations: As shown in the output above, the current implementation of pretty-print does not handle printing of square brackets properly.

— Parameter: pretty-width
— Parameter: pretty-width n

The parameter pretty-width controls the number of characters after which the pretty-print starts breaking long lines into multiple lines. The initial value of pretty-width is set to 60 characters, which is suitable for most terminals and printed material.

          > (parameterize ([pretty-width 40])
               (pretty-print fact-code))
          (letrec ((fact
                    (lambda (n)
                      (if (zero? n)
                          1
                          (* n (fact (- n 1)))))))
            (fact 5))

Note that pretty-width does not guarantee that the output will not extend beyond the specified number. Very long symbols, for examples, cannot be split into multiple lines and may force the printer to go beyond the value of pretty-width.

— Procedure: format fmt-string args ...

The procedure format produces a string formatted according to fmt-string and the supplied arguments. The format string contains markers in which the string representation of each argument is placed. The markers include:

~s
instructs the formatter to place the next argument as if the procedure write has printed it; if the argument contains a string, the string will be quoted and all quotes and backslashes in the string will be escaped; similarly, characters will be printed using the #\x notation;
~a
instructs the formatter to place the next argument as if the procedure display has printed it; strings and characters are placed as they are in the output;
~b
instructs the formatter to convert the next argument to its binary (base 2) representation; the argument must be an exact number;
~o
is similar to ~b except that the number is printed in octal (base 8);
~x
is similar to ~b except that the number is printed in hexadecimal (base 16);
~d
outputs the next argument, which can be an exact or inexact number, in its decimal (base 10) representation;
~~
instructs the formatter to place a tilde character, ~, in the output without consuming an argument;
~%
instructs the formatter to place a newline character in the output without consuming an argument.

Note that the #b, #o, and #x numeric prefixes are not added to the output when ~b, ~o, and ~x are used.

          > (format "message: ~s, ~s, and ~s" 'symbol "string" #\c)
          "message: symbol, \"string\", and #\\c"
          
          > (format "message: ~a, ~a, and ~a" 'symbol "string" #\c)
          "message: symbol, string, and c"
— Procedure: printf fmt-string args ...

The procedure printf is similar to format except that the output is sent to the current-output-port instead of being collected in a string.

          > (let ([n (+ (expt 2 32) #b11001)])
              (printf "~d = #b~b = #x~x\n" n n n))
          4294967321 = #b100000000000000000000000000011001 = #x100000019
— Procedure: fprintf output-port fmt-string args ...

The procedure fprintf is similar to printf except that the output port to which the output is sent is specified as the first argument.

— Parameter: print-graph
— Parameter: print-graph #t
— Parameter: print-graph #f

The graph notation is a way of marking and referencing parts of a data structure and, consequently, creating shared and cyclic data structures at read time instead of resorting to explicit mutation at run time. The #n= marks the following data structure with mark n, where n is a nonnegative integer. The #n# references the data structure marked n. Marks can be assigned and referenced in any order but each mark must be assigned to exactly once in an expression.

          > (let ([x '#0=(1 2 3)])
              (eq? x '#0#))
          #t
          > (let ([x '#0#] [y '#0=(1 2 3)])
              (eq? x y))
          #t
          > (eq? (cdr '(12 . #1#)) '#1=(1 2 3))
          #t
          > (let ([x '#1=(#1# . #1#)])
              (and (eq? x (car x))
                   (eq? x (cdr x))))
          #t

The print-graph parameter controls how the writers (e.g. pretty-print and write) handle shared and cyclic data structures. In Ikarus, all writers detect cyclic data structures and they all terminate on all input, cyclic or otherwise.

If the value of print-graph is set to #f (the default), then the writers do not attempt to detect shared data structures. Any part of the input that is shared is printed as if no sharing is present. If the value of print-graph is set to #t, all sharing of data structures is marked using the #n= and #n# notation.

          > (parameterize ([print-graph #f])
              (let ([x (list 1 2 3 4)])
                (pretty-print (list x x x))))
          ((1 2 3 4) (1 2 3 4) (1 2 3 4))
          
          > (parameterize ([print-graph #t])
              (let ([x (list 1 2 3 4)])
                (pretty-print (list x x x))))
          (#0=(1 2 3 4) #0# #0#)
          
          > (parameterize ([print-graph #f])
              (let ([x (list 1 2)])
                (let ([y (list x x x x)])
                  (set-car! (last-pair y) y)
                  (pretty-print (list y y)))))
          (#0=((1 2) (1 2) (1 2) #0#) #0#)
          
          > (parameterize ([print-graph #t])
              (let ([x (list 1 2)])
                (let ([y (list x x x x)])
                  (set-car! (last-pair y) y)
                  (pretty-print (list y y)))))
          (#0=(#1=(1 2) #1# #1# #0#) #0#)
— Parameter: print-unicode
— Parameter: print-unicode #t
— Parameter: print-unicode #f

Unstable.

— Parameter: print-gensym
— Parameter: print-gensym #t
— Parameter: print-gensym #f
— Parameter: print-gensym 'pretty

The parameter print-gensym controls how gensyms are printed by the various writers.

If the value of print-gensym is #f, then gensym syntax is suppressed by the writers and only the gensyms' pretty names are printed. If the value of print-gensym is #t, then the full #{pretty unique} syntax is printed. Finally, if the value of print-gensym is the symbol pretty, then gensyms are printed using the #:pretty notation.

          > (parameterize ([print-gensym #f])
              (pretty-print (list (gensym) (gensym))))
          (g0 g1)
          
          > (parameterize ([print-gensym #t])
              (pretty-print (list (gensym) (gensym))))
          (#{g2 |KR1M2&CTt1<B0n/m|} #{g3 |FBAb&7NC6&=c82!O|})
          
          > (parameterize ([print-gensym 'pretty])
              (pretty-print (list (gensym) (gensym))))
          (#:g4 #:g5)

The initial value of print-gensym is #t.

— Parameter: gensym-prefix
— Parameter: gensym-prefix string

The parameter gensym-prefix specifies the string to be used as the prefix to generated pretty names. The default value of gensym-prefix is the string g, which causes generated strings to have pretty names in the sequence g0, g1, g2, etc.

          > (parameterize ([gensym-prefix "var"]
                           [print-gensym #f])
               (pretty-print (list (gensym) (gensym) (gensym))))
          (var0 var1 var2)

Beware that the gensym-prefix controls how pretty names are generated, and has nothing to do with how gensym constructs a new gensym. In particular, notice the difference between the output in the first example with the output of the examples below:

          > (pretty-print
              (parameterize ([gensym-prefix "var"] [print-gensym #f])
                (list (gensym) (gensym) (gensym))))
          (g3 g4 g5)
          
          > (let ([ls (list (gensym) (gensym) (gensym))])
              (parameterize ([gensym-prefix "var"] [print-gensym #f])
                (pretty-print ls)))
          (var5 var6 var7)
— Parameter: gensym-count
— Parameter: gensym-count n

The parameter gensym-count determines the number which is attached to the gensym-prefix when gensyms' pretty names are generated. The initial value of gensym-count is 0 and is incremented every time a pretty name is generated. It might be set to any non-negative integer value.

          > (let ([x (gensym)])
              (parameterize ([gensym-count 100] [print-gensym #f])
                (pretty-print (list (gensym) x (gensym)))))
          (g100 g101 g102)

Notice from all the examples so far that pretty names are generated in the order at which the gensyms are printed, not in the order in which gensyms were created.


Next: , Previous: ikaruslib printing, Up: ikaruslib

3.11 Tracing

— Syntax: trace-define (name . args) body body* ...
— Syntax: trace-define name expression

The trace-define syntax is similar to define except that the bound value, which must be a procedure, becomes a traced procedure. A traced procedure prints its arguments when it is called and prints its values when it returns.

          > (trace-define (fact n)
              (if (zero? n) 1 (* n (fact (- n 1)))))
          > (fact 5)
          |(fact 5)
          | (fact 4)
          | |(fact 3)
          | | (fact 2)
          | | |(fact 1)
          | | | (fact 0)
          | | | 1
          | | |1
          | | 2
          | |6
          | 24
          |120
          120

The tracing facility in Ikarus preserves and shows tail recursion and distinguishes it from non–tail recursion by showing tail calls starting at the same line in which their parent was called.

          > (trace-define (fact n)
              (trace-define (fact-aux n m)
                (if (zero? n) m (fact-aux (- n 1) (* n m))))
              (fact-aux n 1))
          > (fact 5)
          |(fact 5)
          |(fact-aux 5 1)
          |(fact-aux 4 5)
          |(fact-aux 3 20)
          |(fact-aux 2 60)
          |(fact-aux 1 120)
          |(fact-aux 0 120)
          |120
          120

Moreover, the tracing facility interacts well with continuations and exceptions.

          > (call/cc
              (lambda (k)
                (trace-define (loop n)
                  (if (zero? n)
                      (k 'done)
                      (+ (loop (- n 1)) 1)))
                (loop 5)))
          |(loop 5)
          | (loop 4)
          | |(loop 3)
          | | (loop 2)
          | | |(loop 1)
          | | | (loop 0)
          done
— Syntax: trace-lambda name args body body* ...

The trace-lambda macro is similar to lambda except that the resulting procedure is traced: it prints the arguments it receives and the results it returns.

— Procedure: make-traced-procedure name proc

The procedure make-traced-procedure takes a name (typically a symbol) and a procedure. It returns a procedure similar to proc except that it traces its arguments and values.

          > (define (fact n)
              (if (zero? n)
                  (lambda (k) (k 1))
                  (lambda (k)
                    ((fact (- n 1))
                     (make-traced-procedure `(k ,n)
                       (lambda (v)
                         (k (* v n))))))))
          > (call/cc
              (lambda (k)
                ((fact 5) (make-traced-procedure 'K k))))
          |((k 1) 1)
          |((k 2) 1)
          |((k 3) 2)
          |((k 4) 6)
          |((k 5) 24)
          |(K 120)
          120


Next: , Previous: ikaruslib tracing, Up: ikaruslib

3.12 Timing

This section describes some of Ikarus's timing facilities which may be useful for benchmarking and performance tuning.

— Syntax: time expression

The time macro performs the following: it evaluates expression, then prints a summary of the run time statistics, then returns the values returned by expression. The run–time summary includes the number of bytes allocated, the number of garbage collection runs, and the time spent in both the mutator and the collector.

          > (let ()                                   ;;; 10 million
              (define ls (time (vector->list (make-vector 10000000))))
              (time (append ls ls))
              (values))
          running stats for (vector->list (make-vector 10000000)):
              3 collections
              672 ms elapsed cpu time, including 547 ms collecting
              674 ms elapsed real time, including 549 ms collecting
              120012328 bytes allocated
          running stats for (append ls ls):
              4 collections
              1536 ms elapsed cpu time, including 1336 ms collecting
              1538 ms elapsed real time, including 1337 ms collecting
              160000040 bytes allocated
Note: The output listed above is just a sample that was taken at some point on some machine. The output on your machine at the time you read this may vary.

— Procedure: time-it who thunk

The procedure time-it takes a datum denoting the name of the computation and a thunk (i.e. a procedure with no arguments), invokes the thunk, prints the stats, and returns the values obtained from invoking the thunk. If the value of who is non–false, who is used when displaying the run–time statistics. If the value of who is #f, then no name for the computation is displayed.

          > (time-it "a very fast computation"
              (lambda () (values 1 2 3)))
          running stats for a very fast computation:
              no collections
              0 ms elapsed cpu time, including 0 ms collecting
              0 ms elapsed real time, including 0 ms collecting
              24 bytes allocated
          1
          2
          3
          
          > (time-it #f (lambda () 12))
          running stats:
              no collections
              0 ms elapsed cpu time, including 0 ms collecting
              0 ms elapsed real time, including 0 ms collecting
              0 bytes allocated
          12


Next: , Previous: ikaruslib timing, Up: ikaruslib

3.13 Guardians and garbage collection

Guardians are available in the (ikarus) library; quoting [DYBGUA]:

Guardians provide a means to protect objects from destruction by the garbage collector. A guardian is an object with which objects can be registered for preservation and from which objects actually saved from destruction can be retrieved, one at a time, at the convenience of the program.
— Function: collect

Run the garbage collector. If post–garbage collection hooks are registered, they are run. This binding is exported by the (ikarus) library.


Next: , Up: ikaruslib guardians

3.13.1 Usage examples for guardians

Let's say that we use the Ikarus FFI to handle some memory block (ikaruslib foreign for details on the FFI); memory blocks allocated with malloc are not released by the Ikarus garbage collector: we have to explicitly apply free to the pointer value referencing them.

If we use the blocks synchronously with the evaluation of forms, we do (assuming we do not use continuations, so this use of dynamic-wind is fine):

     (import (ikarus)
       (ikarus foreign))
     
     (define (do-something-with . args)
       (display args)
       (newline))
     
     (let ((p        #f)
           (size     4096))
       (dynamic-wind
           (lambda ()
             (set! p (malloc size))
             (unless p (error #f "memory allocation")))
           (lambda ()
             (do-something-with p))
           (lambda ()
             (free p))))

and we can define a syntax for it:

     (import (ikarus)
       (ikarus foreign))
     
     (define (do-something-with . args)
       (display args)
       (newline))
     
     (define-syntax with-block
       (syntax-rules ()
         ((_ ?pointer ?size ?body ...)
          (let ((?pointer    #f))
            (dynamic-wind
                (lambda ()
                  (set! ?pointer (malloc ?size))
                  (unless ?pointer (error #f "memory allocation")))
                (lambda ()
                  ?body ...)
                (lambda ()
                  (free ?pointer)))))))
     
     (with-block p 2048
       (do-something-with p))
     
     (with-block p 4096
       (do-something-with p))
     
     (with-block p 8192
       (do-something-with p))

If we need the block in an event driven program: we will probably need to use it asynchronously with the evaluation of forms. For example, we store the pointer value that references a block in a thunk (a closure that takes no arguments):

     (import (ikarus)
       (ikarus foreign))
     
     (define *event-source* '())
     
     (define (enqueue-event event)
       (set! *event-source*
             (reverse (cons event (reverse *event-source*)))))
     
     (define (pop-event)
       (if (null? *event-source*)
           #f
         (let ((event (car *event-source*)))
           (set! *event-source* (cdr *event-source*))
           event)))
     
     ;; Usage:
     
     (define (do-something-with . args)
       (display args)
       (newline))
     
     (let ((p (malloc 4096)))
       (unless p (error #f "memory allocation"))
       (enqueue-event (lambda ()
                        (do-something-with p)))
       (enqueue-event (lambda ()
                        (do-something-with 123)))
       (enqueue-event (lambda ()
                        (do-something-with p))))
     
     (do ((event (pop-event) (pop-event)))
         ((not event))
       (event))

once the thunks have been evaluated, the pointer value is garbage collected, but the allocated memory block becomes leaked memory. We need a way to be notified of the pointer value garbage collection, so that we can apply free to it; this is the job for guardians.

The following script shows the usage of a guardian to free a memory block:

     (import (ikarus)
       (ikarus foreign))
     
     (define g (make-guardian))
     
     (let ((a (malloc (expt 2 20))))
       (unless p (error #f "memory allocation"))
       (g a)
       (printf "value ~s~%" a))
     
     (printf "value from guardian ~s~%" (g))
     
     ;; This triggers a garbage collection.
     (collect)
     
     (let ((p (g)))
       (printf "value from guardian ~s~%" p)
       (free p))

In an event driven program what we have to do is to register the pointer into the guardian, and then periodically enqueue as event a call to the guardian:

     (import (ikarus)
       (ikarus foreign))
     
     ;; Event source handling.
     
     (define *event-source* '())
     
     (define (enqueue-event event)
       (set! *event-source*
             (reverse (cons event (reverse *event-source*)))))
     
     (define (pop-event)
       (if (null? *event-source*)
           #f
         (let ((event (car *event-source*)))
           (set! *event-source* (cdr *event-source*))
           event)))
     
     ;; Block guardian.
     
     (define block-guardian (make-guardian))
     
     (define (run-block-guardian)
       (do ((p (block-guardian) (block-guardian)))
           ((not p))
         (printf "collecting ~s~%" p)
         (free p)))
     
     ;; Application follows.
     
     (define (do-something-with . args)
       (display args)
       (newline))
     
     (let ((p (malloc (expt 2 20))))
       (unless p (error #f "memory allocation"))
       (block-guardian p)
       (enqueue-event (lambda () (do-something-with p)))
       (enqueue-event (lambda () (do-something-with 123)))
       (enqueue-event (lambda () (do-something-with p)))
       (enqueue-event (lambda () (do-something-with 456))))
     
     (do ((event (pop-event) (pop-event))
          (i 1 (+ i 1)))
         ((= i 20))
       (when event
         (event))
       (when (= 0 (modulo i 10))
         (collect)
         (enqueue-event run-block-guardian)))


Previous: ikaruslib guardians examples, Up: ikaruslib guardians

3.13.2 Guardians programming interface

— Function: make-guardian

Build and return a new guardian object: a closure that can be called with one or zero arguments, and that is interfaced with the garbage collector of the Scheme implementation (in a non–portable way).

The guardian works like this:

After an object has been returned by the guardian, it is no more “guarded” so it will be garbage collected when it will become inaccessible (unsless it is registered in a guardian).

Note: it is possible to register an object in more than one guardian, when the object is found inaccessible all the guardian closures that have it will return it to the caller. This may lead to invalid finalisation operations, it is the responsibility of the programmer to avoid this.
Note: it is possible to register an object multiple times in the same guardian, when the object is found inaccessible the guardian closure that has it will return it to the caller multiple times. This may lead to invalid finalisation operations, it is the responsibility of the programmer to avoid this.

— Parameter: post-gc-hooks

Hold a list of thunks evaluated after each garbage collection run. It can be used to run a guardian cleanup function like this (using the example in the previous section):

          (define (run-block-guardian)
            (do ((p (block-guardian) (block-guardian)))
                ((not p))
              (free p)))
          
          (post-gc-hooks
           (cons run-block-guardian
            (post-gc-hooks)))


Next: , Previous: ikaruslib guardians, Up: ikaruslib

3.14 Input/output library

Undocumented feature: this feature is still undocumented in Ikarus revision 1661, so it may change in the future.


Next: , Up: ikaruslib io

3.14.1 Spawning processes

Undocumented feature: this feature is still undocumented in Ikarus revision 1661, so it may change in the future.
— Function: process command . options

Run a subprocess with a set of command line options. command is a string selecting the executable pathname. options is a list of strings interpreted as command line options.

If command does not not contain a / character, the command is searched in the directories listed in the PATH environment variable.

Return multiple values:

  1. the subprocess pid;
  2. an output port that writes to the subprocess' standard input channel;
  3. an input port that reads from the subprocess' standard output channel;
  4. an input port that reads from the subprocess' standard error channel.

If an error occurs: an exception is raised.

Example:

          (call-with-values
              (lambda ()
                (process "ls" "-l"))
            (lambda (pid stdin-out stdout-in stderr-in)
              (do-something)))

with receive:

          (receive (pid stdin-out stdout-in stderr-in)
              (process "ls" "-l")
            (do-something))

with let-values:

          (let-values ([(pid stdin-out stdout-in stderr-in)
                        (process "ls" "-l")])
            (do-something))
Unix: process makes use of the fork() and execvp() system functions.

Creating a Process
Executing a File
Standard Environment Variables

— Function: process-nonblocking command . options

Like process but the returned ports have their underlying file descriptors configured to non–blocking mode (ikaruslib io non-block).


Next: , Previous: ikaruslib io process, Up: ikaruslib io

3.14.2 Using network sockets

Undocumented feature: this feature is still undocumented in Ikarus revision 1661, so it may change in the future.


Next: , Up: ikaruslib io socket
3.14.2.1 Establishing client network connections
Undocumented feature: this feature is still undocumented in Ikarus revision 1661, so it may change in the future.
— Function: tcp-connect hostname service
— Function: udp-connect hostname service
— Function: tcp-connect-nonblocking hostname service
— Function: udp-connect-nonblocking hostname service

Establish a network connection using the TCP or UDP protocol, to the host selected by the string hostname, port/service selected by the string service. The -nonblocking version sets the underlying socket descriptor to non–blocking mode (ikaruslib io non-block). Upon success, return a port; if an error occurs an exception is raised.

hostname can be a dotted decimal address for IPv4, or a hexadecimal string for IPv6, or a network address.

service can be a numerical string representing the port, or the “official” name of a service, like smtp or pop3.

Unix: These functions use of the socket(), connect() and getaddrinfo() system functions.

Creating a Socket
Making a Connection
See the getaddrinfo() manual page.
See the /etc/services configuration file.


Previous: ikaruslib io socket client, Up: ikaruslib io socket
3.14.2.2 Opening server network services
Undocumented feature: this feature is still undocumented in Ikarus revision 1661, so it may change in the future.
— Function: tcp-server-socket server-port-number
— Function: tcp-server-socket-nonblocking server-port-number

Build a new TCP server object for the selected port number (which must be an exact integer). The -nonblocking versions set the underlying socket descriptor to non–blocking mode (ikaruslib io non-block). Upon success, return a server–object; if an error occurs an exception is raised.

Unix: These functions make use of the socket(), setsockopt(), bind() and listen() system functions. The socket is configured with the AF_INET, INADDR_ANY, SO_REUSEADDR values and options (Ikarus version 0.0.3+, checkout 1468).

Creating a Socket
Socket Option Functions
Setting the Address of a Socket
Listening for Connections

— Function: accept-connection server-object
— Function: accept-connection-nonblocking server-object

Accept the next incoming connection to the selected server. If success return two values:

  1. an output port to be used to send data;
  2. an input port to be used to receive data;

if an error occurs: an exception is raised. The -nonblocking versions set the underlying socket descriptor to non–blocking mode (ikaruslib io non-block).

If the server socket was configured as blocking: this function suspends the process waiting for an incoming connection.

FIXME: What happens when the server socket is configured as non–blocking?
Unix: These functions make use of the accept() system call.

Accepting Connections

— Function: close-tcp-server-socket server-object

Close a server socket causing it to stop listening.

Unix: this function makes use of the shutdown() system function.

Closing a Socket See also the shutdown() manual page.

— Function: register-callback server-object proc

Register a callback function to be invoked whenever a new incoming connection request happens on server-object. The callback will be invoked by the Ikarus event loop.

FIXME: What are the parameters that get passed to proc? Look for make-t and t-proc in ikarus.io.ss.

— Parameter: input-socket-buffer-size

Select the size in bytes of the input buffer for server sockets. The value must be a fixnum greater or equal to 128.

— Parameter: output-socket-buffer-size

Select the size in bytes of the output buffer for server sockets. The value must be a fixnum greater than zero.


Previous: ikaruslib io socket, Up: ikaruslib io

3.14.3 Non–blocking mode for ports

Unix: Non–blocking mode is setup with a call like:

     fcntl(fd, F_SETFL, O_NONBLOCK);

Control Operations
Open-time Flags


Next: , Previous: ikaruslib io, Up: ikaruslib

3.15 POSIX functions

Undocumented feature: this feature is still undocumented in Ikarus revision 1661, so it may change in the future.


Next: , Up: ikaruslib posix

3.15.1 Spawning processes and the like

Undocumented feature: this feature is still undocumented in Ikarus revision 1661, so it may change in the future.
— Function: fork parent-proc child-thunk

Fork a new process using the system fork() function.

If the operation is successful: in the parent process parent-proc is evaluated by applying it to an exact integer representing the child pid; in the child process child-thunk is evaluated.

If an error occurs: an exception is raised.

Example:

          (fork
            (lambda (child-pid)
              (printf "in parent, child pid = ~s\n" child-pid))
            (lambda ()
              (printf "in child\n")
              (exit)))
          
          (printf "here we are in the parent\n")

Creating a Process

— Function: system command-string

Executes a command using the system() function, which uses the system shell. If success: return the exit code of the process. If error: raise an exception.

Example:

          (system "ls -l")

Running a Command


Next: , Previous: ikaruslib posix process, Up: ikaruslib posix

3.15.2 Waiting for terminated children

Undocumented feature: this feature is still undocumented in Ikarus revision 1661, so it may change in the future.
— Function: waitpid [pid [block? [want-error?]]]

Wait for another process termination using the system waitpid() function and return informations about its status.

If successful and there is a child waiting to be noticed: the returned value is a record of type wstatus, whose interface is described below. If successful but no child is waiting to be noticed: the returned value is #f.

If an error occurs and want-error? is #t, an exception is raised. If an error occurs and want-error? is #f: the return value is #f. want-error? defaults to #t.

pid is the id of the process to wait for; it defaults to -1. If it is:

a positive integer
return informations about the process having that pid;
0
return informations about a process in the set of terminated children in the same group of the calling process; if more eligible children exists, one is selected at random;
-1
return informations about a process in the set of terminated children; if more eligible children exists, one is selected at random;
a negative integer other than -1
wait for a process in the set of terminated children whose process group id is equal to the absolute value of pid.

If block? is #t (the default), then the process is suspended waiting for an eligible child; if block? is #f the process is not suspended and the return value is #f.

Process Completion

The following example tests waitpid:

     (fork
        (lambda (child-pid)
          (printf "in parent, child pid = ~s\n" child-pid)
          (let ([status (waitpid child-pid)])
            (printf "in parent, child status: ~s\n" status)))
        (lambda ()
          (printf "in child\n")
          (exit 1)))

the following example test killing with a signal:

     (fork
        (lambda (child-pid)
          (printf "in parent, child pid = ~s\n" child-pid)
          (kill child-pid 'SIGKILL)
          (let ([status (waitpid child-pid)])
            (printf "in parent, child status: ~s\n" status)))
        (lambda ()
          (printf "in child\n")
          (nanosleep 5 0)))
Status record interface
— Function: wstatus-pid status

Return the pid of the process extracting it from status.

— Function: wstatus-exit-status status

Return the exit code of the process extracting it from status. If the process was stopped by a signal: the return value is #f.

— Function: wstatus-received-signal status

Return a symbol representing the signal that caused the termination of the child process. If no signal was received: return #f.


Next: , Previous: ikaruslib posix waitpid, Up: ikaruslib posix

3.15.3 Delivering signals to processes

Undocumented feature: this feature is still undocumented in Ikarus revision 1661, so it may change in the future.
— Function: kill pid signame

Send the signal selected by signame to the process or group of processes selected by pid. Make use of the system kill() function. If an error occurs: an exception is raised.

Following is the meaning of pid:

pid > 0
deliver the signal to the process having pid as identifier;
pid == 0
deliver the signal to all the process in the group of the sender;
pid == -1
deliver the signal to all the process having the same effective user identifier of the sender; see the whole documentation in the system library;
pid < -1
deliver the signal to all the process in the group having identifier equal to the absolute value of pid.

signame must be a symbol specifying a signal. Supported signal names follow:

          SIGABRT         SIGALRM         SIGBUS
          SIGCHLD         SIGCONT         SIGFPE
          SIGHUP          SIGILL          SIGINT
          SIGKILL         SIGPIPE         SIGQUIT
          SIGSEGV         SIGSTOP         SIGTERM
          SIGTSTP         SIGTTIN         SIGTTOU
          SIGUSR1         SIGUSR2         SIGPOLL
          SIGPROF         SIGSYS          SIGTRAP
          SIGURG          SIGVTALRM       SIGXCPU
          SIGXFSZ

Signaling Another Process
Standard Signals


Next: , Previous: ikaruslib posix signal, Up: ikaruslib posix

3.15.4 Interfacing with the file system

Undocumented feature: this feature is still undocumented in Ikarus revision 1661, so it may change in the future.
Current working directory
— Function: current-directory [pathname]

When invoked with an argument, change the current working directory to the one selected by the string pathname. Make use of the system chdir() function.

When invoked without argument, return a string representing the current working directory pathname. Make use of the system getcwd() function.

If an error occurs, an exception is raised.

Working Directory

Inspecting the file system
— Function: directory-list pathname

Build and return the list of strings representing the entries in the directory selected by the string pathname. Make use of the opendir(), readdir(), closedir() system functions.

If an error occurs, an exception is raised.

Opening a Directory
Reading/Closing Directory

— Function: file-ctime pathname

Return the time of the last modification to the attributes of the file; makes use of the stat() system function, and return the st_ctime field of the stat data structure.

The returned value is an exact integer, representing the number of seconds elapsed since the Epoch (00:00:00 on January 1, 1970, Coordinated Universal Time).

If an error occurs, an exception is raised.

Reading Attributes

Inspecting file types

For the following functions: if follow is #t, the default, make use of the stat() system function; if follow is #f, make use of the lstat() system function.

Reading Attributes

— Function: file-exists? pathname [follow]

Return #t if pathname exists on the file system; otherwise return #f. If an error occurs, an exception is raised.

— Function: file-regular? pathname [follow]

Return #t if pathname exists on the file system and it is a regular file; otherwise return #f. If an error occurs, an exception is raised.

— Function: file-directory? pathname [follow]

Return #t if pathname exists on the file system and it is a directory; otherwise return #f. If an error occurs, an exception is raised.

— Function: file-symbolic-link? pathname [follow]

Return #t if pathname exists on the file system and it is a symbolic link; otherwise return #f. If an error occurs, an exception is raised.

Creation
— Function: make-symbolic-link to pathname

Create a symbolic link whose pathname is the string pathname referencing the to string pathname. Make use of the symlink() system function. If an error occurs, an exception is raised.

Symbolic Links

— Function: make-directory pathname [mode]

Create a directory with name selected by the string pathname. Make use of the mkdir() system function. If an error occurs, an exception is raised.

mode must be an exact integere selecting the access permissions for the directory; it defaults to ‘#o755’.

Creating Directories

Deletion
— Function: delete-file pathname

Remove from the system the file selected by the pathname string. Make use of the unlink() system function. If an error occurs, an exception is raised.

Deleting Files

— Function: delete-directory pathname

Remove from the system the directory selected by the pathname string. Make use of the rmdir() system function. If an error occurs, an exception is raised.

Deleting Files

Permissions
— Function: change-mode pathname mode

Change the access permissions of the file or directory selected by the pathname string. Make use of the chmod() system function. If an error occurs, an exception is raised.

mode must be an exact integer selecting the permissions.

Setting Permissions


Next: , Previous: ikaruslib posix file, Up: ikaruslib posix

3.15.5 Interfacing with the execution environment

Undocumented feature: this feature is still undocumented in Ikarus revision 1661, so it may change in the future.
— Function: getenv varname

Return the current value, as a string, of the system environment variable selected by the string varname. If the variable is not set: #f is returned.

Example:

          (getenv "PATH")
          ⇒ "/usr/local/bin:/usr/bin:/bin"


Previous: ikaruslib posix env, Up: ikaruslib posix

3.15.6 Miscellaneous functions

Undocumented feature: this feature is still undocumented in Ikarus revision 1661, so it may change in the future.
— Function: strerror error-code

Return a string describing the errno code error-code. Makes use of the system function strerror(). If error-code is not a valid errno value: return a string telling it.

Error Messages

— Function: nanosleep seconds nanoseconds

Suspend the process for the selected time; it makes use of the system nanosleep() function. seconds and nanoseconds must be positive exact integers. If an error occurs an exception is raised.

Sleeping


Next: , Previous: ikaruslib posix, Up: ikaruslib

3.16 The (ikarus foreign) library

This chapter describes the facilities through which Ikarus interfaces with the host operating system and other external libraries. The facilities of the (ikarus foreign) library give the Scheme program unrestricted access to the computer memory, allowing one to allocate, access, modify, and free memory as needed. The facilities also allow the Scheme program to call out to system procedures as well as allow the native procedures to call back into Scheme.

Note: Ikarus version 0.0.4 is the first version of Ikarus to support the described foreign interfaces.
Caveat emptor: Preparing each call out and call back procedure leaks a small amount of memory. This is because the system cannot track such pointers that go into native code (which may retain such pointers indefinitely). Use judiciously.


Next: , Up: ikaruslib foreign

3.16.1 Overview of the foreign functions interface

In order to make full use of the computer, it is important for a programming environment (e.g., Ikarus Scheme) to facilitate access to the underlying architecture on which it runs. The underlying architecture includes the API provided by the host operating system kernel (e.g., Linux), the system libraries (e.g., libc), and other site–installed libraries (e.g., sqlite3).

Providing direct access to such API from within Scheme allows the programmer to write Scheme libraries that have few or no dependencies on external programs (such as C development toolchain). When dealing with system libraries, the programmer must have a thorough understanding of many aspects of the targeted system. This section attempts to provide answers to many questions that are frequently encountered when interfacing to external libraries.

Libffi

Currently (Thu Oct 23, 2008) Ikarus implements the foreign functions interface (FFI) using an extenal library: Libffi, originally by Anthony Green. Libffi can be found at:

http://sourceware.org/libffi/

and it is distributed under a liberal license (look for it at the site, basically we can do everything but remove the original copyright notice).

On Unix–like systems, we can install Libffi with the traditional sequence:

     $ ./configure [options]
     $ make
     $ make install

and the makefile supports the DESTDIR environment variable for installation under a different directory prefix.

Note: Libffi version 3.0.6 installs its header files under:
     ${prefix}/lib/libffi-3.0.6/include

and trying to use the --includedir option to configure will not work in changing this. It means that when configuring Ikarus for installation we have to specify where the Libffi headers are to be found; overview install for details.


Next: , Previous: ikaruslib foreign overview, Up: ikaruslib foreign

3.16.2 Memory management

Ikarus Scheme is a managed environment. Like in many programming environments, Ikarus manages its own memory. Scheme objects are allocated in a special memory region (the Scheme heap) and have type–specific object layouts; this allows the run time system to distinguish object types and the garbage collector to locate all potentially live objects, and reclaim the memory of dead objects. Scheme objects are also opaque in the sense that the data structures used to represent Scheme objects (e.g., pairs) are not exposed to the programmer, who can only interact with objects through an interface (e.g., car, cdr).

Unmanaged environments, such as the operating system on which Ikarus runs, require that the programmer manages the allocation and deallocation of system resources herself. Memory regions, file handles, external devices, the screen, etc., are all examples of resources whose management must be coordinated among the different parts of the system, and this becomes the responsibility of the programmer who is wiring the different subsystems together.

Memory, from a system's point of view, is transparent. A pointer is an integer denoting an address of memory. This memory address may contain a value that requires interpretation. At the lowest–level, each byte of memory contains eight bits, each of which may be toggled on or off. A level higher, contiguous sequences of bytes are grouped together and are interpreted as integers, floating point numbers, or pointers to other memory addresses. These are the basic data types that are often interpreted atomically. Yet a level higher, groups of basic types form data structures such as arrays, linked lists, trees, and so on. Objects, as found in object–oriented programming languages, are at an even higher level of abstraction since they are treated as opaque references that retain state and know how to respond to messages.

The procedures in the (ikarus foreign) library are meant to provide a way to interface with the low level memory operations such as setting and getting bytes from specific locations in memory. Although they do not provide high–level operations, the basic procedures make implementing high–level operations (such as the Objective–C system) possible. Programmers are encouraged to define their own abstractions that are most suitable for the specific target library rather than using the low–level operations directly. This results in writing more robust and more easily maintainable libraries. To put it more boldly: Do not sprinkle your code with low–level memory operations.


Next: , Previous: ikaruslib foreign memory, Up: ikaruslib foreign

3.16.3 Memory operations


Next: , Up: ikaruslib foreign memops
3.16.3.1 Allocating and freeing memory
— Procedure: malloc number-of-bytes

Allocate a block of memory and return a pointer to it. The malloc Scheme procedure is implemented using the host–provided malloc() system procedure (often found in libc). The number of bytes must be a positive exact integer.

          > (malloc 10)
          #<pointer #x00300320>
          > (malloc 10000)
          #<pointer #x01800400>

In case of failure allocating memory: the return value is #f.

— Procedure: free pointer-to-memory-block

Release the memory region at the given address. The memory region must have been allocated with malloc(), calloc(), or a similar system procedure. Once freed, memory operations on the given address are invalid and may cause the system to crash at unpredictable times. Ikarus cannot check for such errors since the memory may be freed by procedures that are external to Ikarus.


Next: , Previous: ikaruslib foreign memops alloc, Up: ikaruslib foreign memops
3.16.3.2 Handling pointer values

The result of calling the procedures eq?, eqv? and equal? on pointer values is unspecified.

— Procedure: pointer->integer pointer

Convert the value of pointer to an exact integer value. The result may be a fixnum or a bignum depending on the pointer.

— Procedure: integer->pointer exact-integer

Convert exact-integer to a pointer value. The lower 32 bits (or 64 bits on 64-bit systems) of the argument are significant in computing the pointer value. It is guaranteed that (integer->pointer (pointer->integer p)) points to the same address as p.

— Procedure: pointer? x

Return #t if the value of x is a pointer, return #f otherwise.

— Procedure: pointer-size

Return the number of bytes required to hold a pointer on the underlying platform.


Next: , Previous: ikaruslib foreign memops pointer, Up: ikaruslib foreign memops
3.16.3.3 Poking values

With all the following functions: the pointer argument must be a valid pointer; the offset and value arguments must be exact integers. When adding an offset to a pointer: all the following functions do not scale the offset to the size of the poked value. Pointer arithmetics is performed with byte offsets.

Poking exact integers
— Procedure: pointer-set-c-char! pointer offset value

Set to value the single byte of memory located at offset from pointer. Only the 8 lowermost bits of value are used in the operation and the remaining bits are ignored.

— Procedure: pointer-set-c-short! pointer offset value

Set to value the two bytes of memory located at offset from pointer. Only the 16 lowermost bits of value are used in the operation and the remaining bits are ignored.

— Procedure: pointer-set-c-int! pointer offset value

Set to value the four bytes of memory located at offset from pointer. Only the 32 lowermost bits of value are used in the operation and the remaining bits are ignored.

— Procedure: pointer-set-c-long! pointer offset value

On 64-bit systems: Set to value the eight bytes of memory located at offset from pointer. Only the 64 lowermost bits of value are used in the operation and the remaining bits are ignored.

On 32-bit systems: perform the same task as pointer-set-c-int!.

— Procedure: pointer-set-c-long-long! pointer offset value

Set to value a number of bytes corresponding to the long long type located at offset from pointer.

Poking floating point numbers
— Procedure: pointer-set-c-float! pointer offset flonum

Convert the Scheme floating point number flonum (represented in Ikarus as an IEEE 754 double precision floating point number) to a float (an IEEE 754 single precision floating point number) and store the result in the four bytes at offset from pointer.

— Procedure: pointer-set-c-double! pointer offset flonum

Store the double precision IEEE 754 floating point value of the Scheme flonum in the eight bytes at offset from pointer.

Poking pointers
— Procedure: pointer-set-c-pointer! pointer offset value

On 64-bit systems: set the eight bytes at offset from pointer to the 64-bit pointer value.

On 32-bit systems: set the four bytes at offset from pointer to the 32-bit pointer value.


Previous: ikaruslib foreign memops poke, Up: ikaruslib foreign memops
3.16.3.4 Peeking values

With all the following functions: the pointer argument must be a valid pointer; the offset argument must be an exact integer. When adding an offset to a pointer: all the following functions do not scale the offset to the size of the poked value. Pointer arithmetics is performed with byte offsets.

Peeking exact integers
— Procedure: pointer-ref-c-signed-char pointer offset

Peek the single byte located at offset from pointer and return an exact integer representing the sign–extended integer value of that byte. The resulting value is in the inclusive range [-128, 127].

— Procedure: pointer-ref-c-unsigned-char pointer offset

Peek the single byte located at offset from pointer and return an exact integer representing the unsigned integer value of that byte. The resulting value is in the inclusive range [0, 255].

The following example shows the difference between the two functions above:

     > (let ([p (malloc 3)])
         (pointer-set-c-char! p 0 #b01111111)
         (pointer-set-c-char! p 1 #b10000000)
         (pointer-set-c-char! p 2 #b11111111)
         (let ([result
                (list (pointer-ref-c-signed-char p 0)
                      (pointer-ref-c-signed-char p 1)
                      (pointer-ref-c-signed-char p 2)
                      (pointer-ref-c-unsigned-char p 0)
                      (pointer-ref-c-unsigned-char p 1)
                      (pointer-ref-c-unsigned-char p 2))])
           (free p)
           result))
     (127 -128 -1 127 128 255)
— Procedure: pointer-ref-c-signed-short pointer offset

Peek two bytes at offset from pointer and return an exact integer representing the sign–extended integer value of the sequence. The resulting value is in the inclusive range [-32768, 32767].

— Procedure: pointer-ref-c-unsigned-short pointer offset

Peek two bytes at offset from pointer and return an exact integer representing the unsigned integer value of the sequence. The resulting value is in the inclusive range [0, 65535].

— Procedure: pointer-ref-c-signed-int pointer offset

Peek four bytes at offset from pointer and return an exact integer in the inclusive range [-2^31, 2^31-1].

— Procedure: pointer-ref-c-unsigned-int pointer offset

Peek four bytes at offset from pointer and return an exact integer in the inclusive range [0, 2^32-1].

— Procedure: pointer-ref-c-signed-long pointer offset

On 64-bit systems: peek eight bytes at offset from pointer and return an integer in the inclusive range [-2^63, 2^63-1].

On 32-bit systems, perform the same task as pointer-ref-c-signed-int.

— Procedure: pointer-ref-c-unsigned-long pointer offset

On 64-bit systems: peek eight bytes at offset from pointer and return an integer in the inclusive range [0, 2^64-1].

On 32-bit systems: perform the same task as pointer-ref-c-unsigned-int.

— Procedure: pointer-ref-c-signed-long-long pointer offset

Peek a number of bytes corresponding to the native long long type at offset from pointer and return an integer.

— Procedure: pointer-ref-c-unsigned-long-long pointer offset

Peek a number of bytes corresponding to the native unsigned long long type at offset from pointer and return an integer.

Peeking floating point numbers
— Procedure: pointer-ref-c-float pointer offset

Return the four–byte float (represented as IEEE 754 single precision floating point number) stored at offset from pointer. The value is extended to an IEEE 754 double precision floating point number that Ikarus uses to represent inexact numbers.

— Procedure: pointer-ref-c-double pointer offset

Return the eight–byte float (represented as IEEE 754 double precision floating point number) stored at offset from pointer.

Peeking pointers
— Procedure: pointer-ref-c-pointer pointer offset

Return the pointer stored at offset from pointer. The size of the pointer (also the number of bytes loaded) depends on the architecture: it is 4 bytes on 32-bit systems and 8 bytes on 64-bit systems.


Next: , Previous: ikaruslib foreign memops, Up: ikaruslib foreign

3.16.4 Accessing foreign objects from Scheme

— Procedure: dlopen
— Procedure: dlopen library-name
— Procedure: dlopen library-name lazy? global?

Take the string library-name representing a system library and call the system procedure dlopen() which dynamically loads the given library into the running process.

The name of the library is system–dependent and must include the appropriate suffix (e.g. *.so on Linux, *.dylib on Darwin and *.dll on Cygwin). library-name may include a full path which identifies the location of the library, or it may just be the name of the library in which case the system will lookup the library name using the LD_LIBRARY_PATH environment variable.

The argument lazy? specifies how library dependencies are loaded. If true, dlopen delays the resolution and loading of dependent libraries until they are actually used. If false, all library dependencies are loaded before the call to dlopen returns.

The argument global? specifies the scope of the symbols exported from the loaded library. If true, all exported symbols become part of the running image, and subsequent dlsym calls may not need to specify the library from which the symbol is loaded. If false, the exported symbols are not global and the library pointer needs to be specified for dlsym.

Calling (dlopen library-name) is equivalent to (dlopen library-name #f #f). Calling (dlopen) without arguments returns a pointer to the current process.

If succesful, dlopen returns a pointer to the external library which can be used subsequently by dlsym and dlclose. If the library cannot be loaded, dlopen returns #f and the procedure dlerror can be used to obtain the cause of the failure.

Consult the dlopen(3) page in your system manual for further details.

— Procedure: dlclose library-pointer

A wrapper for the system procedure of the same name. It receives a library pointer (e.g. one obtained from dlopen) and releases the resources loaded from that library. Closing a library renders all symbols and static data structures that the library exports invalid and the program may crash or corrupt its memory if such symbols are used after a library is closed.

Most system implementations of dynamic loading employ reference counting for dlopen and dlclose in that library resources are not freed until the number of calls to dlclose matches the number of calls to dlopen.

The procedure dlclose returns a boolean value indicating whether the success status of the operation. If dlclose returns #f, the procedure dlerror can be used to obtain the cause of the error.

Consult the dlclose(3) page in your system manual for further details.

— Procedure: dlsym library-pointer string

Take a library pointer (e.g. one obtained by a call to dlopen) and a string representing the name of a symbol that the library exports and return a pointer to the location of that symbol in memory. If dlsym fails, it returns #f and the cause of the error can be obtained using the procedure dlerror.

Consult the dlsym(3) page in your system manual for further details.

— Procedure: dlerror

If any of the dynamic loading operations (i.e., dlopen, dlclose, dlsym) fails, the cause of the error can be obtained by calling dlerror which returns a string describing the error. The procedure dlerror returns #f if there was no dynamic loading error.

Consult the dlerror(3) page in your system manual for further details.


Next: , Previous: ikaruslib foreign dl, Up: ikaruslib foreign

3.16.5 Calling out to foreign procedures

Ikarus provides the means to call out from Scheme to foreign procedures. This allows the programmers to extend Ikarus to access system–specific facilities that are available on the host machine.


Next: , Up: ikaruslib foreign call out
3.16.5.1 Interface to foreign functions

In order to call out to a foreign procedure, one must provide two pieces of information: the signature of the foreign procedure (e.g. its type declaration if it is a C language procedure) and the address of the procedure in memory. The address of the procedure can be easily obtained using dlsym if the name of the procedure and its exporting library are known. The signature of the procedure cannot, in general, be obtained dynamically, and therefore must be hard coded into the program.

The signature of the foreign procedure is required for proper linkage between the Scheme system and the foreign system. Using the signature, Ikarus determines how Scheme values are converted into native values, and where (e.g. in which registers and stack slots) to put these arguments. The signature also determines where the returned values are placed and how they are converted from the system data types to the corresponding Scheme data types.

A procedure's signature is composed of two parts: the return type and the parameter types:

the symbol void can appear as a return type but cannot appear as a parameter type.

— Procedure: make-c-callout return-type parameter-types

The procedure make-c-callout is the primary facility for making foreign procedures callable from Scheme. It works as follows:

  1. make-c-callout receives two arguments denoting the signature of the procedure to be called; it prepares a bridge that converts from Scheme's calling conventions and data structures to their foreign counterparts; it returns a procedure p1;
  2. the procedure p1 accepts a pointer to a foreign procedure (e.g. one obtained from dlsym) and returns a Scheme procedure p2 that encapsulates the foreign procedure;
  3. the final procedure p2 can be called with as many arguments as the ones specified in the parameter-types; the parameters supplied to p2 must match the types supplied as the parameter-types according to the “Valid Scheme types” column in the table in ikaruslib foreign call out types;
  4. the procedure p2 converts the parameters from Scheme types to native types, calls the foreign procedure, obtains the result, and converts it to the appropriate Scheme value (depending on the return-type).

The interface of make-c-callout is broken down into three stages in order to accomodate common usage patterns. Often a function signature can be used by many foreign procedures and therefore, make-c-callout can be called once per signature and each signature can be used multiple times. Similarly, separating the foreign procedure preparation from parameter passing allows for preparing the foreign procedure once and calling it many times.

The list of types in the table in ikaruslib foreign call out types is restricted to basics and provides no automatic conversion from composite Scheme data structures (such as strings, symbols, vectors, and lists) to native types. The restriction is intentional in order for Ikarus to avoid making invalid assumptions about the memory management of the target library.

For example, while Ikarus can convert a Scheme string to a native byte array (e.g. use string->bytevector to decode the string, then use malloc to allocate a temporary buffer, then copy the bytes from the bytevector to the allocated memory), it cannot decide when this allocated byte array is no longer needed and should be freed. This knowledge is library–dependent and is often procedure–dependent. Therefore, Ikarus leaves it to the programmer to manage all memory related issues.

Outgoing parameters to foreign procedures are checked against the declared types. For example, if a callback is prepared to expect a parameter of type signed-int, only exact integers are allowed to be passed out. For integer types, only a fixed number of bits is used and the remaining bits are ignored. For floating point types, the argument is checked to be a Scheme flonum. No implicit conversion between exact and inexact numbers is performed.


Next: , Previous: ikaruslib foreign call out api, Up: ikaruslib foreign call out
3.16.5.2 Type specifiers

The following table lists valid type specifiers that can be used in callout and callback signatures. Specifiers with “4/8 bytes” have size that depends on the system: it is 4 bytes on 32-bit systems and 8 bytes on 64-bit systems. The void specifier can only be used as a return value specifier to mean “no useful value is returned”.

Type specifier Size Valid Scheme types Corresponding C types
signed-char 1 byte exact integer char
unsigned-char 1 byte exact integer unsigned char
signed-short 2 bytes exact integer short
unsigned-short 2 bytes exact integer unsigned short
signed-int 4 bytes exact integer int
unsigned-int 4 bytes exact integer unsigned int
signed-long 4/8 bytes exact integer long
unsigned-long 4/8 bytes exact integer unsigned long
float 4 bytes flonum float
double 8 bytes flonum double
pointer 4/8 bytes pointer void*, char*, int*, int**, etc
void void


Previous: ikaruslib foreign call out types, Up: ikaruslib foreign call out
3.16.5.3 C language call out example

The following example illustrates the use of the make-c-callout procedure in combination with dlopen and dlsym; the session was run on a 32-bit Ikarus running under Mac OS X 10.4:

  1. the libc.dylib foreign library is loaded and is bound to the variable libc;
  2. we obtain a pointer to the atan() foreign procedure that is defined in libc; the native procedure atan() takes a double as an argument and returns a double and that's the signature that we use for make-c-callout;
  3. we call the foreign procedure interface with one argument, 1.0, which is a flonum and thus matches the required parameter type; the native procedure returns a double value which is converted to the Scheme flonum with value 0.7853981633974483.
     > (import (ikarus foreign))
     > (define libc (dlopen "libc.dylib"))
     > libc
     #<pointer #x00100770>
     > (define libc-atan-ptr (dlsym libc "atan"))
     > libc-atan-ptr
     #<pointer #x9006CB1F>
     > (define libc-atan
         ((make-c-callout 'double '(double)) libc-atan-ptr))
     > libc-atan
     #<procedure>
     > (libc-atan 1.0)
     0.7853981633974483
     > (libc-atan 1)
     Unhandled exception
      Condition components:
         1. &assertion
         2. &who: callout-procedure
         3. &message: "argument does not match type double"
         4. &irritants: (1)


Previous: ikaruslib foreign call out, Up: ikaruslib foreign

3.16.6 Calling back to Scheme

In order to provide full interoperability with native procedures, Ikarus allows native procedures to call back into Scheme just as it allows Scheme to call out to native procedures. This is important for many system libraries that provide graphical user interfaces with event handling (e.g. Cocoa, GTK+, GLUT, etc.), database engines (e.g. libsqlite, libmysql, etc.), among others.

The native calling site for the call back is compiled with a specific callback signature encoding the expected parameter types and return type. Therefore, a Scheme procedure used for a call back must be wrapped with a proper adapter that converts the incoming parameters from native format to Scheme values as well as convert the value that the Scheme procedure returns back to native format. The signature format is similar to the one used for call outs (ikaruslib foreign call out types for details).

— Procedure: make-c-callback return-type parameter-types

The procedure make-c-callback is similar to the procedure make-c-callout except that it provides a bridge from native procedures back into Scheme. While the procedure make-c-callout takes a native pointer and returns a Scheme procedure, make-c-callback takes a Scheme procedure and returns a native pointer. The native pointer can be called by foreign procedures. The native parameters are converted to Scheme data (according to parameter-types), the Scheme procedure is called with these parameters, and the returned value is converted back into native format (according to return-type) before control returns to the native call site.

Note that the native procedure pointer obtained from make-c-callback is indistinguishable from other native procedures that are obtained using dlsym or similar means. In particular, such native pointers can be passed to make-c-callout resulting in a Scheme procedure that calls out to the native procedure that in turn calls back into Scheme. The following segment illustrates a very inefficient way of extracting the lowermost 32 bits from an exact integer.

          > (format "~x"
              (((make-c-callout 'unsigned-int '(unsigned-int))
                ((make-c-callback 'unsigned-int '(unsigned-int))
                 values))
               #xfedcba09876543210fedcba09876543210))
          "76543210"


Previous: ikaruslib foreign, Up: ikaruslib

3.17 Miscellaneous functions

— Function: void

Return the void value. It is the value returned by forms that do not return a meaningful value.


Next: , Previous: ikaruslib, Up: Top

Appendix A Accessing development revisions

Ikarus development is accessible at the site:

https://launchpad.net/ikarus

where we can find the latest release tarball. For the development revisions, we can use the Bazaar revision control system:

http://bazaar-vcs.org/

Bazaar is written in Python, so you have to install Python, too.

To checkout the latest revision we do:

     $ bzr checkout --lightweight \
           http://bazaar.launchpad.net/~aghuloum/ikarus/ikarus.dev

or simply:

     $ bzr checkout --lightweight lp:ikarus

this will put the source tree under the ikarus.dev local directory. To checkout a specific revison, say 1700:

     $ bzr checkout --lightweight --revision=1700 lp:ikarus

and to check it out into the ikarus-1700 local directory:

     $ bzr checkout --lightweight --revision=1700 lp:ikarus ikarus-1700

the command bzr help shows a little help screen for Bazaar; the command bzr checkout --help shows a little help screen for the checkout Bazaar subcommand; the command bzr log prints the revision logs, and we can use it to read the number of the last checkin.

Notice that it is not mandatory to actually install, or even build, Bazaar on our system, it is enough to unpack its release archive in a directory; with Bazaar (at least) version 1.1 after unpacking we have a functional bzr executable in the top directory of the source tree, which we can use by typing its pathname:
     $ cd /usr/local/src
     $ tar xvzf bzr-1.1.tar.gz
     $ ./bzr-1.1/bzr checkout ...


Next: , Previous: devel, Up: Top

Appendix B Missing Features

Ikarus does not fully conform to R6RS yet. Although it implements most of R6RS's macros and procedures, some are still missing. This appendix summarizes the set of missing features and procedures.


Next: , Previous: missing, Up: Top

Appendix C Credits for this document

Skeleton

The skeleton of this document is the original “Ikarus User's Guide” in XeLaTeX format, by Abdulaziz Ghulom. That document is available in the Ikarus distribution and is covered by the GNU General Public License version 3 as published by the Free Software Foundation. It is available at the following URLs:

http://www.cs.indiana.edu/~aghuloum/ikarus/
https://launchpad.net/ikarus


Next: , Previous: credits, Up: Top

Appendix D GNU General Public License

Version 3, 29 June 2007
     Copyright © 2007 Free Software Foundation, Inc. http://fsf.org/
     
     Everyone is permitted to copy and distribute verbatim copies of this
     license document, but changing it is not allowed.

Preamble

The GNU General Public License is a free, copyleft license for software and other kinds of works.

The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program—to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too.

When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things.

To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others.

For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights.

Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it.

For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions.

Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users.

Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free.

The precise terms and conditions for copying, distribution and modification follow.

TERMS AND CONDITIONS

  1. Definitions.

    “This License” refers to version 3 of the GNU General Public License.

    “Copyright” also means copyright-like laws that apply to other kinds of works, such as semiconductor masks.

    “The Program” refers to any copyrightable work licensed under this License. Each licensee is addressed as “you”. “Licensees” and “recipients” may be individuals or organizations.

    To “modify” a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a “modified version” of the earlier work or a work “based on” the earlier work.

    A “covered work” means either the unmodified Program or a work based on the Program.

    To “propagate” a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well.

    To “convey” a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying.

    An interactive user interface displays “Appropriate Legal Notices” to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent

  2. in the list meets this criterion.
  3. Source Code.

    The “source code” for a work means the preferred form of the work for making modifications to it. “Object code” means any non-source form of a work.

    A “Standard Interface” means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language.

    The “System Libraries” of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A “Major Component”, in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it.

    The “Corresponding Source” for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work.

    The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source.

    The Corresponding Source for a work in source code form is that same work.

  4. Basic Permissions.

    All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law.

    You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you.

    Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary.

  5. Protecting Users' Legal Rights From Anti-Circumvention Law.

    No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures.

    When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures.

  6. Conveying Verbatim Copies.

    You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program.

    You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee.

  7. Conveying Modified Source Versions.

    You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions:

    1. The work must carry prominent notices stating that you modified it, and giving a relevant date.
    2. The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to “keep intact all notices”.
    3. You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it.
    4. If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so.

    A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an “aggregate” if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate.

  8. Conveying Non-Source Forms.

    You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways:

    1. Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange.
    2. Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge.
    3. Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b.
    4. Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements.
    5. Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d.

    A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work.

    A “User Product” is either (1) a “consumer product”, which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, “normally used” refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product.

    “Installation Information” for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made.

    If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM).

    The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network.

    Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying.

  9. Additional Terms.

    “Additional permissions” are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions.

    When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission.

    Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms:

    1. Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or
    2. Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or
    3. Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or
    4. Limiting the use for publicity purposes of names of licensors or authors of the material; or
    5. Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or
    6. Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors.

    All other non-permissive additional terms are considered “further restrictions” within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying.

    If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms.

    Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way.

  10. Termination.

    You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11).

    However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation.

    Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice.

    Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10.

  11. Acceptance Not Required for Having Copies.

    You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so.

  12. Automatic Licensing of Downstream Recipients.

    Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License.

    An “entity transaction” is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts.

    You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it.

  13. Patents.

    A “contributor” is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's “contributor version”.

    A contributor's “essential patent claims” are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, “control” includes the right to grant patent sublicenses in a manner consistent with the requirements of this License.

    Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version.

    In the following three paragraphs, a “patent license” is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To “grant” such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party.

    If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. “Knowingly relying” means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid.

    If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it.

    A patent license is “discriminatory” if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007.

    Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law.

  14. No Surrender of Others' Freedom.

    If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program.

  15. Use with the GNU Affero General Public License.

    Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such.

  16. Revised Versions of this License.

    The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns.

    Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License “or any later version” applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation.

    If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program.

    Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version.

  17. Disclaimer of Warranty.

    THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM “AS IS” WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.

  18. Limitation of Liability.

    IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.

  19. Interpretation of Sections 15 and 16.

    If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee.

END OF TERMS AND CONDITIONS

How to Apply These Terms to Your New Programs

If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms.

To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the “copyright” line and a pointer to where the full notice is found.

     one line to give the program's name and a brief idea of what it does.
     Copyright (C) year name of author
     
     This program is free software: you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
     the Free Software Foundation, either version 3 of the License, or (at
     your option) any later version.
     
     This program is distributed in the hope that it will be useful, but
     WITHOUT ANY WARRANTY; without even the implied warranty of
     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     General Public License for more details.
     
     You should have received a copy of the GNU General Public License
     along with this program.  If not, see http://www.gnu.org/licenses/.

Also add information on how to contact you by electronic and paper mail.

If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode:

     program Copyright (C) year name of author
     This program comes with ABSOLUTELY NO WARRANTY; for details type ‘show w’.
     This is free software, and you are welcome to redistribute it
     under certain conditions; type ‘show c’ for details.

The hypothetical commands ‘show w’ and ‘show c’ should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an “about box”.

You should also get your employer (if you work as a programmer) or school, if any, to sign a “copyright disclaimer” for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see http://www.gnu.org/licenses/.

The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read http://www.gnu.org/philosophy/why-not-lgpl.html.


Next: , Previous: package license, Up: Top

Appendix E Bibliography and references

Revised^6 Report on the Algorithmic Language Scheme. Michael Sperber and R. Kent Dybvig and Matthew Flatt and Anton Van Straaten (Editors). 2007.

Revised^6 Report on the Algorithmic Language Scheme—Standard Libraries. Michael Sperber and R. Kent Dybvig and Matthew Flatt and Anton Van Straaten (Editors). 2007.

Scheme Request for Implementation 41: Streams. Philip L. Bewig. 2007. http://srfi.schemers.org/srfi-41/srfi-41.html

[DYBGUA] Guardians in a generation–based garbage collector. R. Kent Dybvig and Carl Bruggeman and David Eby. PLDI '93, pages 207–216, June 1993. Introduces guardians and describes the implementation of guardians and weak pairs in Chez Scheme.

Don't stop the BiBOP: Flexible and efficient storage management for dynamically–typed languages. R. Kent Dybvig and David Eby and Carl Bruggeman. Technical Report 400, Indiana University, March 1994. Describes Chez Scheme's storage management system.

Generation Friendly Eq Hash Tables. Abdulaziz Ghuloum and R. Kent Dybvig. In Proceedings of the 2007 Workshop on Scheme and Functional Programming, pages 207–216. Universite Laval Technical Report DIUL-RT-0701, 2007.

Extending the Scope of Syntactic Abstraction. Oscar Waddell. PhD thesis, Indiana University Computer Science Department, August 1999. http://www.cs.indiana.edu/~owaddell/papers/thesis.ps.gz

Extending the Scope of Syntactic Abstraction. Oscar Waddell and R. Kent Dybvig. In Conference Record of POPL'99: The 26th ACM SIGPLAN-SIGACT Symposium on Principles of Programming Languages, pages 203–213, January 1999. http://www.cs.indiana.edu/~dyb/papers/popl99.ps.gz

Chez Scheme Version 7 User's Guide. R. Kent Dybvig. Cadence Research Systems, 2005. User's guide and reference manual for Chez Scheme Version 7. http://www.scheme.com/csug7/

Implicit phasing for R6RS libraries. Abdulaziz Ghuloum and R. Kent Dybvig. In ICFP '07: Proceedings of the 2007 ACM SIGPLAN international conference on Functional programming, pages 303–314. New York, NY, USA, 2007. ACM.


Next: , Previous: references, Up: Top

Appendix F An entry for each concept


Next: , Previous: concept index, Up: Top

Appendix G An entry for each function.


Next: , Previous: function index, Up: Top

Appendix H An entry for each variable.


Next: , Previous: variable index, Up: Top

Appendix I An entry for each type.


Footnotes

[1] The configure command shown here is very imposing, and anyone can easily make mistakes. In general, it's a good idea to create a shell script that has the sequence of configure and make commands needed to configure, build, and install a package; this not only allows you to re–install easily, but also gives you a concrete indication of what options you used to build the software the last time. Of course, you should put this script somewhere other than in the actual Ikarus software directory!

[2] Parameters are found in many Scheme implementations such as Chez Scheme and MzScheme.