Kawa is a Scheme environment, written in java, and that compiles Scheme code into Java byte-codes.
This documents version 1.0, updated 16 September 1996.
R. Alexander Milowski <alex@copsol.com> wrote the first Kawa releases. Per Bothner <bothner@cygnus.com> extensively re-wrote Kawa, and released version 0.3 and up. For now, send bug reports to bothner@cygnus.com.
Kawa is a full Scheme implementation. It implements almost
all of R4RS (for exceptions see section Features of R4RS not implemented), plus some extensions.
It provide define-syntax
from the R4RS appendex,
and multiple values (from the draft R5RS).
It is completely written in Java. Scheme functions and files are automatically compiled into Java byte-codes, providing reasonable speed. (However, Kawa is not an optimizing compiler, and does not perform major transformations on the code.)
Kawa provides the usual read-eval-print loop, as well as batch modes.
Kawa is written in an object-oriented style.
Before installing Kawa, you must have Java working on your system.
You can compile Kawa from the source distribution. Alternatively, you can install the pre-compiled binary distribution.
You will need a working Java system. The discussion below assumes you are using the Java Developer's Kit (JDK) JDK 1.0.x from JavaSoft (Sun). You can download free copies of JDK 1.0.2 for Sparc/Solaris, x86/Solaris, MS-Windows 95/NT, and MacOS; see `http://java.sun.com:80/java.sun.com/products/JDK/1.0.2/index.html'.
The program 'java' is the Java interpreter.
The program 'javac' is the Java compiler,
and is needed if you want to compile the source release yourself.
Both programs must both be in your PATH
.
You also need to set CLASSPATH
so it includes both the
current directory, and the standard Java library.
After you have installed Kawa, the CLASSPATH
needs to
include wherever you installed Kawa.
If you have the JDK in directory $JDK
,
and you are using a Bourne-shell compatible shell
(/bin/sh, ksh, bash, or other) you can set both variables thus:
PATH=$JDK/bin:$PATH CLASSPATH=.:$JDK/lib/classes.zip export PATH CLASSPATH
The binary release includes only the binary compiled `.class' versions of the same `.java' source files in the source release. It does not include any documentation, so you probably want the source release in addition to the binary release. The purpose of the binary release is just to save you time and trouble compiling the sources.
The binary release comes as a gzip-compressed tar file named `kawa-1.0-compiled.tar.gz'.
You need to decide where you want to put the Kawa `.class' files. Assuming it is `/usr/local/java' (the default), you can do:
gunzip -c <kawa-1.0-compiled.tar.gz|(cd /usr/local/java; tar xf -)
Then, before you can actually run Kawa, you need to set CLASSPATH
so it includes the Kawa files. For example:
export CLASSPATH=.:/usr/local/java:$JDK/lib/classes.zip
Then to run Kawa do:
java kawa
The Kawa release normally comes as a gzip-compressed tar file named `kawa-1.0.tar.gz'.
In your build directory do:
tar xzf kawa-1.0.tar.gz cd kawa-1.0
Then you must configure the sources. This you can do the same way you configure most other GNU software. Normally you can just run the configure script with no arguments:
./configure
This will specify that a later make install
will install the
compiled `.class' files into /usr/local/java
. If you want them
to be installed someplace else, such as $PREFIX/java
, then
specify that when you run configure:
./configure -- prefix $PREFIX
Thus you need to compile all the .java source files. Just run make:
make
You can now test the system by running Kawa in place:
java kawa
or you can install the compiled files:
make install
This will install your classes into $PREFIX/java
(and its
sub-directories). Here $PREFIX
is the directory you specified
to configure with the --prefix
option, or /usr/local
if you
did not specify a --prefix
option.
To use the installed files, you need to set CLASSPATH
so
that $PREFIX/java
is on the path:
export CLASSPATH=.:$PREFIX/java:$JDK/lib/classes.zip
To run Kawa, you must start a Java interpreter.
This depends on the Java interpreter.
For JavaSoft's JDK, you must have the Java interpreter
in your PATH
.
You must also make sure that the kawa.class
file,
the rest of the Kawa packages, and the standard Java
packages can be found by searching CLASSPATH.
See section Getting and running Java.
Then you do:
java kawa
You will then get the `kawa>' prompt, which means you are in the Kawa read-eval-print-loop. If you type a Scheme expression, Kawa will evaluate it. Kawa will then print the result (if there is a non-"void" result).
To exit Kawa, type the end-of-file character (normally ctrl/D),
or call the exit
procedure (with 0 or 1 integer arguments).
You can pass various flags to Kawa, for example:
java kawa -e '(display (+ 12 4))(newline)'
This causes Kawa to print `16', and then exit.
If there are further command-line arguments after the options have been processed, then the first remaining argument names a file that is read and evaluated. If there is no such argument, then Kawa enters an interactive read-eval-print loop, but only if none of the `-c', `-e', `-f', `-s', or `--' options were specified.
Variable: command-line-arguments
The remaining arguments (following any switches processed by Kawa itself) are assigned to the global variable `command-line-arguments', which is a vector of strings.
The file Compliance.html specifies which functions and syntax have been implemented so far.
Of the "numeric tower", only integers and double-precision floats are implemented. There is partial bignum support, but many operations are only available for 32-bit (or sometimes 64-bit) fixnums. Integral function do not necessarily work on inexact (floating-point) integers. (The whole idea of "inexact integer" in R4RS seems rather pointless ...)
Also, call-with-current-continuation is only "upwards" (?). I.e. once a continuation has been exited, it cannot be invoked. These restricted continuations can be used to implement catch/throw (such as the examples in R4RS), but not co-routines or backtracking.
Kawa does not do general tail-call elimination. However, if the
compiler can prove that the procedure being called is the current
function, then the tail call will be replaced by a jump.
This means the procedure must be defined using a letrec, not a
define (because the compiler does not know if someone might
re-define a global definition), and there must be no assignments
(using set!
) to the procedure binding.
Function: exit [code]
Exits the Kawa interpreter, and ends the Java session. The integer value code is returned to the operating system. If code is not specified, zero is returned, indicating normal (non-error) termination.
Macro: when condition form...
If condition is true, evaluate each form in order, returning the value of the last one.
Macro: unless condition form...
If condition is false, evaluate each form in order, returning the value of the last one.
Function: vector-append arg...
Creates a new vector, containing the elements from all the args appended together. Each arg may be a vector or a list.
Function: call-with-input-string string proc
Create an input port that gets its data from string, call proc with that port as its one argument, and return the result from the call of proc
Function: call-with-output-string proc
Create an output port that writes its data to a string, and call proc with that port as its one argument. Return a string consisting of the data written to the port.
All Scheme functions and source files are invisibly compiled into internal Java byte-codes. A traditional evaluator is only used for top-level directly entered expressions outside a lambda. (It would have been simpler to also byte-compile top-level expressions by surrounding them by a dummy lambda. However, this would create a new Class object in the Java VM for every top-level expression. This is undesirable unless you have a VM that can garbage collect Class objects. Sun's VM currently does not, but that is planned for JDK 1.1.)
To save speed when loading large Scheme source files, you probably want to pre-compile them and save them on your local disk. There are two ways to do this.
You can compile a Scheme source file to a single archive file.
You do this using the compile-file
function.
The result is a single file that you can move around load
just like the .scm
source file. You just specify the name
of the archive file to the load
procedure.
Currently, the archive is a "zip" archive and has extension ".zip";
a future release will probably use "Java Archive" (jar) files,
once support from that has been released from JavaSoft.
The advantage of compiling to an archive is that it is simple
and transparent. A disadvantage is that it causes the
Java "verifier" to be run when functions are loaded from it,
which takes a little extra time.
Alternatively, you can compile a Scheme source file to a
collection of `.class' files using the stand-alone
`kawac' application.
You then use the standard Java class loading mechanism to load the code.
The Java "verifier" does not need to get run, which makes
loading a little faster.
The compiled class files do have to be installed be installed somewhere
in the CLASSPATH
.
To byte-compile a file `foo.scm' do:
(compile-file "foo.scm" "foo")
This will create `foo.zip', which contains byte-compiled "j-code" that implements `foo.scm'.
You can later do:
(load "foo")
This will load `foo.zip', which should have the same effect as loading `foo.scm', except you will get the byte-compiled versions.
The `kawac' will compile a `.scm' source file into one or more `.class' files.
You run it as follows:
java kawac infile [-d outdirectory] [prefix [topname]]
Here:
When you actually want to load the classes, the outdirectory
must be in your `CLASSPATH'.
You can use the standard load
function to load the code,
by specifying the top-level class, either as a file name
(relative to outdirectory) or a class name.
E.g. if you did:
java kawac foosrc.scm -d /usr/local/java my.lib. foo
you can use either:
(load "my.lib.foo")
or:
(load "my/lib/foo.class")
All Scheme values are implemented by sub-classes of `java.lang.Object'.
Scheme symbols are implemented by kawa.lang.Symbol
.
Use the `make' static method to create a new (interned) symbol.
Scheme integers are implemented by kawa.math.IntNum
.
Use the make static function to create a new IntNum from an int or a long.
Use the intValue or longValue methods to get the int or long value of
an IntNum. There is partial support for bignums.
A Scheme "flonum" is implemented by kawa.math.DFloNum
.
A Scheme pair is implemented by kawa.lang.Pair
.
A Scheme vector is implemented by kawa.lang.Vector
.
Scheme characters are implemented using kawa.lang.Char
.
Scheme strings are currently implemented using
java.lang.StringBuffer
. This will probably be
changed to use a new sub-class of kawa.lang.Sequence
.
Scheme procedures are all sub-classes of kawa.lang.Procedure
.
Normally each function (lambda expression) in the source code is
compiled to a separate sub-class of `Procedure'.
The "action" of a `Procedure' is invoked by using one of
the `apply*' methods: `apply0', `apply1',
`apply2', `apply3', `apply4', or `applyN'.
Various sub-class of `Procedure' provide defaults
for the various `apply*' methods. For example,
a `Procedure2' is used by 2-argument procedures.
The `Procedure2' class provides implementations of all
the `apply*' methods except `apply2',
which must be provided by any class that extends Procedure2
.
A function that does some initialization and makes definitions
should probably be written as a ModuleBody
.
A ModuleBody
is a sub-class of Procedure0
.
It must therefore define an `apply0' method; that is where
you can put definitions. A ModuleBody
can be loaded
using the Scheme `load' procedure.
You can define new bindings in the top-level environment
using the define_global
method in kawa.lang.Interpreter
.
For example:
Interpreter.define_global (Symbol.make ("pi"), new DFloNum (java.lang.Math.PI));
The various Kawa classes may be shuffled around some in the future.
It is quite likely they will not remain in the package `kawa.lang',
so you should probably use import
statements rather than
fully-qualified class names to refer to Kawa classes.
This software is copyrighted by Copernican Solutions Incorporated, R. Alexander Milowski, Cygnus Support, and other parties. The following terms apply to all files associated with the software unless explicitly disclaimed in individual files.
The authors hereby grant permission to use, copy, modify, distribute, and license this software and its documentation for any purpose, provided that existing copyright notices are retained in all copies and that this notice is included verbatim in any distributions. No written agreement, license, or royalty fee is required for any of the authorized uses. Modifications to this software may be copyrighted by their authors and need not follow the licensing terms described here, provided that the new terms are clearly indicated on the first page of each file where they apply.
IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.