(ikarus foreign)
library
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:
or:
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
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.
Ikarus Scheme provides the programmer with many advantages:
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:
gobject-introspection
making accessible the Gtk
programming interface at the Scheme level.
http://rotty.yi.org/software/sbank/.
Developers of other extension libraries are encouraged to contact us to have their libraries listed here.
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.
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.
Ikarus is tested under the following operating systems; in general, any later version of one of these systems should be usable:
You will need some additional libraries and tools to build Ikarus.
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
, 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.
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.
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.
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.
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
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.
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.
If you downloaded a distribution, unpack it. From the shell prompt:
$ tar -zxf ikarus-n.n.n.tar.gz
this creates the base directory ikarus-n.n.n.
ikarus.dev
. No unpacking is needed.
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.
libffi
.
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:
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.
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
Once Ikarus is properly installed, you can invoke it either in an interactive terminal session or as the interpreter for invoking a script.
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.
The ikarus executable recognizes a few command line switches that influence how Ikarus starts.
ikarus -h
ikarus -b path/to/boot/file.boot
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 ...]
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 ...]
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.
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.
.command
extension, then it can be executed from the Finder by double–clicking
on it. This brings up a terminal window in which the script is
executed. The .command
extension can be hidden from the
Get Info item from the Finder's File menu.
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.
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.
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:
(sample)
causes Ikarus to look in each directory on the
path for a file named sample.sls.
(yoyodyne sample)
causes Ikarus to look in each
directory on the path for a directory named yoyodyne
, and within
that a file named sample.sls. (The library
form for this
library must still specify the library name as (yoyodyne sample)
,
not (sample)
.)
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.
(lib)
,
and additional optional extensions named (lib extras)
and
(lib frills)
. Although the previous packaging method can be
used, Ikarus allows you to use a simpler technique where the main
library is stored in lib/main.sls, and the extensions are stored
in lib/extras.sls and lib/frills.sls, respectively.
Ikarus attempts to import the library (a b c)
as follows, for
each directory in the search path:
/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.
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
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.
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
Ikarus extends Scheme's lexical syntax (R6RS Chapter 4) in a variety of ways including:
- end–of–file marker,
#!eof
;- gensym syntax,
#{gensym}
;- graph syntax,
#nn= #nn#
.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.
Accept an input port as argument and return a symbol among
r6rs-mode
andikarus-mode
as result. All input ports start underikarus-mode
and thus accept Ikarus–specific reader extensions. When the#!r6rs
token is read from a port, its mode changes toikarus-mode
.> (port-mode (current-input-port)) ikarus-mode > #!r6rs (port-mode (current-input-port)) r6rs-mode > #!ikarus (port-mode (current-input-port)) ikarus-mode
Modifiy the lexical syntax accepted by subsequent calls to
read
on the input port. The mode is a symbol amongr6rs-mode
andikarus-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
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
andget-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 $
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. Ifnew-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
andconsole-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.
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.
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.
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
.
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 localimport
form is similar to theimport
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.
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 themodule
andimport
keywords. Ikarus's internal module system is similar in spirit to that of Chez Scheme.
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.
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)))
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]))
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 ofimport
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 thatimport
the module.definitions and expressions have no default access to the bindings in the enclosing region; only imported bindings are visible from the module.
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.
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. Thescreen-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, throughmax
that the assigned value is always positive.
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 thebody body* ...
expressions.The
lhs* ...
are expressions, each of which must evaluate to a parameter. Such parameters are not necessarily constructed bymake-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 aparameterize
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 ofcall/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 ofterminal-property
withinparameterize
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)
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}
.
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. Ifgensym
is passed a symbol, the pretty name of the symbol is used as the pretty name of the returned gensym. Seegensym-prefix
andgensym-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|}
Return
#t
if its argument is a gensym, return#f
otherwise.> (gensym? (gensym)) #t > (gensym? 'foo) #f > (gensym? 12) #f
Return the unique name associated with the gensym argument.
> (gensym->unique-string (gensym)) "YukrolLMgP?%ElcR"
Ikarus's
read
andwrite
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}) #tThe 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 topretty-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 ispretty-name
and whose unique name is fresh. This form guarantees that the resulting gensym is noteq?
to any other symbol in the system.> '#:foo #{foo |j=qTGlEwS/Zlp2Dj|} > (eq? '#:foo '#:foo) #f
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>)
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. Unlikewrite
, 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, thecurrent-output-port
is used.Limitations: As shown in the output above, the current implementation ofpretty-print
does not handle printing of square brackets properly.
The parameter
pretty-width
controls the number of characters after which thepretty-print
starts breaking long lines into multiple lines. The initial value ofpretty-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 ofpretty-width
.
The procedure
format
produces a string formatted according tofmt-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"
The procedure
printf
is similar toformat
except that the output is sent to thecurrent-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
The procedure
fprintf
is similar toprintf
except that the output port to which the output is sent is specified as the first argument.
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)))) #tThe
print-graph
parameter controls how the writers (e.g.pretty-print
andwrite
) 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 ofprint-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#)
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 ofprint-gensym
is#t
, then the full#{pretty unique}
syntax is printed. Finally, if the value ofprint-gensym
is the symbolpretty
, 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
.
The parameter
gensym-prefix
specifies the string to be used as the prefix to generated pretty names. The default value ofgensym-prefix
is the stringg
, which causes generated strings to have pretty names in the sequenceg0
,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 howgensym
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)
The parameter
gensym-count
determines the number which is attached to thegensym-prefix
when gensyms' pretty names are generated. The initial value ofgensym-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.
The
trace-define
syntax is similar todefine
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 120The 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 120Moreover, 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
The
trace-lambda
macro is similar tolambda
except that the resulting procedure is traced: it prints the arguments it receives and the results it returns.
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
This section describes some of Ikarus's timing facilities which may be useful for benchmarking and performance tuning.
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 allocatedNote: 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.
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
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.
Run the garbage collector. If post–garbage collection hooks are registered, they are run. This binding is exported by the
(ikarus)
library.
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)))
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:
- when the closure is called with one argument: it adds the argument to an internal set of “guarded values”, accessible to the garbage collector;
- when the garbage collector detects no other references to a value in the set of guarded values: the guardian removes it from the set and appends it to an internal queue (FIFO);
- when the closure is called with zero arguments: it removes the first value from the internal queue and returns it; if no values are in the queue it returns
#f
.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.
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)))
Undocumented feature: this feature is still undocumented in Ikarus revision 1661, so it may change in the future.
Undocumented feature: this feature is still undocumented in Ikarus revision 1661, so it may change in the future.
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:
- the subprocess pid;
- an output port that writes to the subprocess' standard input channel;
- an input port that reads from the subprocess' standard output channel;
- 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 thefork()
andexecvp()
system functions.
Creating a Process
Executing a File
Standard Environment Variables
Like
process
but the returned ports have their underlying file descriptors configured to non–blocking mode (ikaruslib io non-block).
Undocumented feature: this feature is still undocumented in Ikarus revision 1661, so it may change in the future.
Undocumented feature: this feature is still undocumented in Ikarus revision 1661, so it may change in the future.
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
orpop3
.Unix: These functions use of thesocket()
,connect()
andgetaddrinfo()
system functions.Creating a Socket
Making a Connection
See thegetaddrinfo()
manual page.
See the /etc/services configuration file.
Undocumented feature: this feature is still undocumented in Ikarus revision 1661, so it may change in the future.
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 thesocket()
,setsockopt()
,bind()
andlisten()
system functions. The socket is configured with theAF_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
Accept the next incoming connection to the selected server. If success return two values:
- an output port to be used to send data;
- 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 theaccept()
system call.
Close a server socket causing it to stop listening.
Unix: this function makes use of theshutdown()
system function.Closing a Socket See also the
shutdown()
manual page.
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 formake-t
andt-proc
in ikarus.io.ss.
Select the size in bytes of the input buffer for server sockets. The value must be a fixnum greater or equal to 128.
Select the size in bytes of the output buffer for server sockets. The value must be a fixnum greater than zero.
Unix: Non–blocking mode is setup with a call like:
fcntl(fd, F_SETFL, O_NONBLOCK);
Control Operations
Open-time Flags
Undocumented feature: this feature is still undocumented in Ikarus revision 1661, so it may change in the future.
Undocumented feature: this feature is still undocumented in Ikarus revision 1661, so it may change in the future.
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")
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")
Undocumented feature: this feature is still undocumented in Ikarus revision 1661, so it may change in the future.
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
.
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)))
Return the exit code of the process extracting it from status. If the process was stopped by a signal: the return value is
#f
.
Return a symbol representing the signal that caused the termination of the child process. If no signal was received: return
#f
.
Undocumented feature: this feature is still undocumented in Ikarus revision 1661, so it may change in the future.
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
Undocumented feature: this feature is still undocumented in Ikarus revision 1661, so it may change in the future.
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.
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.
Return the time of the last modification to the attributes of the file; makes use of the
stat()
system function, and return thest_ctime
field of thestat
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.
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.
Return
#t
if pathname exists on the file system; otherwise return#f
. If an error occurs, an exception is raised.
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.
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.
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.
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.
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’.
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.
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.
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.
Undocumented feature: this feature is still undocumented in Ikarus revision 1661, so it may change in the future.
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"
Undocumented feature: this feature is still undocumented in Ikarus revision 1661, so it may change in the future.
Return a string describing the
errno
code error-code. Makes use of the system functionstrerror()
. If error-code is not a validerrno
value: return a string telling it.
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.
(ikarus foreign)
libraryThis 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.
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.
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:
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/includeand 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.
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.
Allocate a block of memory and return a pointer to it. The
malloc
Scheme procedure is implemented using the host–providedmalloc()
system procedure (often found inlibc
). 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
.
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.
The result of calling the procedures eq?
, eqv?
and
equal?
on pointer values is unspecified.
Convert the value of pointer to an exact integer value. The result may be a fixnum or a bignum depending on the pointer.
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.
Return the number of bytes required to hold a pointer on the underlying platform.
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.
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.
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.
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.
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!
.
Set to value a number of bytes corresponding to the
long long
type located at offset from pointer.
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.
Store the double precision IEEE 754 floating point value of the Scheme flonum in the eight bytes at offset from pointer.
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.
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.
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]
.
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)
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]
.
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]
.
Peek four bytes at offset from pointer and return an exact integer in the inclusive range
[-2^31, 2^31-1]
.
Peek four bytes at offset from pointer and return an exact integer in the inclusive range
[0, 2^32-1]
.
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
.
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
.
Peek a number of bytes corresponding to the native
long long
type at offset from pointer and return an integer.
Peek a number of bytes corresponding to the native
unsigned long long
type at offset from pointer and return an integer.
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.
Return the eight–byte float (represented as IEEE 754 double precision floating point number) stored at offset from pointer.
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.
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 todlopen
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 subsequentdlsym
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 fordlsym
.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 bydlsym
anddlclose
. If the library cannot be loaded,dlopen
returns#f
and the proceduredlerror
can be used to obtain the cause of the failure.Consult the
dlopen(3)
page in your system manual for further details.
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
anddlclose
in that library resources are not freed until the number of calls todlclose
matches the number of calls todlopen
.The procedure
dlclose
returns a boolean value indicating whether the success status of the operation. Ifdlclose
returns#f
, the proceduredlerror
can be used to obtain the cause of the error.Consult the
dlclose(3)
page in your system manual for further details.
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. Ifdlsym
fails, it returns#f
and the cause of the error can be obtained using the proceduredlerror
.Consult the
dlsym(3)
page in your system manual for further details.
If any of the dynamic loading operations (i.e.,
dlopen
,dlclose
,dlsym
) fails, the cause of the error can be obtained by callingdlerror
which returns a string describing the error. The proceduredlerror
returns#f
if there was no dynamic loading error.Consult the
dlerror(3)
page in your system manual for further details.
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.
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.
The procedure
make-c-callout
is the primary facility for making foreign procedures callable from Scheme. It works as follows:
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 procedurep1
;- the procedure
p1
accepts a pointer to a foreign procedure (e.g. one obtained fromdlsym
) and returns a Scheme procedurep2
that encapsulates the foreign procedure;- the final procedure
p2
can be called with as many arguments as the ones specified in the parameter-types; the parameters supplied top2
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;- 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. usestring->bytevector
to decode the string, then usemalloc
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.
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
|
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:
libc
;
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
;
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)
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).
The procedure
make-c-callback
is similar to the proceduremake-c-callout
except that it provides a bridge from native procedures back into Scheme. While the proceduremake-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 usingdlsym
or similar means. In particular, such native pointers can be passed tomake-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"
Return the void value. It is the value returned by forms that do not return a meaningful value.
Ikarus development is accessible at the site:
where we can find the latest release tarball. For the development revisions, we can use the Bazaar revision control system:
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 ...
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.
equal?
may not terminate on infinite (circular)
input, when the input is equal?
.
number->string
does not accept the third argument (precision).
Similarly, string->number
and the reader do not recognize the
|p
notation.
(rnrs arithmetic
bitwise)
:
bitwise-reverse-bit-field bitwise-rotate-bit-field
(rnrs arithmetic
fixnum)
:
fxreverse-bit-field fxrotate-bit-field
(rnrs hashtables)
:
make-eqv-hashtable equal-hash
(rnrs io ports)
:
port-has-port-position? port-position port-has-set-port-position!? set-port-position! make-custom-binary-input/output-port make-custom-textual-input/output-port open-file-input/output-port
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:
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.
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.
“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
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.
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.
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.
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.
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:
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.
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:
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.
“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:
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
SIGABRT
: ikaruslib posix signalSIGALRM
: ikaruslib posix signalSIGBUS
: ikaruslib posix signalSIGCHLD
: ikaruslib posix signalSIGCONT
: ikaruslib posix signalSIGFPE
: ikaruslib posix signalSIGHUP
: ikaruslib posix signalSIGILL
: ikaruslib posix signalSIGINT
: ikaruslib posix signalSIGKILL
: ikaruslib posix signalSIGPIPE
: ikaruslib posix signalSIGPOLL
: ikaruslib posix signalSIGPROF
: ikaruslib posix signalSIGQUIT
: ikaruslib posix signalSIGSEGV
: ikaruslib posix signalSIGSTOP
: ikaruslib posix signalSIGSYS
: ikaruslib posix signalSIGTERM
: ikaruslib posix signalSIGTRAP
: ikaruslib posix signalSIGTSTP
: ikaruslib posix signalSIGTTIN
: ikaruslib posix signalSIGTTOU
: ikaruslib posix signalSIGURG
: ikaruslib posix signalSIGUSR1
: ikaruslib posix signalSIGUSR2
: ikaruslib posix signalSIGVTALRM
: ikaruslib posix signalSIGXCPU
: ikaruslib posix signalSIGXFSZ
: ikaruslib posix signal#!eof
: ikaruslib reader#!ikarus
: ikaruslib reader#:pretty-name
: ikaruslib gensym#{gensym}
: ikaruslib gensym#{pretty-name unique-name}
: ikaruslib gensym#{unique-name}
: ikaruslib gensymaccept-connection
: ikaruslib io socket serveraccept-connection-nonblocking
: ikaruslib io socket serverchange-mode
: ikaruslib posix fileclose-tcp-server-socket
: ikaruslib io socket servercollect
: ikaruslib guardianscurrent-directory
: ikaruslib posix filedelete-directory
: ikaruslib posix filedelete-file
: ikaruslib posix filedirectory-list
: ikaruslib posix filedlclose
: ikaruslib foreign dldlerror
: ikaruslib foreign dldlopen
: ikaruslib foreign dldlsym
: ikaruslib foreign dlfile-ctime
: ikaruslib posix filefile-directory?
: ikaruslib posix filefile-exists?
: ikaruslib posix filefile-regular?
: ikaruslib posix filefile-symbolic-link?
: ikaruslib posix filefork
: ikaruslib posix processformat
: ikaruslib printingfprintf
: ikaruslib printingfree
: ikaruslib foreign memops allocgensym
: ikaruslib gensymgensym->unique-string
: ikaruslib gensymgensym-count
: ikaruslib printinggensym-prefix
: ikaruslib printinggensym?
: ikaruslib gensymgetenv
: ikaruslib posix envimport
: ikaruslib importinput-socket-buffer-size
: ikaruslib io socket serverinteger->pointer
: ikaruslib foreign memops pointerinteraction-environment
: ikaruslib environmentkill
: ikaruslib posix signalload
: ikaruslib loadmake-c-callback
: ikaruslib foreign call backmake-c-callout
: ikaruslib foreign call out apimake-directory
: ikaruslib posix filemake-guardian
: ikaruslib guardians apimake-parameter
: ikaruslib parametersmake-symbolic-link
: ikaruslib posix filemake-traced-procedure
: ikaruslib tracingmalloc
: ikaruslib foreign memops allocmodule
: ikaruslib modules apinanosleep
: ikaruslib posix miscnew-cafe
: ikaruslib cafeoutput-socket-buffer-size
: ikaruslib io socket serverparameterize
: ikaruslib parameterspointer->integer
: ikaruslib foreign memops pointerpointer-ref-c-double
: ikaruslib foreign memops peekpointer-ref-c-float
: ikaruslib foreign memops peekpointer-ref-c-pointer
: ikaruslib foreign memops peekpointer-ref-c-signed-char
: ikaruslib foreign memops peekpointer-ref-c-signed-int
: ikaruslib foreign memops peekpointer-ref-c-signed-long
: ikaruslib foreign memops peekpointer-ref-c-signed-long-long
: ikaruslib foreign memops peekpointer-ref-c-signed-short
: ikaruslib foreign memops peekpointer-ref-c-unsigned-char
: ikaruslib foreign memops peekpointer-ref-c-unsigned-int
: ikaruslib foreign memops peekpointer-ref-c-unsigned-long
: ikaruslib foreign memops peekpointer-ref-c-unsigned-long-long
: ikaruslib foreign memops peekpointer-ref-c-unsigned-short
: ikaruslib foreign memops peekpointer-set-c-char!
: ikaruslib foreign memops pokepointer-set-c-double!
: ikaruslib foreign memops pokepointer-set-c-float!
: ikaruslib foreign memops pokepointer-set-c-int!
: ikaruslib foreign memops pokepointer-set-c-long!
: ikaruslib foreign memops pokepointer-set-c-long-long!
: ikaruslib foreign memops pokepointer-set-c-pointer!
: ikaruslib foreign memops pokepointer-set-c-short!
: ikaruslib foreign memops pokepointer-size
: ikaruslib foreign memops pointerpointer?
: ikaruslib foreign memops pointerport-mode
: ikaruslib readerpost-gc-hooks
: ikaruslib guardians apipretty-print
: ikaruslib printingpretty-width
: ikaruslib printingprint-gensym
: ikaruslib printingprint-graph
: ikaruslib printingprint-unicode
: ikaruslib printingprintf
: ikaruslib printingprocess
: ikaruslib io processprocess-nonblocking
: ikaruslib io processregister-callback
: ikaruslib io socket serverset-port-mode!
: ikaruslib readerstrerror
: ikaruslib posix miscsystem
: ikaruslib posix processtcp-connect
: ikaruslib io socket clienttcp-connect-nonblocking
: ikaruslib io socket clienttcp-server-socket
: ikaruslib io socket servertcp-server-socket-nonblocking
: ikaruslib io socket servertime
: ikaruslib timingtime-it
: ikaruslib timingtrace-define
: ikaruslib tracingtrace-lambda
: ikaruslib tracingudp-connect
: ikaruslib io socket clientudp-connect-nonblocking
: ikaruslib io socket clientvoid
: ikaruslib miscwaitpid
: ikaruslib posix waitpidwstatus-exit-status
: ikaruslib posix waitpidwstatus-pid
: ikaruslib posix waitpidwstatus-received-signal
: ikaruslib posix waitpid[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.