如何从Java中调用Clojure宏?

10

有没有办法从Java中调用Clojure宏?

这是我想要做的事情:

RT.var("clojure.core", "require").invoke(Symbol.create("clojure.contrib.prxml"));
Var prxml = RT.var("clojure.contrib.prxml", "prxml");
Var withOutStr = RT.var("clojure.core", "with-out-str");
String stringXML = (String) withOutStr.invoke((prxml.invoke("[:Name \"Bob\"]")));

默认情况下,prxml会写入*out*,这就是我需要使用with-out-str宏进行包装的原因,该宏返回字符串。

我遇到了以下错误:

 [java] java.lang.IllegalArgumentException: Wrong number of args (1) passed to: core$with-out-str
 [java]     at clojure.lang.AFn.throwArity(AFn.java:437)
 [java]     at clojure.lang.RestFn.invoke(RestFn.java:412)
 [java]     at clojure.lang.Var.invoke(Var.java:365)
 [java]     at JavaClojure.xml.main(Unknown Source)

你能从Java中调用宏吗?我对Clojure不是很了解,但在Lisp中,宏是由读取器在编译代码之前进行评估的。如果Clojure也是这样的话,那么从Java中调用可能会太晚了。在Clojure中,宏何时被展开? - Ryan Stewart
1
是的,你可以从Java调用Clojure宏,但需要调用Clojure Reader(以便调用编译器并展开宏),而不是特定的函数(因为函数已经编译!)。值得查看这个问题的答案:https://dev59.com/ZHI95IYBdhLWcg3wtwf4 - mikera
mikera,我认为你有所发现。我想知道是否有一种好的方法在Java中调用Clojure阅读器上的宏。 - mudgen
4个回答

7

您需要自己编写withOutStr的代码。

class YourClass {
    static final Var withBindings = RT.var("clojure.core", "with-bindings*");
    static final Var list = RT.var("clojure.core", "list*");
    static final Var out = RT.var("clojure.core", "*out*");
    static final Var prxml = RT.var("clojure.contrib.prxml", "prxml");

    static String withOutStr(IFn f, Object args...) {
        StringWriter wtr = new StringWriter();
        withBindings.applyTo(list.invoke(RT.map(out, wtr), f, args));
        return wtr.toString();
    }

    ...

    String stringXML = withOutStr(prxml, "[:Name \"Bob\"]");
}

5
问题很基础。
invoke(以及它的姊妹函数apply)用于函数。
宏不是函数,因此不能被调用。宏需要编译。在正常的Lisps中,它们可以被eval'd或macroexpanded或其他方式处理。而且在查看了10m之后,显然Clojure没有一个简单的RT.eval(String script)函数来使这个过程变得容易。
但是,这就是需要做的事情。您需要编译和执行此操作。
我看到一个package将Clojure与Java JSR 223集成,并且它具有一个eval(因为223具有一个eval)。但我不知道a)包是否好用或b)这是否是您想要走的方向。

1
我发现我可以使用以下代码在Java中获取Clojure的读取器和求值器:RT.var("clojure.core", "eval").invoke(RT.var("clojure.core", "read-string").invoke("这里是Clojure字符串。")) - mudgen
啊,太好了。是的,我很惊讶我找不到一个类似的调用只是从RT,但这正是你想要做的。 - Will Hartung

1
免责声明:我对Clojure知之甚少(我的经验主要来源于其他函数式编程语言和Java)。
然而,我的直觉告诉我问题在`prxml.invoke()`周围。我的想法是该语句评估得太早,并将结果发送到`withOutStr`(而不是让`withOutStr`对其进行评估)。
仅查看在线源...特别是RT, VarAFn以及Clojure文档中的with-out-str,我会尝试类似以下的东西:
String stringXML = (String) withOutStr.invoke(RT.list(prxml,"[:Name \"Bob\"]"));

编辑:我还怀疑它能否从Java调用Clojure宏,否则Var上的isMacro()函数似乎有些愚蠢...

编辑2:下载了Clojure并尝试了一下...目前不起作用,所以请忽略这个。

编辑3:显然,with-out-str需要两个参数:

final Cons consXML = (Cons) withOutStr.invoke(prxml, RT.list("[:Name \"Bob\"]"));
final Object[] objs = RT.seqToArray(consXML);
System.out.println(Arrays.toString(objs));

输出结果为:[clojure.core/let, [s__4095__auto__ (new java.io.StringWriter)], (clojure.core/binding [clojure.core/*out* s__4095__auto__] (clojure.core/str s__4095__auto__))]

我不确定这是否会产生有用的结果(关于绑定,我不确定自己是否正确,必须弄清楚如何通过Java评估cons。

编辑4:浏览编译器和更多代码后,发现宏实际上有两个隐藏参数。请参见提交17a8c90

复制编译器中的方法:

final ISeq form = RT.cons(withOutStr, RT.cons(prxml, RT.cons("[:Name \"Bob\"]", null)));
final Cons consXML = (Cons) withOutStr.applyTo(RT.cons(form, RT.cons(null, form.next())));
System.out.println(consXML.toString());
// Output: (clojure.core/let [s__4095__auto__ (new java.io.StringWriter)] (clojure.core/binding [clojure.core/*out* s__4095__auto__] #'clojure.contrib.prxml/prxml "[:Name \"Bob\"]" (clojure.core/str s__4095__auto__)))

这似乎更有前途,但它仍需要评估 let 表达式,这在编译器中似乎有一个特殊情况。


0
如果您想从C#或Java执行Clojure代码,请使用Clojure的load-string函数。这将接受一个普通字符串并执行它,就像您在REPL中键入字符串一样。这包括处理宏。
这是C#代码的简短版本。Java版本也不会有太大差异。
    private static readonly IFn LOAD_STRING = Clojure.var("clojure.core", "load-string");

    private void executeButton_Click(object sender, EventArgs e)
    {
        try
        {
            object result = LOAD_STRING.invoke(sourceTextBox.Text);
            if (null == result)
                resultTextBox.Text = "null";
            else
                resultTextBox.Text = result.ToString() + " (" + result.GetType().Name + ")";
        }
        catch (Exception ex)
        {
            resultTextBox.Text = ex.ToString();
        }
    }

您可以在此处查看示例程序的完整版本。其中包括大量的样例代码,以演示Clojure互操作性。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接