Common Lisp / Kawa Scheme interop via OpenLDK.
Kawa Scheme, created by Per Bothner, compiles Scheme code to Java bytecode and runs on the JVM. OpenLDK is a JVM implemented in Common Lisp; it transpiles Java bytecode to Common Lisp code. In SBCL, that Common Lisp code is then compiled to native assembly. Since both share the same SBCL process and heap, cl-kawa enables deep interoperability between Common Lisp and Scheme with no serialization or process boundaries.
Project status: technology demonstration. Not a performant or production ready implementation.
- Evaluate Scheme from Common Lisp (strings or s-expressions).
- Call Scheme procedures from Common Lisp.
- Register Common Lisp functions and call them from Scheme.
- Exchange basic values (numbers, strings, booleans, lists) across the boundary.
- SBCL
- OpenLDK
- Java 8 JDK (
JAVA_HOMEpointing to a JRE withlib/rt.jar) - Kawa 3.1.1 JAR (downloaded automatically
via Maven, or manually placed in
libs/)
- Ensure prerequisites are installed and
JAVA_HOMEis set. - Download Kawa (or let Maven fetch it) into
libs/askawa-3.1.1.jar. - Load the ASDF system from your Common Lisp image.
(asdf:load-system :cl-kawa)
;; Initialize the runtime (point to the Kawa JAR)
(kawa:startup :classpath "libs/kawa-3.1.1.jar")
;; Evaluate Scheme expressions -- as s-expressions or strings
(kawa:eval '(+ 1 2)) ; => 3
(kawa:eval '(string-length "hello")) ; => 5
(kawa:eval '(list 1 2 3)) ; => (1 2 3)
(kawa:eval "(* 6 7)") ; => 42 (strings still work)
;; Look up a Scheme procedure and call it from CL
(let ((add (kawa:lookup "+")))
(kawa:funcall add 10 20)) ; => 30
;; Register a CL function so Scheme can call it
(kawa:register "cl-square" (lambda (x) (* x x)))
(kawa:eval '(cl-square 7)) ; => 49hello.lisp demonstrates the full Common Lisp → Scheme → Java
interop chain in a single SBCL process:
(format t "~A~%"
(kawa:eval '(let ((s (|java.lang.String| (string-append "Hello" ", " "World!"))))
(|s:toUpperCase|))))
;; prints: HELLO, WORLD!This single expression crosses three language boundaries:
- Common Lisp calls
kawa:evalwith a quoted s-expression. - Kawa Scheme assembles the greeting with
string-appendand wraps it in ajava.lang.String. - Java's
String.toUpperCase()runs -- its bytecode was transpiled by OpenLDK into Common Lisp, then compiled by SBCL to native x86-64 assembly.
The result flows back through Scheme's value system into Common Lisp
and is printed by format. No FFI, no sockets, no serialization --
just nested function calls inside a single Lisp image.
LDK_CLASSPATH=libs/kawa-3.1.1.jar \
JAVA_HOME=/path/to/java8/jre \
sbcl --load hello.lispJAVA_HOME: path to a Java 8 JRE that containslib/rt.jar.LDK_CLASSPATH: optional classpath override for OpenLDK.
Initialize the Kawa Scheme runtime. classpath is a colon-separated
string of JAR paths. Safe to call multiple times (subsequent calls are
no-ops).
Evaluate a Scheme expression. expr can be a string of Scheme source
or a Common Lisp s-expression (symbols are automatically downcased,
quote becomes ', t/nil become #t/'()). Returns the result
converted to a Common Lisp value.
Look up a Scheme binding by name. Returns the raw Java/Kawa object
(typically a Procedure).
Call a Scheme procedure with Common Lisp arguments. Arguments are automatically converted to Kawa values; the result is converted back.
Register a Common Lisp function as a Scheme procedure. The function
becomes callable from Scheme code via kawa:eval.
Convert a Java/Kawa object to a Common Lisp value. Handles integers, floats, strings, booleans, and lists. Returns the object unchanged if no conversion applies.
Convert a Common Lisp value to a Java/Kawa object. Handles integers, floats, strings, and cons cells.
Create a new Scheme environment, optionally inheriting from a parent.
| Kawa type | Common Lisp type |
|---|---|
gnu.math.IntNum |
integer |
gnu.math.DFloNum |
double-float |
java.lang.String |
string |
java.lang.Boolean |
T / NIL |
gnu.lists.Pair |
cons |
gnu.lists.LList (empty) |
NIL |
Or manually:
LDK_CLASSPATH=libs/kawa-3.1.1.jar \
JAVA_HOME=/path/to/java8/jre \
sbcl --load test.lisp- Designed as a proof-of-concept; performance and completeness are not goals.
- The conversion layer only handles basic scalar and list types.
- Requires Java 8 because OpenLDK depends on
rt.jar.
cl-kawa was written by Anthony Green and is distributed under the terms of the MIT license.