【问题标题】:How to call Clojure Macros from Java?如何从 Java 调用 Clojure 宏?
【发布时间】:2011-10-04 02:46:11
【问题描述】:

是否可以从 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 中的宏何时扩展?
  • 是的,您可以从 Java 调用 Clojure 宏,但您需要调用 Clojure 读取器(以便调用编译器并扩展宏),而不是特定函数(因为函数已经编译! )。可能值得查看这个问题的答案:stackoverflow.com/questions/2181774/calling-clojure-from-java
  • mikera,我想你在做点什么。我想知道是否有一种好方法可以在 Java 中的宏上调用 clojure 阅读器。

标签: java clojure clojure-contrib


【解决方案1】:

如果您想从 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();
        }
    }

您可以看到完整版的示例程序here。它包含大量演示 Clojure 互操作的示例代码。

【讨论】:

    【解决方案2】:

    您必须自己使用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\"]");
    }
    

    【讨论】:

      【解决方案3】:

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

      然而,我的直觉认为问题出在prxml.invoke() 附近。这里的想法是该语句评估得太快,并将结果发送到 withOutStr (而不是让 withOutStr 评估它)。

      仅查看在线资源...尤其是 RTVarAFn 以及 with-out-str 的 clojure 文档,我会尝试以下方式:

      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 个隐藏参数。见the 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 表达式。

      【讨论】:

        【解决方案4】:

        问题是基本的。

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

        宏不是函数,所以它们不能被调用。需要编译宏。在普通的 Lisp 中,它们可以被评估或宏扩展或其他。在 10m 的范围内,显然 Clojure 没有一个简单的 RT.eval(String script) 函数来简化此操作。

        但是,这是必须要做的。你需要编译并执行它。

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

        【讨论】:

        • 我发现我可以使用以下代码在 Java 中获取 Clojure 阅读器和评估器:RT.var("clojure.core", "eval").invoke(RT.var("clojure.core) ", "read-string").invoke("这里是 Clojure 字符串。")
        • 啊,太好了。是的,我很惊讶在 RT 上找不到类似的电话,但这正是您想要做的。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-08-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-01-13
        相关资源
        最近更新 更多