简体   繁体   English

如何从 Java 调用 Clojure 宏?

[英]How to call Clojure Macros from Java?

Is there anyway to call Clojure macros from Java?有没有办法从 Java 调用 Clojure 宏?

Here is what I am trying to do:这是我正在尝试做的事情:

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 writes to *out* by default which is why I need to wrap it with the macro with-out-str which returns the string. prxml 默认写入 *out*,这就是为什么我需要用返回字符串的宏 with-out-str 来包装它。

I am getting this error:我收到此错误:

 [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)

You'll have to roll your own withOutStr.您必须自己使用OutStr。

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\"]");
}

The problem is basic.问题是基本的。

invoke (and its sister, apply) are used for functions.调用(及其姐妹应用)用于函数。

Macros are not functions, so they can not be invoked.宏不是函数,因此不能调用它们。 Macros need to be compiled.需要编译宏。 In normal Lisps, they could just be eval'd or macroexpanded or whatever.在普通的 Lisp 中,它们可以被评估或宏扩展或其他。 And in 10m of looking around, apparently Clojure doesn't have a simple RT.eval(String script) function to make this easy.在 10m 的环顾中,显然 Clojure 没有简单的 RT.eval(String script) function 来简化此操作。

But, that's what needs to be done.但是,这就是需要做的。 You need to compile and execute this.您需要编译并执行它。

I saw a package that integrates Clojure with Java JSR 223, and IT has an eval (because 223 has an eval).我看到一个package集成了 Clojure 和 Java JSR 223,它有一个 eval(因为 223 有一个 eval)。 But I don't know a) if the package is any good or b) if this is the direction you want to go.但我不知道 a)package 是否好,或者 b)如果这是您想要的 go 的方向。

Disclaimer: I know very little about clojure (my experience has been with other functional languages and Java)免责声明:我对 clojure 知之甚少(我的经验是使用其他函数式语言和 Java)

My gut instinct however says the problem is around prxml.invoke() .然而,我的直觉说问题出在prxml.invoke()周围。 The thought here being that that statement evaluates too soon, and sends the result to withOutStr (instead of letting withOutStr evaluate it).这里的想法是该语句评估得太快,并将结果发送到 withOutStr (而不是让 withOutStr 评估它)。

Looking at the sources online alone... notably RT , Var & AFn as well as the clojure doc for with-out-str I would try something along the lines of:仅查看在线资源...尤其是RTVarAFn以及 clojure doc for with-out-str我会尝试以下内容:

String stringXML = (String) withOutStr.invoke(RT.list(prxml,"[:Name \"Bob\"]"));

Edit: Also I would suspect that it is able to call clojure macros from java otherwise the isMacro() function on Var seems rather silly...编辑:我还怀疑它能够从 java 调用 clojure 宏,否则 Var 上的 isMacro() function 似乎相当愚蠢

Edit 2: Downloaded clojure, and tried it... doesn't work so ignore this for now.编辑 2:下载 clojure,并尝试过...不起作用所以暂时忽略它。

Edit 3: with-out-str apparently requires 2 parameters so:编辑 3:with-out-str 显然需要 2 个参数,因此:

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

has an output of: [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__))] has an output of: [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__))]

I wonder if that will evaluate to something useful, or not (not sure if I'm correct on the bindings, have to figure out how to evaluate the cons through Java.我想知道这是否会评估为有用的东西(不确定我在绑定上是否正确,必须弄清楚如何通过 Java 评估缺点。

Edit 4: Poking through the Compiler and more code, it seems macros actually have 2 hidden parameters.编辑 4:通过编译器和更多代码,似乎宏实际上有 2 个隐藏参数。 See the commit 17a8c90查看提交 17a8c90

Copying the method in the compiler I have:在我拥有的编译器中复制方法:

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__)))

Which seems a bit more promising, but it still requires the evaluation of the let expression which seems to have a special case in the compiler.这似乎更有希望,但它仍然需要评估在编译器中似乎有特殊情况的 let 表达式。

If you want to execute Clojure code from C# or Java, use Clojure's load-string function.如果要从 C# 或 Java 执行 Clojure 代码,请使用 Clojure 的加载字符串ZC1C4252674C1ZA98。 This will take an ordinary string and execute it exactly as if you'd typed the string in the REPL.这将采用一个普通字符串并完全按照您在 REPL 中键入字符串的方式执行它。 That includes dealing with macros.这包括处理宏。

Here's a short version of the C# code.这是 C# 代码的简短版本。 The Java version won't be far from that. 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();
        }
    }

You can see the full version of the sample program here .您可以在此处查看示例程序的完整版本。 It includes a lot of sample code to demonstrate Clojure interop.它包含大量示例代码来演示 Clojure 互操作。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM