Kawa, The Java-Based Scheme System

Note: We are reposting this original resource for people who still use and refer to it. All credits go to R. Alexander Milowski.

Kawa is a Scheme environment, written in Java, and that compiles Scheme code into Java byte-codes.

This documents version 1.4, updated 31 March 1997.

The author of Kawa is Per Bothner <bothner@cygnus.com>. It is a re-write of Kawa 0.2, which was written by R. Alexander Milowski <alex@copsol.com>.

The name Kawa may change in the future, to avoid confusion with a proprietary product of the same name.

The Scheme repository has various useful information on Scheme, including a pointer to an online copy of R4RS.

KAWA-JAVA

Related Information

For copyright information on the software and documentation, see section License.

Features

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 provides define-syntax from the R4RS appendix, and (from the draft R5RS) eval and multiple values.

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.

Kawa implements most of the features of the expression language of DSSSL, the Scheme-derived ISO-standard Document Style Semantics and Specification Language for SGML. Of the core expression language, the only features missing are character properties, external-procedure, the time-relationed procedures, and character name escapes in string literals. Also, Kawa is not generally tail-recursive, and literal unescaped symbols are case-insensitive (folded to lower-case). From the full expression language, Kawa additionally is missing format-number, format-numer-list, and language objects. Quantities, keyword values, and the expanded lambda form (with optional and keyword parameters) are supported.

Getting Kawa

The current sources release is in here.

A ready-to-run .zip archive of the pre-compiled classes is in here.

Building And Installing Kawa

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.

Running Java: Getting and running Java
Binary distribution: Installing and using the binary distribution
Source distribution: Installing and using the source distribution

Getting and running Java

You will need a working Java system. The discussion below assumes you are using the Java Developer’s Kit (JDK) version 1.0.x or 1.1 from JavaSoft (Sun). (Kawa has also been reported to work with Kaffe, Semantec Cafe, and J++.) You can download free copies of JDK 1.0.2 for Sparc/Solaris, x86/Solaris, MS-Windows 95/NT, and MacOS.

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 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, and some others) you can set both variables thus:

PATH=$JDK/bin:$PATH

CLASSPATH=.:$JDK/lib/classes.zip

export PATH CLASSPATH

Installing and using the binary distribution

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 of compiling the sources.

The binary release comes as a .zip archive `kawa-1.4-compiled.zip’.

You can unzip the archive, or you can use it as is. Assuming the latter, copy the archive to some suitable location, such as /usr/local/lib/kawa-compiled.zip.

Then, before you can actually run Kawa, you need to set CLASSPATH so it includes the Kawa archive. For example:

export CLASSPATH=.:/usr/local/lib/kawa-compiled.zip:$JDK/lib/classes.zip

Then to run Kawa do:

java kawa.repl

Installing and using the source distribution
The Kawa release normally comes as a gzip-compressed tar file named `kawa-1.4.tar.gz’.
In your build directory do:

tar xzf kawa-1.4.tar.gz

cd kawa-1.4

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

If you have JDK 1.1 or some other Java 1.1 system with the java.lang.reflect.Field class, you add the option –with-jdk1. This will enable the record feature.

Thus you need to compile all the .java source files. Just run make:

make

This assume that `java’ and `javac’ are the java interpreter and compiler, respectively. If you are using the Kaffe free Java interpreter, you need to instead say:

make JAVA=kaffe

You can now test the system by running Kawa in place:

java kawa.repl

or you can run the test suite:

(cd testsuite; make check)

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

This is done automatically if you use the `kawa’ script.

How To Start Up And Run Kawa

The easiest way to start up Kawa is to run the `kawa’ script. This finds your java interpreter, and sets up `CLASSPATH’ correctly. If you have installed Kawa such $PREFIX/bin is in your $PATH, just do:

kawa

However, `kawa’ only works if you have a Unix-like shell.
To run Kawa manually, you must start a Java interpreter. How you do 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/repl.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.repl

In either case, 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:

kawa -e ‘(display (+ 12 4))(newline)’

or:

java kawa.repl -e ‘(display (+ 12 4))(newline)’

Either causes Kawa to print `16′, and then exit.
`-e expr’
Kawa evaluates expr, which contains one or more Scheme expressions.
`-c expr’
Same as `-e expr’.
`-f filename’
Kawa reads and evaluates expressions from the file named by filename. If filename is `-‘, standard input is read (with no prompting).
`-s’
`–‘
The global variable `command-line-arguments’ is set to the remaining arguments (if any), and an interactive read-eval-print loop is started.
The options `-C’, `-d’, `-T’ and `-P’ are used to compile a Scheme file; see section Compiling Scheme to a set of .class files.

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’, `-C’, 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.

Features Of R4RS Not Implemented

The file Compliance.html specifies which functions and syntax have been implemented so far.

The entire “numeric tower” is implemented. However, some transcendental function only work on reals. 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.

The define-syntax primitive is provided, but has some bugs making it not properly hygienic. Neither let-syntax nor letrec-syntax are implemented.

Extensions

Multiple values
Keywords
Special named constants
Optional and keyword lambda parameters
Logical Number Operations
Records
Ports
Eval and Environments
Quantities
Threads
Miscellaneous

Multiple values

The multiple-value feature will be in R5RS.

Function: values object …

Delivers all of its arguments to its continuation.

Function: call-with-values thunk receiver

Call its thunk argument with a continuation that, when passed some values, calls the receiver procedure with those values as arguments.

Special named constants

Constant: #!optional

Special self-evaluating literal used in lambda parameter lists before optional parameters.

Constant: #!rest

Special self-evaluating literal used in lambda parameter lists before the rest parameter.

Constant: #!key

Special self-evaluating literal used in lambda parameter lists before keyword parameters.

Constant: #!eof

The end-of-file object.

Constant: #!void

The void value. Same as (values). If this is the value of an expression in a read-eval-print loop, nothing is printed.

Constant: #!null

The Java null value. This is not really a Scheme value but is useful when interfacing to low-level Java code.

Keywords

Keywords are similar to symbols. The main difference is that keywords are self-evaluating and therefore do not need to be quoted in expressions. They are used mainly for specifying keyword arguments.

keyword = identifier:

A keyword is a single token; therefore no whitespace is allowed between the identifier and the colon (which is not considered part of the name of the keyword).

Function: keyword? obj

Return #t if obj is a keyword, and otherwise returns #f.

Function: keyword->string keyword

Returns the name of keyword as a string. The name does not include the final #\:.

Function: string->keyword string

Returns the keyword whose name is string. (The string does not include a final #\:.)

Optional and keyword lambda parameters

Kawa borrows the extended formal argument list of DSSSL:

lambda-expression = (lambda (formal-arguments) body)

You can of course also use the extended format in a define:

(define (name formal-arguments) body)

formal-arguments =

req-opt-args . rest-arg or:

req-opt-args rest-key-args

req-opt-args = req-arg* (#!optional opt-arg*)?

rest-key-args = (#!rest rest-arg)? (#!key key-arg*)?

opt-arg = variable | (variable initializer)

req-arg = variable

key-arg = variable | (variable initializer)

rest-arg = variable

When the procedure is applied to a list of actual arguments, the formal and actual arguments are processed from left to right as follows:

The req-args are bound to actual arguments starting with the first actual argument. It shall be an error if there are fewer actual arguments then there are req-args.
Next the opt-args are bound to remaining actual arguemnts. If there are fewer remaining actual arguments than there are opt-args, then the remaining variables are bound to the corresponding initializer, if one was specified, and otherwise to #f. The initializer is evaluated in an environment in which all the previous formal parameters have been bound.
If there is a rest-arg, it is bound to a list of all the remaining actual arguments. These remaining actual arguments are also eligible to be bound to keyword arguments. If there is no rest-arg and there are no key-args, then it shall be an error if there are any remaining actual arguments.
If #!key was specified, then there shall be an even number of remaining actual arguments. These are interpreted as a series of pairs, where the first member of each pair is a keyword specifying the argument name, and the second is the corresponding value. It shall be an error if the first member of a pair is not a keyword. It shall be an error if the argument name is not the same as a variable in a key-args, unless there is a rest-arg. If the same argument name occurrs more than once in the list of actual arguments, then the first value is used. If there is no actual argument for a particular key-arg, then the variable is bound to the corresponding initializer, if one was specified, and otherwise to #f. The initializer is evaluated in an environment in which all the previous formal parameters have been bound.

Logical Number Operations

These functions operate on the 2’s complement binary representation of an exact integer.

Function: logand i …

Returns the bit-wise logical “and” of the arguments. If no argument is given, the result is -1.

Function: logior i …

Returns the bit-wise logical “(inclusive) or” of the arguments. If no argument is given, the result is 0.

Function: logxor i …

Returns the bit-wise logical “exclusive or” of the arguments. If no argument is given, the result is 0.

Function: lognot i

Returns the bit-wise logical inverse of the argument.

Function: logop op x y

Perform one of the 16 bitwise operations of x and y, depending on op.

Function: bittest i j

Returns true if the arguments have any bits in common. Same as (not (zero? (logand i j))), but is more efficient.

Function: logbit? i pos

Returns #t iff the bit numbered pos in i is one.

Function: arithmetic-shift i j

Shifts i by j. It is a “left” shift if j>0, and a “right” shift if j<0.

The result is equal to (floor (* i (expt 2 j))).

Function: ash i j

Alias for arithmetic-shift.

Function: logcount i

Count the number of 1-bits in i, if it is non-negative. If i is negative, count number of 0-bits.

Function: integer-length i

Return number of bits needed to represent i in an unsigned field. Regardless of the sign of i, return one less than the number of bits needed for a field that can represent i as a two’s complement integer.

Function: bit-extract n start end

Return the integer formed from the (unsigned) bit-field starting at start and ending just before end. Same as (arithmetic-shift (bitand n (bitnot (arithmetic-shift -1 end))) (- start)).

Records

The Record package provides a facility for user to define their own record data types. A record type is implemented as Java Class object, and records are extensions of the class Record. These procedures are only available if you specified –with-jdk11 when you configured Kawa, since they use the Java 1.1 reflection facility.

Function: make-record-type type-name field-names

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

In Kawa, returns a newly-created Class object that extends the Record class. Each record field is implemented as a public Java instance field.

Function: record-constructor rtd [field-names]

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

In Kawa, rtd may be any Class that has a public default constructor, as long as the field-names are public instance fields. (The fields should have type Object — unless you know what you are doing!)

Function: record-predicate rtd

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

In Kawa, the retured procedure checks if the argument is an instance of rtd or one of its sub-classes.

Function: record-accessor rtd field-name

Returns a procedure for reading the value of a particular field of a member of the type represented by rtd. The returned procedure accepts exactly one argument which must be a record of the appropriate type; it returns the current value of the field named by the symbol field-name in that record. The symbol field-name must be a member of the list of field-names in the call to make-record-type that created the type represented by rtd. (In Kawa, the field-name can be any public non-final Object field of the Class rtd.)

Function: record-modifier rtd field-name

Returns a procedure for writing the value of a particular field of a member of the type represented by rtd. The returned procedure accepts exactly two arguments: first, a record of the appropriate type, and second, an arbitrary Scheme value; it modifies the field named by the symbol field-name in that record to contain the given value. The returned value of the modifier procedure is unspecified. The symbol field-name must be a member of the list of field-names in the call to make-record-type that created the type represented by rtd. (In Kawa, the field-name can be any public non-final Object field of the Class rtd.)

Function: record? obj

Returns a true value if obj is a record of any type and a false value otherwise.

In Kawa, this is true if obj is an instance of kawa.lang.Record.

Function: record-type-descriptor record

Returns a record-type descriptor representing the type of the given record. That is, for example, if the returned descriptor were passed to record-predicate, the resulting predicate would return a true value when passed the given record. In Kawa, record may be any object, and the value returned is the class of the object.

Function: record-type-name rtd

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

Function: record-type-field-names rtd

Returns a list of the symbols naming the fields in members of the type represented by rtd. The returned value is equal? to the field-names argument given in the call to make-record-type that created the type represented by rtd.

Ports

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.

Function: force-output [port]

Forces any pending output on port to be delivered to the output device and returns an unspecified value. If the port argument is omitted it defaults to the value returned by (current-output-port).

Eval and Environments

Function: eval expression [environment-specifier]

eval evaluates expression in the environment indicated by environment-specifier.

The default for environment-specifier is the result of (interaction-environment).

Function: null-environment

This procedure returns an environment that contains no variable bindings, but contains (syntactic) bindings for all the syntactic keywords.

The effect of assigning to a variable in this environment (such as let) is undefined.

Function: scheme-report-environment version

The version must be an exact non-negative inetger corresponding to a version of one of the Revisedversion Reports on Scheme. The procedure returns an environment that contains exactly the set of bindings specified in the corresponding report.

This implementation supports version that is 4 or 5.

The effect of assigning to a variable in this environment (such as car) is undefined.

Function: interaction-environment

This procedure return an environment that contains implementation-defined bindings, as well as top-level user bindings.

Quantities

As a super-class of numbers, Kawa also provides quantities. A quantity is a product of a unit and a pure number. The number part can be an arbitrary complex number. The unit is a product of integer powers of base units, such as meter or second.

Kawa quantities are a generalization of the quantities in DSSSL, which only has length-derived quantities.

The precise syntax of quantity literals may change, but some examples are 10pt (10 points), 5s (5 seconds), and 4cm2 (4 square centimeters).

Function: quantity? object

True iff object is a quantity. Note that all numbers are quantities, but not the other way round.

Function: quantity->number q

Returns the pure number part of the quantity q, relative to primitive (base) units. If q is a number, returns q. If q is a unit, yields the magitude of q relative to base units.

Function: quantity->unit q

Returns the unit of the quantity q. If q is a number, returns the empty unit.

Function: make-quantity x unit

Returns the product of x (a pure number) and unit. You can specify a string instead of unit, such as “cm” or “s” (seconds).

Macro: define-unit unit-name expression

Define unit-name as a unit (that can be used in literals) equal to the quantity expression.

Threads

There is a very preliminary interface to create parallel threads. The interface is similar to the standard delay/force, where a thread is basically the same as a promise, except that evaluation may be in parallel.

So far, little or no effort has been made into making Kawa thread-safe. There are no per-thread bindings, and the current input and output parts are global. That needs to change.

Macro: future expression

Creates a new thread that evaluates expression.

Function: force thread

The standard force function has generalized to also work on threads. If waits for the thread’s expression to finish executing, and returns the result.

Function: sleep time

Suspends the current thread for the specified time. The time can be either a pure number (in secords), or a quantity whose unit is a time unit (such as 10s).

Function: gentemp

Returns a new (interned) symbol each time it is called. The symbol names are implementation-dependent.

Macro: defmacro name lambda-list form …

Defines an old-style macro a la Common Lisp, and installs (lambda lambda-list form …) as the expansion function for name. When the translator sees an application of name, the expansion function is called with the rest of the application as the actual arguments. The resulting object must be a Scheme source form that is futher processed (it may be repeatedly macro-expanded).

If you define a macro with defmacro, you (currently) cannot use the macro in the same compilation as the definition. This restrictions does not apply to macros defined by define-syntax.

Miscellaneous

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.

Function: error string

Signals an error with the given string as an error message. It does this by throwing a GenericError (but that may change). This normally prints a stack trace, and brings you back to the top level, or exits kawa if you are not running interactively.

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.

Compiling Scheme Code To Byte-Codes

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 and 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. The advantage of compiling to an archive is that it is simple and transparent. A minor 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. 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.

Archive compilation: Compiling Scheme to an archive file
Files compilation: Compiling Scheme to a set of .class files

Compiling Scheme to an archive file

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.

Compiling Scheme to a set of .class files

Invoking `kawa’ (or `java kawa.repl’) with the `-C’ flag will compile a `.scm’ source file into one or more `.class’ files.

You run it as follows:

kawa [-d outdirectory] [-P prefix] [-T topname] -C infile

Note the `-C’ must come last, because `Kawa’ processes the arguments and options in order,

Here:

`-C infile’
The Scheme source file we want to compile.
`-d outdirectory’
The directory under which the resulting `.class’ files will be. The default is the current directory.
`-P prefix’
A string to prepend to the generated class names. The default is the empty string.
`-T topname’
The name of the “top” class – i.e. the one that contains the code for the top-level expressions and definitions. The default is generated from the infile and prefix.
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:

kawa -d /usr/local/java -P my.lib. -T foo -C foosrc.scm

you can use either:

(load “my.lib.foo”)

or:

(load “my/lib/foo.class”)

If you are compiling a Scheme source file (say `foosrc.scm’) that uses macros defined in some other file (say `macs.scm’), you need to make sure the definitions are visible to the compiler. One way to do that is with the `-f’:

kawa -f macs.scm -C foosrc.scm

The Scheme-Java interface

Scheme types in Java

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. (It is likely that Scheme Symbols will be represented by java.lang.String values after JDK 1.1 is released.)

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.

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 implemented using kawa.lang.FString.

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.

Calling Java methods from Scheme

Kawa provides a simple yet powerful “Foreign Function Interface”, which allows you to call any (virtual or static) Java method as if it were a Scheme procedure.

These primitives require you to specify the parameter and return types. Type specifications are currently required to be string literals. A type specifier can be a fully-qualified Java class name (for example “java.lang.StringBuffer”). In that case, the actual argument is cast at run time to the named class. The specification “String” is an exception: It causes the toString method of the actual argument to be invoked.

A type specifier can also be one of the primitive Java types. The numeric types “long”, “int”, “short”, “byte”, “float”, and “double” are converted from the corresponding Scheme number classes. Similarly, “char” can be converted to and from Scheme characters. The type boolean matches any object, and the result is false if and only if the actual argument is #f. The return type “void” indicates that no value is returned.

Macro: primitive-constructor class (argtype …)

Returns a new anonymous procedure, which when called will create a new object of the specified class, and will then call the constructor matching the specified argument types.

Macro: primitive-virtual-method class method rtype (argtype …)

Returns a new anonymous procedure, which when called will invoke the instance method whose name is the string method in the class whose name is class.

Macro: primitive-static-method class method rtype (argtype …)

Returns a new anonymous procedure, which when called will invoke the static method whose name is the string method in the class whose name is class.

Macro: primitive-interface-method interface method rtype (argtype …)

Returns a new anonymous procedure, which when called will invoke the matching method from the interface whose name is interface.

The macros return procedure values, just like lambda, but the procedures are not first-class. Currently, they can only be used directly as the procedure of a procedure call, and they cannot be assigned, nor passed as values. These restrictions will be eased in a future Kawa release (which will use the new Java reflective facilities). Note also that neither macro checks that there really is a method that matches the specification.

Some examples using these primitives are `vectors.scm’ and `characters.scm’ the directory `kawa/lib’ in the Kawa sources.

Loading a ModuleBody

The “top” class created by kawa -C (see section Compiling Scheme to a set of .class files) extends the ModuleBody class. It is actually fairly easy to write a ModuleBody by hand in Java, and you can then use the Scheme load procedure to cause arbitrary actions. Here is an example. (Note that the details are subject to change!)

package MyDev;

import kawa.lang.*;

class MyDevFunc extends Procedure2

{

public Object apply2 (Object arg1, Object arg2)

{

… stuff to control my device …;

}

}

 

public class MyDevice extends ModuleBody

{

public Object run (Environment env)

throws WrongArguments, WrongType, GenericError, UnboundSymbol

{

… initialize my device here …;

 

// Declare (handle-my-device x y) to call MyDevFunc.apply2 (x, y):

env.define (“handle-my-device”, new MyDevFunc ());

 

// Return the void value (i.e. no value).

return Interpreter.voidObject;

}

}

If this text is in the file MyDev/MyDevice.java, and you compile it with javac, you will get MyDev/MyDevice.class and MyDev/MyDevFunc.class. Assuming the current directory is in your CLASSPATH, you can now do the following in Kawa:

(load “MyDev/MyDevice.class”)

or:

(load “MyDev.MyDevice”)

This will cause the actions in MyDevice.run to be executed. The current environment is passed in as the parameter env. One of those actions is to define the procedure handle-my-device.

Evaluating Scheme Expressions from Java

The following methods are recommended if you need to evaluate a Scheme expression from a Java method. (Some details (such as the `throws’ lists) may change.)

Static method: Object Scheme.eval (InPort port, Environment env)

Read expressions from port, and evaluate them in the env environment, until end-of-file is reached. Return the value of the last expression, or Interpreter.voidObject if there is no expression.

Static method: Object Scheme.eval (String string, Environment env)

Read expressions from string, and evaluate them in the env environment, until the end of the string is reached. Return the value of the last expression, or Interpreter.voidObject if there is no expression.

Static method: Object Scheme.eval (Object sexpr, Environment env)

The sexpr is an S-expression (as may be returned by read). Evaluate it in the env environment, and return the result.

For the Environment in most cases you could use `Environment.current()’.

Reporting Bugs

If you have a problem installing or using Kawa, send mail to kawa@cygnus.com

This mailing list is used for reporting bugs, patches, discussing changes to Kawa, and announcing snapshots. If you wish to subscribe (or later unsubscribe), send a request to kawa-request@cygnus.com.

License

This is the license for the Kawa software:

This software is copyrighted by Cygnus Support, R. Alexander Milowski, Copernican Solutions Incorporated, 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.

Here is the license for this manual:

Copyright (C) 1996, 1997 Per Bothner
Parts of this manual were derived from the SLIB manual, copyright (C) 1993, 1994 Todd R. Eigenschink and Aubrey Jaffer.
Parts of this manual were derived from ISO/EIC 10179:1996(E) (Document Style and Specifical Language) – unknown copyright.

Permission is granted to make and distribute verbatim copies of this manual provided the copyright notice and this permission notice are preserved on all copies.

Permission is granted to copy and distribute modified versions of this manual under the conditions for verbatim copying, provided that the entire resulting derived work is distributed under the terms of a permission notice identical to this one.

Permission is granted to copy and distribute translations of this manual into another language, under the above conditions for modified versions, except that this permission notice may be stated in a translation approved by the author.

Post Comment