Glojure: A Hosted Clojure Interpreter for Go
Glojure is a hosted interpreter for Clojure implemented in Go. It enables developers to write Clojure code that has direct, seamless access to Go libraries, mirroring the relationship Clojure has with the Java Virtual Machine (JVM) and Java frameworks.
Key Technical Architecture
Hosted Language Design
Glojure is designed as a "hosted" language, meaning it is implemented in terms of its host language (Go). This architecture ensures that all Go values can be used as Glojure values and vice versa, allowing for high-fidelity interop between the two languages.
Execution Model
Glojure utilizes a tree-walk interpreter. While this approach allows for rapid development and flexibility, it is noted as having limited performance compared to bytecode interpreters or compiled languages.
Go Interop and Standard Library Support
Glojure provides out-of-the-box interop with a wide range of Go standard library packages. To avoid conflicts with Clojure's namespaced symbols (which use /), Go package names containing slashes are replaced with colons (:).
Default supported packages include:
fmt,os,io,net/http(accessed asnet:http),time,sync,context,reflect, andregexp.
For developers needing access to additional Go packages, Glojure provides a tool called gen-import-interop. This tool generates a Go file that exports a function to add specific package exports to the Glojure package map, allowing custom Go libraries to be integrated into the Clojure environment.
Usage and Deployment
Standalone Tooling
The glj command-line tool provides a full Clojure development experience, including a REPL with Vi and Emacs editing modes, multiline editing, tab completion, and persistent history. It supports evaluating expressions directly via the -e flag or running .glj scripts as standalone programs.
Embedding in Go Applications
Glojure can be embedded as a scripting language within Go binaries. This allows developers to add scriptable configuration, create plugin systems, or mix Go's performance with Clojure's expressiveness.
Integration example:
package main
import (
"fmt"
"github.com/glojurelang/glojure/pkg/runtime"
)
func main() {
result := runtime.ReadEval(`(defn factorial [n] (if (<= n 1) 1 (* n (factorial (dec n))))) (factorial 5)`)
fmt.Printf("5! = %v\n", result)
}
Comparison with Other Go-based Clojure Implementations
| Aspect | Glojure | Joker | let-go |
|---|---|---|---|
| Hosted | Yes | No | No |
| Extensible Go Interop | Yes | No | No |
| Concurrency | Yes | Yes (GIL) | Yes |
| Tooling (Linter) | No | No | No |
| Execution | Tree-walk Interpreter | Tree-walk Interpreter | Bytecode Interpreter |
Data Type Mapping
To maintain compatibility between Go and Clojure, Glojure maps types as follows:
- Integers:
longmaps toint64,intmaps to Go's platform-dependentint. - Floating Point:
doublemaps tofloat64,floatmaps tofloat32. - Bytes:
bytemaps to Go's unsignedbyte(differing from JVM's signed bytes). - Arbitrary Precision:
BigIntandBigDecimalwrap*big.Intand*big.Floatrespectively. - Characters:
charis implemented as a tagged rune (type Char rune).
Community Insights
Community discussion highlights that Glojure is viewed as a promising implementation due to its focus on proper interop. However, some users have noted the lack of of security or sandboxing features for embedded use cases, and the potential performance overhead inherent to tree-walk interpretation.