8

ホスト Java プログラムから Clojure をスクリプト言語として使用しようとしています。ドメイン固有の Java API を呼び出す Clojure スクリプト コードをエンド ユーザーが記述できるようになるという考えです。実行時に、ホストの Java プログラムはエンドユーザーの Clojure スクリプトを評価します (これにより、ドメイン API が呼び出されます)。そこで、地形を探索するための非常にシンプルなプロトタイプから始めました。

ドメイン

package a.problem.domain;

public class Domain {

    public Domain() { }

    public String defaultMsg() {
        return "default";
    }

    public String passBackMsg(String s) {
        return s;
    }
}

ホスト Java プログラム

(エンド ユーザーの Clojure スクリプトは、簡単にするためにハードコードされています)

String script = "(do                                    "+
                "  (import '(a.problem.domain Domain))  "+
                "  (.defaultMsg (Domain.))              "+
                ")                                      ";
System.out.println(RT.var("clojure.core", "eval").invoke(RT.var("clojure.core","read-string").invoke(script)));

(ここから取られたコードスニペット)

ここまでは順調ですね。

ただし、2 番目のメソッド (引数が必要なメソッド) を呼び出す方法が見つかりませんでした。代わりに、実行時に Clojure スクリプトを動的に生成し、プレースホルダーをドメイン メソッドpassBackMsgの呼び出し結果を表すリテラルに置き換えるという手段に頼りました。明らかに、これは不十分であり、あまり効果がありません (Clojure スクリプトに java.sql.Connection を渡したい場合はどうすればよいでしょうか?)。

では、ホスト Java プログラムからpassBackMsgメソッドを呼び出すにはどうすればよいでしょうか。

次のことを試すと:

String script = "(ns foo)                                   "+ 
                "(import '(a.problem.domain Domain))        "+
                "(defn numberToString [s]  (                "+
                "  (.passBackMsg (Domain.) s)               "+
                "))                                         ";
RT.var("clojure.core", "eval").invoke(RT.var("clojure.core","read-string").invoke(script)); // line-A
System.out.println(RT.var("foo", "numberToString").invoke(33)); // line-B

私は得る:

java.lang.IllegalStateException: Can't change/establish root binding of: *ns* with set

...ラインAで。ns なしで試してみると、次のようになります。

RT.var("user", "numberToString").invoke(33)

(名前空間引数のないvarメソッドが表示されないため、「ユーザー」は大げさな推測です)

私は得る:

java.lang.IllegalStateException: Attempting to call unbound fn: #'user/numberToString"

...ラインBで。

4

1 に答える 1

4

これを試して:

String script = "(do                                    "+
                "  (import '(a.problem.domain Domain))  "+
                "  (fn [s]                " +
                "   (.passBackMsg (Domain.) s)               "+
                "))                                         ";

IFn fn = (IFn)RT.var("clojure.core", "eval").invoke(RT.var("clojure.core","read-string").invoke(script));

fn.invoke("hello");

更新: 以下のサンプル コードは正常に動作します。

package hello_clj;

import clojure.lang.RT;
import clojure.lang.IFn;

public class Main {

    public String passBackMsg(String s) {
        return s;
    }

    public static void main(String[] args) {
        String script = "(do (import 'hello_clj.Main) (fn [s] " + 
                        "(.passBackMsg (Main.) s) ))";

        IFn fn = (IFn)RT.var("clojure.core", "eval").invoke(RT.var("clojure.core","read-string").invoke(script));
        System.out.print(fn.invoke("Hello"));
    }

}
于 2013-02-21T08:18:13.433 に答える