2011-07-13 23 views
10

¿Hay alguna forma de invocar macros Clojure desde Java?Cómo llamar a Clojure Macros desde Java?

Aquí es lo que estoy tratando de hacer:

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 escribe a cabo * * por defecto que es por eso que necesito para envolverlo con la macro-con-str cabo que devuelve la cadena.

estoy recibiendo este 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) 
+0

¿Se puede invocar una macro desde Java? No sé mucho sobre Clojure, pero en Lisp, el lector evalúa las macros antes de compilar el código. Si Clojure es de la misma manera, entonces llamar desde Java puede ser demasiado tarde. ¿Cuándo se amplían las macros en Clojure? –

+1

Sí, puede invocar macros Clojure desde Java pero necesita invocar el lector Clojure (para que el compilador se llame y expanda la macro), no la función específica (¡ya que una función ya está compilada!). Puede valer la pena revisar las respuestas a esta pregunta: http://stackoverflow.com/questions/2181774/calling-clojure-from-java – mikera

+0

mikera, creo que estás en algo. Me pregunto si hay una buena manera de llamar al lector clojure en una macro en Java. – mudge

Respuesta

1

responsabilidad: Sé muy poco sobre clojure (mi experiencia ha sido con otros lenguajes funcionales y Java)

Mi instinto sin embargo dice que el problema es de alrededor prxml.invoke(). La idea es que esa afirmación se evalúa demasiado pronto y envía el resultado a withOutStr (en lugar de dejar que withOutStr lo evalúe).

En cuanto a las fuentes en línea solo ... sobre todo RT, VarAFn &, así como el documento de clojure with-out-str me gustaría probar algo en la línea de:

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

Editar: También sospecharía que es capaz de llamar macros clojure desde Java, de lo contrario la función isMacro() en Var parece bastante tonta ...

Editar 2: descargué clojure, y lo probé ... no funciona, así que ignóralo por ahora.

Datos 3: con-out-str aparentemente requiere 2 parámetros así:

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

tiene una potencia de: [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__))]

Me pregunto si eso va a evaluar en algo útil, o no (no estoy seguro si estoy correcto en los enlaces, tengo que descubrir cómo evaluar los inconvenientes a través de Java.

Editar 4: examinando el compilador y más código, parece que las macros tienen realmente 2 parámetros ocultos. Véase the commit 17a8c90

Copiar el método en el compilador que tengo:

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

que parece un poco más prometedor, pero todavía requiere la evaluación de la expresión dejar que parece tener un caso especial en el compilador.

5

El problema es básico.

invoke (y su hermana, aplicar) se utilizan para las funciones.

Las macros no son funciones, por lo que no se pueden invocar. Las macros necesitan ser compiladas. En Lisps normales, simplemente podrían ser evaluados o macroexpandidos o lo que sea. Y en 10 m de mirar alrededor, aparentemente Clojure no tiene una función simple RT.eval (String script) para hacer esto fácil.

Pero, eso es lo que hay que hacer. Necesitas compilar y ejecutar esto.

Vi un package que integra Clojure con Java JSR 223, y TI tiene un eval (porque 223 tiene un eval). Pero no sé a) si el paquete es bueno o b) si esta es la dirección que desea seguir.

+1

Encontré que podía usar lo siguiente para obtener el lector y evaluador de Clojure en Java: RT.var ("clojure.core", "eval"). Invocar (RT.var ("clojure.core", "cadena de lectura")). invoca ("Clojure string here.") – mudge

+0

ah excelente. Sí, me sorprendió que no pude encontrar una llamada similar simplemente por RT, pero eso es exactamente lo que quieres hacer. –

7

Tendrá que hacer el suyo conOutStr.

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

Sí, esto está bien, gracias ! – mudge

0

Si desea ejecutar código Clojure desde C# o Java, utilice la función de Clojure load-string. Esto tomará una cadena común y la ejecutará exactamente como si hubiera tecleado la cadena en REPL. Eso incluye tratar con macros.

Aquí hay una versión corta del código C#. La versión de Java no estará lejos de eso.

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

Se puede ver la versión completa del programa de ejemplo here. Incluye una gran cantidad de código de muestra para demostrar interoperabilidad Clojure.