Welcome! Please see the About page for a little more info on how this works.

0 votes
in Compiler by

clojure.lang.Compiler has a method with the signature

public static Object eval(Object form, boolean freshLoader)

but the freshLoader argument is ignored since https://github.com/clojure/clojure/commit/2c2ed386ed0f6f875342721bdaace908e298c7f3

Is there a good reason this still needs to be "hotfixed" like this?

We would like to provide our own ClassLoader for eval to manage the lifecycle of the generated classes.

4 Answers

0 votes

Comment made by: stu

This is not part of the public API of Clojure. We would need to understand more about the use case.

0 votes

Comment made by: sfnelson

Sorry Stuart, we only just noticed your response, thanks to the publicity around http://ashtonkemerling.com/blog/2016/06/11/my-increasing-frustration-with-clojure/

I'm going to try and explain our use-case a bit for context, but please understand that this issue was simply a question about what appears to be inconsistent behaviour from a function that looks and smells a lot like a public API function (who would have thought 'eval' would be private :-)

Our company uses Clojure to build a cloud platform that does computation in response to user requests using modules loaded from a database. The modules are trusted code, but they are independent from our main platform so there might be multiple versions of the same module running at the same time (we want to avoid namespace collisions). We've looked at lots of approaches to keep modules from interfering with each other including containers and micro services running separate JVMs, but in order to have acceptable response times for simple queries like "is this input valid?" we want to run simple queries in the same JVM as the web server.

The general approach we use to answer a query from a user is to build a namespace for our computation (which might require loading other namespaces from the computation module), then eval the expression in the context we've built. We have a LRU cache for module namespaces but we still end up with a lot of metaspace churn for the eval, which we mitigate by using a clojure interpreter to handle simple queries (eval is too slow).

When we implemented our LRU modules namespace cache we wanted to experiment with loading module namespaces into their own class loader to help track class lifecycle and GC, and hopefully to allow multiple namespace versions to coexist. We've since concluded that this is impractical because clojure has so many global lookups related to namespaces, so now we preprocess module namespaces and perform name mangling on load, and explicitly unregister loaded namespaces when the cache expires so that their classes can be collected. We avoid using language features like multimethods and protocols that use globals in modules.

Once again, we're not looking for the Clojure team to implement containers for us (though that would certainly be a nice feature to have!), this was simply an inconsistency we noticed between API and implementation. What is the expected entry point for Java-interop eval?

0 votes

Comment made by: alexmiller

You can use the Clojure Java API as documented at http://clojure.github.io/clojure/javadoc/clojure/java/api/Clojure.html.

A basic example that read and eval'ed code from a Java string would look like:

import clojure.java.api.Clojure;
import clojure.lang.IFn;

// ...

IFn read = Clojure.var("clojure.core", "read-string");
IFn eval = Clojure.var("clojure.core", "eval");

Object code = read.invoke("(+ 1 1)");
Object result = eval.invoke(code);
System.out.println("read: " + code + ", eval: " + result);

You could do more complicated things though like generate a string for a namespace and call {{load-string}} via the same mechanism as above.

0 votes
Reference: https://clojure.atlassian.net/browse/CLJ-1463 (reported by alex+import)