Using Lisp or Scheme for runtime configuration of

2019-03-15 11:09发布

问题:

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 program is to read one or more property files according to some application specific rules and then take action depending on their values. At one point or another this breaks down and you need actual program logic in your configuration which then can be indicated with a flag and adding code to your application which then handles the flag.

I was wondering if a tiny Lisp configuration reader module might be a better option, where the file to be read is not a property file but a Lisp program which is then eval'ed to create a final datastructure representing the configuration. A minimal set of functions in the runtime library would then allow string manipulation and perhaps even calling into the JVM. Just think of "construct an URL based on the current hostname".

I am not interested in a full Lisp engine with bells and whistles but just a small library for this purpose which can be enclosed in even small programs without a large jar containing the Lisp engine.

So does such a library exist?

  • Small size
  • Just need read file + eval and the ability to read the resulting data structure from the main Java program
  • A small Lisp runtime library
  • Speed is of lesser importance.
  • Actively maintained.

Suggestions?


Edit 2012-01-20: I initially found all the candidates undesirable, but have decided to use this as a Maven exercise on the side with the 1998 jscheme 1.4 release. Project at https://github.com/ravn/jscheme-1998


Edit 2012-12-11: It turned out that the integration layer between the program being interpreted in Scheme and the host Java program was more important that I originally thought, and that I needed in a project to be able to provide classes with JAX-WS annotaions at runtime, which I could not do with JScheme but I could do with Groovy. The idea of a small configuration library which allows code snippets in the libraries is still valid, but I ended up needing more for it to be useful.

回答1:

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.



回答2:

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!



回答3:

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.



回答4:

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



标签: java lisp scheme