Frage Wie Clojure Makros von Java aufrufen?


Gibt es denn Clojure-Makros von Java aus aufzurufen?

Hier ist, was ich versuche zu tun:

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 schreibt standardmäßig nach * out *, weshalb ich es mit dem Makro wracken muss, das die Zeichenkette zurückgibt.

Ich erhalte diesen Fehler:

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

10
2017-07-13 00:43


Ursprung


Antworten:


Sie müssen Ihre eigenen mit OutStr rollen.

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

7
2017-07-13 06:31



Das Problem ist grundlegend.

invoke (und seine Schwester, apply) werden für Funktionen verwendet.

Makros sind keine Funktionen, daher können sie nicht aufgerufen werden. Makros müssen kompiliert werden. In normalen Lisps konnten sie einfach evaluiert oder Makro-erweitert werden oder was auch immer. Und in 10m sieht sich Clojure anscheinend keine einfache RT.eval (String script) -Funktion an, um das zu vereinfachen.

Aber das muss getan werden. Sie müssen dies kompilieren und ausführen.

Ich sah ein Paket das integriert Clojure mit Java JSR 223, und IT hat ein eval (weil 223 ein eval hat). Aber ich weiß nicht a) ob das Paket gut ist oder b) ob das die Richtung ist, in die du gehen willst.


5
2017-07-13 03:54



Disclaimer: Ich weiß sehr wenig über Clojure (meine Erfahrung war mit anderen funktionalen Sprachen und Java)

Mein Bauchgefühl sagt jedoch, dass das Problem um ist prxml.invoke(). Der Gedanke hierbei ist, dass diese Anweisung zu früh ausgewertet wird und das Ergebnis an withOutStr gesendet wird (anstatt es mit OutStr auszuwerten).

Die Quellen online betrachten ... besonders RT, Var & AFN sowie der Clojure Doc für ohne-str Ich würde etwas versuchen in der Art von:

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

Edit: Auch würde ich vermuten, dass es Clojure-Makros von Java aufrufen kann, ansonsten scheint die isMacro () -Funktion auf Var ziemlich albern zu sein ...

Edit 2: Heruntergeladene Clojure, und versuchte es ... funktioniert nicht so ignorieren Sie dies für jetzt.

Edit 3: with-out-str benötigt anscheinend 2 Parameter:

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

hat eine Ausgabe von: [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__))]

Ich frage mich, ob sich das auf etwas Nützliches auswirkt oder nicht (ich bin mir nicht sicher, ob ich in den Bindings richtig bin, muss herausfinden, wie man die Nachteile über Java auswertet).

Edit 4: Durch den Compiler und mehr Code, scheinen Makros tatsächlich 2 versteckte Parameter. Sehen das Commit 17a8c90

Kopieren der Methode im Compiler habe ich:

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

Was etwas vielversprechender erscheint, erfordert aber immer noch die Auswertung des Let-Ausdrucks, der im Compiler einen Sonderfall zu haben scheint.


1
2017-07-13 01:50



Wenn Sie Clojure-Code von C # oder Java ausführen möchten, verwenden Sie Clojure Ladezeichenfolge Funktion. Dies nimmt eine gewöhnliche Zeichenkette und führt sie genau so aus, als ob Sie die Zeichenkette in der REPL eingegeben hätten. Dazu gehört der Umgang mit Makros.

Hier ist eine kurze Version des C # -Codes. Die Java-Version wird nicht weit davon entfernt sein.

    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();
        }
    }

Sie können die Vollversion des Beispielprogramms sehen Hier. Es enthält eine Menge Beispielcode, um Clojure interop zu demonstrieren.


0
2018-05-08 16:19