Как вызвать Clojure Macros из Java? - PullRequest
10 голосов
/ 13 июля 2011

Можно ли в любом случае вызывать макросы Clojure из Java?

Вот что я пытаюсь сделать:

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)

Ответы [ 4 ]

7 голосов
/ 13 июля 2011

Вам придется накатить свой собственный с 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\"]");
}
5 голосов
/ 13 июля 2011

Проблема базовая.

invoke (и его сестра, apply) используются для функций.

Макросы не являются функциями, поэтому их нельзя вызывать. Макросы должны быть скомпилированы. В нормальных Лиспах они могут быть просто eval'd или макрорасширенными или как угодно. И через 10 минут осмотра, очевидно, у Clojure нет простой функции RT.eval (String script), чтобы сделать это легко.

Но это то, что нужно сделать. Вам нужно скомпилировать и выполнить это.

Я видел пакет , который интегрирует Clojure с Java JSR 223, и у ИТ есть eval (потому что 223 имеет eval). Но я не знаю, а) хорош ли пакет или б), в каком направлении вы хотите идти.

1 голос
/ 13 июля 2011

Отказ от ответственности: я очень мало знаю о clojure (мой опыт работы с другими функциональными языками и Java)

Мой инстинкт кишки, однако, говорит, что проблема в prxml.invoke(). Здесь мысль состоит в том, что этот оператор вычисляется слишком рано и отправляет результат в withOutStr (вместо того, чтобы позволить withOutStr оценивать его).

Глядя только на источники в сети ... особенно RT , Var & AFn , а также документ clojure для без str Я бы попробовал что-то вроде:

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

Редактировать: Также я подозреваю, что он может вызывать макросов clojure из Java, в противном случае функция isMacro () в Var выглядит довольно глупо ...

Редактировать 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 скрытых параметра См коммит 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 голосов
/ 08 мая 2016

Если вы хотите выполнить код Clojure из C # или Java, используйте функцию 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.

...