Using Lisp or Scheme for runtime configuration of Java programs

后端 未结 4 1322
梦毁少年i
梦毁少年i 2021-02-05 22:59

I have now seen several projects ending at a point where the actual configuration depended on things only available at run-time.

The typical way to configure a Java prog

相关标签:
4条回答
  • 2021-02-05 23:35

    Try SISC, it is a reasonably small (300kb jar) Scheme implementation with no bells and whistles. Glueing it with Java is trivial, and an execution speed is quite impressive for a pure interpreter.

    0 讨论(0)
  • 2021-02-05 23:37

    I know you want a small size and runtime. Scheme is the usual choice for easy embedding, and would be my first choice. But I have no information on that field, though. My second choice is Clojure:

    • Not that small; the jar is ~3 MB
    • Overkill for a simple config reading, but the extra features can be safely ignored
    • ~ Easy to call from Java: Calling clojure from java
    • Full, excellent access to JVM
    • Might invoke a small extra to the startup, which can be too much (see below)
    • Now that I tried replicating the example's functionality with Clojure, I feel that Clojure isn't well suited for these kind of scripts (rc, etc). Clojure 1.3 will address some of that startup penalty but I don't know the magnitude of speed improvements coming

    A respective code with using Clojure:

    import clojure.lang.RT;
    import clojure.lang.Var;
    import clojure.lang.Compiler;
    import java.io.FileReader;
    import java.io.FileNotFoundException;
    
    public class ClojTest {
        public static void main(String[] args) throws Exception {
            try {
                Compiler.load(new FileReader("hello.clj"));
            } catch(FileNotFoundException e) { return; }
    
            System.out.println("Message: '"+ RT.var("user", "msg").get() +"'");
    
            // Values
            int answer = (Integer) RT.var("user", "answer").get();
    
            // Function calls
            System.out.println(RT.var("user", "countdown").invoke(42));
        }
    }
    

    with hello.clj being:

    (ns user)
    (defn countdown [n]
      (reduce + (range 1 (inc n))))
    
    (def msg "Hello from Clojure!")
    (def answer (countdown 42))
    

    Running time java ClojTest for a while yields in an average of 0.75 seconds. Clojure compiling the script has quite a penalty!

    0 讨论(0)
  • 2021-02-05 23:46

    Good, much smaller solution is an embedded Scheme for Java. Out of many implementations, I found and tested JScheme. (API looks okay: http://jscheme.sourceforge.net/jscheme/doc/api/index.html)

    A simple main program in Java:

    import jscheme.JScheme;
    import jscheme.SchemeException;
    import java.io.*;
    public class SchemeTest {
        public static void main(String[] args) {
            JScheme js = null;
            try {
                js = new JScheme();
                js.load(new FileReader("config.scm"));
            } catch (FileNotFoundException e) { return; }
    
            System.out.println("Message: '" + js.eval("msg") + "'");
    
            // Values
            int answer = (Integer) js.eval("answer");
    
            // Function calls
            System.out.println(js.call("countdown", 42));
        }
    }
    

    And an example config.scm:

    (define (countdown x)
      (define (loop x acc)
        (if (= x 0)
          acc
          (loop (- x 1) (+ acc x))))
      (loop x 0))
    
    ;;; config variables
    (define msg "Hello from JScheme!")
    ;; tail calls are optimized as required
    (define answer (countdown 42))
    

    The scheme is being interpreted at startup every time, so this is good choice for configurations. Advantages to JScheme include:

    • good JVM integration
    • apparently a complete Scheme implementation
    • smallish size (572 kB)
    • fast, somewhat easily called from Java

    Benchmark update: this code runs in 0.13 seconds time. Compared to the Clojure version's time this is pretty fast.

    0 讨论(0)
  • 2021-02-05 23:58

    Clojure is excellent, it is embeddable and has very good interoperability for calling Java libraries. However it is not particularly small (you get a full compiler and quite a decent runtime library included). Still worth considering if you have a broader requirement for this kind of functionality - I've found Clojure to work as an excellent dynamic "glue" for Java code.

    Otherwise, your best bet is probably a tiny embedded Scheme interpreter.

    It may be possible to use the early (1998) version of JScheme at this link, which is only about a 30k jar file: http://norvig.com/jscheme.html

    Otherwise, it's probably possible to write something even more minimal in a few hundred lines of Java.... it's probably only a weekend project considering how small the core of Scheme is. The following page is quite interesting if you want to write a mini-interpreter for Scheme: http://archives.evergreen.edu/webpages/curricular/2000-2001/fofc00/eval.html

    0 讨论(0)
提交回复
热议问题