Kawaを使ってJavaとSchemeを相互に呼び出す

概要

Kawaを使うとSchemeプログラムをコンパイルしてJavaから呼び出したり、逆にSchemeプログラムの中からJavaのクラスを使ったりできる。

Kawaのインストール

kawa-1.8.jarをダウンロードしたら適当なところに置いてクラスパスを通すだけ。

一応、RPMパッケージも作った。(src.rpm)

JavaからSchemeの呼び出し

import java.lang.reflect.Field;

import gnu.expr.*;
import gnu.lists.*;
import gnu.mapping.*;
import gnu.text.*;
import kawa.lang.*;
import kawa.standard.*;

public class Main
{
    private Scheme scheme;
    private Environment env;

    public Main()
    {
        scheme = Scheme.getInstance();
        env = Scheme.builtin();

        Language.setDefaultLanguage(scheme);
        Environment.setGlobal(env);
    }

    public Object hello(String str) throws Throwable
    {
        ModuleBody.setMainPrintValues(true);
        scheme.loadClass("test");

        // (hello str)
        Object obj = LList.list2(env.getSymbol("hello"), new FString(str));
        return scheme.eval(obj, env);
    }

    public static void main(String[] args)
    {
        Main main = new Main();
        try {
            main.hello("Java");
        } catch (Throwable th) {
            th.printStackTrace();
        }
    }
}
  • test.scm
(define (hello s)
  (display (format #f "Hello, ~s world!\n" s)))
(hello "Scheme")

% javac Main.java
% java kawa.repl -C test.scm

  • 実行

% java Main
Hello, "Scheme" world!
Hello, "Java" world!
%

1行目は.scmファイルがロードされた時に実行されたもの。2行目がMainクラスから直接呼び出されている。

SchemeからJavaのクラスを使う

public class MyModule implements Runnable
{
    public void run()
    {
        System.out.println("MyModule required.");
    }
}
public class MyClass
{
    private int d;
    public MyClass(int d) { this.d = d; }
    public int inc() { return d++; }
    public String toString() { return "" + d; }
}
  • test2.scm
(require <MyModule>)

(define (run n)
  (let ((obj1 (make <MyClass> n)))
    (display obj1)
    (display (MyClass:inc (as <MyClass> obj1)))
    (display (MyClass:inc (as <MyClass> obj1)))
    (display obj1)
    (display "\n")))
(run 0)
  • 実行

% java kawa.repl test2.scm
MyModule required.
0012
%

(require)でモジュールの呼び出し、(make)でオブジェクトの生成。クラスCのメソッドmethod()はC:methodという関数に対応しているようだ。

JavaSchemeJavaの呼び出し

当然可能。Main.javaに以下のメソッドを追加して、

    public Object run(int n) throws Throwable
    {
        ModuleBody.setMainPrintValues(true);

        scheme.loadClass("test2");

        // (run n)
        Object obj = LList.list2(env.getSymbol("run"), n);
        return scheme.eval(obj, env);
    }

main()の中の main.hello("Java"); の下に以下の文を追加する。

            main.run(1);

Main.javaとtest1.scmをコンパイルしてから実行する。

% java Main
MyModule called.
Hello, "Scheme" world!
Hello, "Java" world!
0012
1123
%

その他

Kawaは"A framework written in Java for implementing high-level and dynamic languages"ということで、Scheme以外の言語も扱える(らしい)。具体的には、Common LispEmacs Lispが現時点で利用可能。これを利用してJEmacsの開発も行われている(が、まだ実用レベルにはなっていないようだ)。