Llamando a Clojure desde Java


Answers

A partir de Clojure 1.6.0, hay una nueva forma preferida de cargar e invocar las funciones de Clojure. Ahora se prefiere este método para llamar a RT directamente (y reemplaza a muchas otras respuestas aquí). El javadoc está here - el principal punto de entrada es clojure.java.api.Clojure .

Para buscar y llamar a una función de Clojure:

IFn plus = Clojure.var("clojure.core", "+");
plus.invoke(1, 2);

Las funciones en clojure.core se cargan automáticamente. Otros espacios de nombres se pueden cargar a través de require:

IFn require = Clojure.var("clojure.core", "require");
require.invoke(Clojure.read("clojure.set"));

IFn se pueden pasar a funciones de orden superior, por ejemplo, el siguiente ejemplo pasa plus para read :

IFn map = Clojure.var("clojure.core", "map");
IFn inc = Clojure.var("clojure.core", "inc");
map.invoke(inc, Clojure.read("[1 2 3]"));

La mayoría de los IFn en Clojure se refieren a las funciones. Algunos, sin embargo, se refieren a valores de datos no funcionales. Para acceder a estos, use deref lugar de fn :

IFn printLength = Clojure.var("clojure.core", "*print-length*");
IFn deref = Clojure.var("clojure.core", "deref");
deref.invoke(printLength);

Algunas veces (si usa alguna otra parte del tiempo de ejecución de Clojure), es posible que deba asegurarse de que el tiempo de ejecución de Clojure esté correctamente inicializado. Llamar a un método en la clase Clojure es suficiente para este propósito. Si no necesita llamar a un método en Clojure, basta con simplemente hacer que la clase se cargue (en el pasado hubo una recomendación similar para cargar la clase RT, que ahora se prefiere):

Class.forName("clojure.java.api.Clojure") 
Question

La mayoría de los principales éxitos de google para "llamar a clojure desde Java" están desactualizados y recomendamos usar clojure.lang.RT para compilar el código fuente. ¿Podría ayudar con una explicación clara de cómo llamar a Clojure desde Java suponiendo que ya ha creado un contenedor del proyecto Clojure y lo incluyó en el classpath?




Esto funciona con Clojure 1.5.0:

public class CljTest {
    public static Object evalClj(String a) {
        return clojure.lang.Compiler.load(new java.io.StringReader(a));
    }

    public static void main(String[] args) {
        new clojure.lang.RT(); // needed since 1.5.0        
        System.out.println(evalClj("(+ 1 2)"));
    }
}



EDITAR: Escribí esta respuesta hace casi tres años. En Clojure 1.6 hay una API adecuada exactamente con el propósito de llamar a Clojure desde Java. Por favor, la respuesta de Alex Miller para obtener información actualizada: https://.com/a/23555959/202121

Respuesta original de 2011:

Tal como lo veo, la forma más simple (si no se genera una clase con compilación AOT) es usar clojure.lang.RT para acceder a funciones en clojure. Con él puedes imitar lo que hubieras hecho en Clojure (sin necesidad de compilar las cosas de manera especial):

;; Example usage of the "bar-fn" function from the "foo.ns" namespace from Clojure
(require 'foo.ns)
(foo.ns/bar-fn 1 2 3)

Y en Java:

// Example usage of the "bar-fn" function from the "foo.ns" namespace from Java
import clojure.lang.RT;
import clojure.lang.Symbol;
...
RT.var("clojure.core", "require").invoke(Symbol.intern("foo.ns"));
RT.var("foo.ns", "bar-fn").invoke(1, 2, 3);

Es un poco más detallado en Java, pero espero que quede claro que los fragmentos de código son equivalentes.

Esto debería funcionar siempre que Clojure y los archivos fuente (o archivos compilados) de su código Clojure estén en el classpath.




Otra técnica que funciona también con otros lenguajes sobre JVM es declarar una interfaz para las funciones que desea llamar y luego usar la función 'proxy' para crear una instancia que las implemente.