The jscheme.JS (for Java - Scheme Interface) provides a simple API for invoking Scheme code from Java.
Loading Scheme code:
JS.load("elf/basic.scm");
Invoking a procedure:
System.out.println(JS.call("+", JS.toObject(2), JS.toObject(3)));
Eval:
output.setText(JS.eval(input.getText()).toString());
A Wrapper class is a Java class that invokes Scheme procedures.
JScheme provides Applet and Servlet wrappers.
Listener interface implements 35 Swing listeners.
Example: Comparator:
import java.util.Comparator;
import jsint.Procedure;
import jscheme.JS;
public class Compare1 implements Comparator {
private Procedure predicate;
public Compare1(Procedure predicate) {
this.predicate = predicate;
}
private boolean p(Object a, Object b) {
return JS.booleanValue(JS.call(this.predicate, a, b));
}
public int compare(Object a, Object b) {
return p(a, b) ? -1 : p(b, a) ? 1 : 0;
}
}
Sorting an array:
> (let ((a #(5 2 1 3 9 6 2 7 1)))
(Arrays.sort a (Compare1. <))
a)
#(1 1 2 2 3 5 6 7 9)
In JDK 1.3 you can generate proxy classes on the fly that implement several interaces.
The proxy calls an InvocagtionHandler to handle each method invocation.
package elf;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import jsint.Procedure;
import jscheme.JS;
public class SchemeInvocationHandler implements InvocationHandler {
Procedure proc;
public SchemeInvocationHandler(Procedure proc) { this.proc = proc; }
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
return JS.call(proc, proxy, method, args);
}
}
(import "elf.SchemeInvocationHandler")
(define (delegate-to delegate handler)
;; Returns a proxy that delegates all its interface methods through handler
;; to delegate. See (trace-object) for example.
(Proxy.newProxyInstance
(.getClassLoader (.getClass delegate))
(.getInterfaces (.getClass delegate))
(SchemeInvocationHandler.
(lambda (proxy method argv)
(handler delegate method argv)))))
(define (trace-handler delegate method argv)
(print (list 'call: delegate (.getName method) argv))
(let ((result (.invoke method delegate argv)))
(print (list 'return: result))
result))
(define (trace-object x)
;; Returns a proxy object for x that traces all interface methods.
(delegate-to x trace-handler))
Tracing a Hashtable.
> (define h (Hashtable. 10))
{}
> (define th (trace-object h))
(call: {} "toString" #null)
(return: "{}")
{}
> (.put th 'a 3)
(call: {} "put" #(a 3))
(return: #null)
#null
> (.put th 'b 4)
(call: {a=3} "put" #(b 4))
(return: #null)
#null
> th
(call: {b=4, a=3} "toString" #null)
(return: "{b=4, a=3}")
{b=4, a=3}
Java classes written in Scheme
The macro (define-class) defines a Java class using a syntax
that blends Java and Scheme together.
(define-class
(package frog)
(import java.util.Comparator)
(public class Compare implements Comparator)
;; Design issue, fields must be public for Jscheme code to access.
(public Procedure predicate)
(public Compare (Procedure predicate)
(.predicate$ this predicate))
(public boolean predicate (Object a Object b)
((.predicate$ this) a b))
(public int compare (Object a Object b)
(cond ((.predicate this a b) -1)
((.predicate this b a) 1)
(else 0)))
(public boolean equals (Object that)
(and (eq? (.getClass this) (.getClass that))
(eq? (.predicate$ this) (.predicate$ that))))
(public int hashCode () 0))
Issues
Fields must be declared public for Scheme to access them in an
applet.
Fields accessed using reflector syntax
(this ...) and (super ...) in a constructor is
treated specially.
(.method super ...): super treated as a special word.
No way to invoke super in Reflection API. Scheme must generate a
method.
No debugging support.
Allowing internal (define...) might provide a nice
modularity mechanism.
How to avoid unnecessary recompilation.