Jolt — Clojure on Chez Scheme

https://jolt-lang.github.io/

A Clojure implementation on Chez Scheme. Self-hosted compiler, Clojure-compatible standard library, no JVM.

by @yogthos@lemmygrad.ml

(yooo Awesome project!!!)

14 points · 10 comments · view on lemmy.world

10 Comments

yogthos@lemmy.ml · 7 pts · 35d

Thanks! It's been pretty fun working on it, and starting to get to a state where you can actually do useful things with it. :)

davel@lemmy.ml · 2 pts · 35d (8 replies)

davel@lemmy.ml · 4 pts · 35d (7 replies)

@yogthos@lemmy.ml, I’d be interested in a blog post of your thought process on 1) taking up this project given how many Clojure dialects are already out there, and 2) why you chose Chez Scheme as the host. I imagine you considered Racket as well, since it’s also hosted on CS, which would mean you had to weigh Racket’s “language-oriented language” features against a panoply of other factors.

yogthos@lemmy.ml · 4 pts · 35d (6 replies)

Sure, I can answer some of that here. The motivation is that I wanted to see how hard it would be to make a Clojure dialect that could run existing Clojure libraries for the JVM. My intuition was that most of them use a fairly small chunk of the Java standard library, and if I shimmed out stuff like IO and a few other things then stuff should just work. Turned out to be a bit more work than I anticipated, but a lot of the libraries now really do work same as on the JVM. And the bonus here is that I was able to run the full test suites for the libraries on Jolt and verify they really do behave as intended.

The problem with any Clojure dialect is ultimately in building out the ecosystem around it, and you tend to get a chicken and egg problem as a result. Being able to use a lot of the existing libraries out of the box largely side steps the issue. Aside from what I've shimmed out in the core language, it's also trivial to make your own adapter library providing whatever shim you need if you want to get an existing Clojure library running that requires some other part of Java standard library. So, migration process is fairly painless.

Java runtime evolved in an era of enterprise architecture, designed for massive application servers like Tomcat or WebSphere which were effectively their own operating systems. In that model the application server would greedily consume all available host memory and run continuously for months on end, making long JVM startup phases and heavy initial resource footprints perfectly acceptable trade-offs. But that monolithic design is completely at odds with how modern web applications are typically built, especially in fast-paced startup environments where the engineering culture has shifted toward microservices. Today, you typically want to deploy fleets of isolated containers that boot instantly and can dynamically scale horizontally to handle sudden traffic spikes.

The main reason for using Chez is that it provides IR along with JIT and generational GC. The last is really important since Clojure creates a lot of short lived objects with its persistent data structures. Chez is also pretty compact, and you can get a standalone binary for Jolt under 10 megs with it. Incidentally, Racket is itself built on Chez, so I do have compatibility with it as well. Also worth noting that choice of Chez is incidental from user perspective since Jolt has its own standalone binary and doesn't require Chez to be installed to work.

And that brings us to the second benefit which is having access to Chez, Racket, and all the native C/C++/Rust libraries. For example, I made a Reagent style library on top of GTK, and have a TodoMVC example using it here. You can just develop a native UI using nREPL, and have it reload live when you make changes. I even have a full on Quake style FPS demo working with it.

Performance is already decent beating Java in some cases, being 12x in most, and around 78x in the worst case.

davel@lemmy.ml · 1 pts · 34d (5 replies)

I’m not terribly familiar with Chez, and don’t know why Hombrew and Mise provide different names for the main executable: chez vs scheme.

$ ls -l /opt/homebrew/Cellar/chezscheme/10.4.1/bin/ | cut -d' ' -f11-

chez -> ../lib/csv10.4.1/tarm64osx/chez
petite -> ../lib/csv10.4.1/tarm64osx/petite
scheme-script -> ../lib/csv10.4.1/tarm64osx/scheme-script

$ ls -l ~/.local/share/mise/installs/chezscheme/latest/bin/ | cut -d' ' -f15-

petite -> ../lib/csv10.4.1/tarm64osx/petite
scheme -> ../lib/csv10.4.1/tarm64osx/scheme
scheme-script -> ../lib/csv10.4.1/tarm64osx/scheme-script

After symlinking chez to scheme, I ran into:

$  bin/joltc -e '(+ 1 2)'
joltc: source mode; run 'make devboot' to precompile
cannot find compatible chez.boot in search path
  "%x:%x/../lib/csv%v/%m:%x/../../boot/%m"
Abort trap: 6              bin/joltc -e '(+ 1 2)'

Which is above my pay grade. Workaround was echo -e '#!/bin/sh \nscheme $@' > ~/.local/bin/chez


This is on a MacBook Neo:

$ time /opt/homebrew/bin/joltc -e 1
1

real	0m0.103s
user	0m0.074s
sys	0m0.026s

Using Chez Scheme (time ./bin/joltc -e 1) was about a 4x slower start time.

yogthos@lemmy.ml · 2 pts · 34d (4 replies)

Yeah, the whole Chez set up is basically just for the development of Jolt itself, and bin/joltc gets evaluated in a Chez runtime when you run it from the project. The homebrew version is the optimized release binary. Also no idea why the naming is different, seems like unnecessary confusion. :)

davel@lemmy.ml · 1 pts · 34d (3 replies)

BTW, you probably do want to support scheme as the executable: https://cisco.github.io/ChezScheme/csug/use.html#g6

When Chez Scheme is installed with default options, entering the command scheme at the shell's prompt starts an interactive Scheme session. The command petite does the same for Petite Chez Scheme.

yogthos@lemmy.ml · 2 pts · 34d (2 replies)

I do have my own repl though if you just run joltc you'll get an interactive Clojure session that way too, or you can do joltc nrepl-server to get nREPL going and connect an editor.

davel@lemmy.ml · 2 pts · 34d

Yes, this is only really relevant for Jolt contributors, not users.