Go to the first, previous, next, last section, table of contents.


The Scheme-Java interface

Kawa has extensive features so you can work with Java objects and call Java methods.

Scheme types in Java

All Scheme values are implemented by sub-classes of `java.lang.Object'.

Scheme symbols are implemented using java.lang.String. (Don't be confused by the fact the Scheme sybols are represented using Java Strings, while Scheme strings are represented by kawa.lang.Scheme. It is just that the semantics of Java strings match Scheme symbols, but do not match mutable Scheme strings.) Interned symbols are presented as interned Strings. (Note that with JDK 1.1 string literals are automatically interned.)

Scheme integers are implemented by gnu.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 gnu.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.

Low-level Operations on Java Arrays

The following macros evaluate to procedures that can be used to manipulate primitive Java array objects. The compiler can inline each to a single bytecode instruction (not counting type conversion).

Syntax: primitive-array-new element-type
Evaluates to a one-argument procedure. Applying the resulting procedure to an integer count allocates a new Java array of the specified length, and whose elements have type element-type.

Syntax: primitive-array-set element-type
Evaluates to a three-argument procedure. The first argument of the resulting procedure must be an array whose elements have type element-type; the second argument is an index; and the third argument is a value (coercible to element-type) which replaces the value specified by the index in the given array.

Syntax: primitive-array-get element-type
Evaluates to a two-argument procedure. The first argument of the resulting procedure must be an array whose elements have type element-type; the second argument is an index. Applying the procedure returns the element at the specified index.

Syntax: primitive-array-new element-type
Evaluates to a one-argument procedure. The argument of the resulting procedure must be an array whose elements have type element-type. Applying the procedure returns the length of the array.

Low-level Method invocation

The following lower-level primitives require you to specify the parameter and return types explicitly. Type specifications are currently required to be string literals or one of the standard types (see section Standard Types).

Syntax: 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.

Syntax: 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.

Syntax: 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.

Syntax: 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. If the macros are used directly as the procedure of a procedure call, then kawa can inline the correct bytecodes to call the specified methods. (Note also that neither macro checks that there really is a method that matches the specification.) Otherwise, the Java reflection facility is used.

Low-level Operations on Object Fields

The following macros evaluate to procedures that can be used to access or change the fields of objects or static fields. The compiler can inline each to a single bytecode instruction (not counting type conversion).

These macros are deprecated. The fields and static-field functions (see section Accessing fields of Java objects) are easier to use, more powerful, and just as efficient. (One exception is for primitive-set-static; while its functionality can be expressed using (set! (static-field ...) ...), that idiom is currently less efficient.) Also, the high-level functions currently do not provide access to private fields.

Syntax: primitive-get-field class fname ftype
Use this to access a field named fname having type type in class class. Evaluates to a new one-argument procedure, whose argument is a reference to an object of the specified class. Calling that procedure returns the value of the specified field.

Syntax: primitive-set-field class fname ftype
Use this to change a field named fname having type type in class class. Evaluates to a new two-argument procedure, whose first argument is a reference to an object of the specified class, and the second argument is the new value. Calling that procedure sets the field to the specified value. (This macro's name does not end in a `!', because it does not actually set the field. Rather, it returns a function for setting the field.)

Syntax: primitive-get-static class fname ftype
Like primitive-get-field, but used to access static fields. Returns a zero-argument function, which when called returns the value of the static field.

Syntax: primitive-set-static class fname ftype
Like primitive-set-field, but used to modify static fields. Returns a one-argument function, which when called sets the value of the static field to the argument.

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()'. Before you start, you need to initialize the global environment, which you can with

Environment.setCurrent(new Scheme().getEnvironment());

Alternatively, rather than setting the global environment, you can use this style:

Scheme scm = new Scheme();
Object x = scm.eval("(+ 3 2)");
System.out.println(x);


Go to the first, previous, next, last section, table of contents.