如何从 Java 调用 Clojure 宏?

发布于 2024-11-19 12:41:51 字数 778 浏览 6 评论 0原文

有没有办法从 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)

Is there anyway to call Clojure macros from Java?

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.

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)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

耀眼的星火 2024-11-26 12:41:51

你必须自己推出 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\"]");
}

You'll have to roll your own 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\"]");
}
月光色 2024-11-26 12:41:51

问题是基本的。

invoke(及其姐妹,apply)用于函数。

宏不是函数,因此无法调用它们。需要编译宏。在普通的 Lisp 中,它们可以被评估或宏扩展或其他什么。经过 10m 的环顾,显然 Clojure 没有一个简单的 RT.eval(String script) 函数来使这变得简单。

但是,这就是需要做的。您需要编译并执行它。

我看到一个将 Clojure 与 Java JSR 223 集成的软件包,并且 IT 有一个 eval (因为 223 有一个 eval)。但我不知道a)这个包是否好,或者b)这是否是你想要的方向。

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. And in 10m of looking around, apparently Clojure doesn't have a simple RT.eval(String script) function to make this easy.

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). But I don't know a) if the package is any good or b) if this is the direction you want to go.

别低头,皇冠会掉 2024-11-26 12:41:51

免责声明:我对 clojure 知之甚少(我的经验是使用其他函数式语言和 Java),

但我的直觉告诉我问题出在 prxml.invoke() 上。这里的想法是该语句计算得太早,并将结果发送到 withOutStr (而不是让 withOutStr 计算它)。

单独查看在线源...特别是 RT Var & ; AFn 以及 clojure 文档对于 with-out-str 我会尝试一些行:

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

编辑:另外我怀疑它能够从java调用clojure宏,否则Var上的isMacro()函数看起来相当愚蠢...

编辑2:下载了clojure,并尝试了...不起作用所以暂时忽略这个。

编辑 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));

输出为: [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 评估缺点。

编辑 4:浏览编译器和更多代码,似乎宏实际上有 2 个隐藏参数。请参阅 commit 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 表达式进行求值,这在编译器中似乎有一个特殊情况。

Disclaimer: I know very little about clojure (my experience has been with other functional languages and Java)

My gut instinct however says the problem is around prxml.invoke(). The thought here being that that statement evaluates too soon, and sends the result to withOutStr (instead of letting withOutStr evaluate it).

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:

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...

Edit 2: Downloaded clojure, and tried it... doesn't work so ignore this for now.

Edit 3: with-out-str apparently requires 2 parameters so:

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

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.

Edit 4: Poking through the Compiler and more code, it seems macros actually have 2 hidden parameters. See the commit 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.

淡看悲欢离合 2024-11-26 12:41:51

如果您想从 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 互操作。

If you want to execute Clojure code from C# or Java, use Clojure's load-string function. This will take an ordinary string and execute it exactly as if you'd typed the string in the REPL. That includes dealing with macros.

Here's a short version of the C# code. The Java version won't be far from that.

    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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文