So, if you’re not familiar with Chlorine, or any of my tooling, let me get you a refresher. If you don’t want to read it all, the TL;DR; is – Chlorine is a Socket REPL plug-in for the Clojure, and ClojureScript languages. It also supports nREPL, although to a lesser extend, and it works on the Atom editor. It also contains a VSCode version called Clover, and I tried to port it to NeoVIM and I called it Clematis.

And now, it’s gone.

For historical reasons, I wanted to rename the project for a while – so that Chlorine would stay as a “socket REPL for the Atom editor” and the new plug-in would be the “Chlorine:Next” or something like this. I tried to like the name Alya, but in the end I never did…

… and then, I stopped working with Clojure. Like, at all. For a while I haven’t touched a single line of Clojure code, although I still use ClojureScript on my personal plug-ins. But ClojureScript have a weird problem – I can’t develop two plug-ins written in ClojureScript at the same time, so I need to compile everything, except for one, plug-in for “release”. Which is annoying to say the least, so that’s why I started the “Star Ring” metapackage – to make all plug-ins a “single, unified codebase”. Unfortunately, this have its own fractal of problems that I won’t discuss now.

So not that I’m now working with Ruby, I needed better tooling for Ruby otherwise I would go crazy. Leaving the “REPL-Driven Development” of Clojure behind is a nightmare, because it’s such a great tool and it somehow “grows” in you in such a way that you can’t imagine working in a codebase when you can’t evaluate your code all the time. And that’s where Lazuli appears… but again, because of the problems with not being able to develop Lazuli and Chlorine at the same time, Chlorine’s development stalled… and I decided to stop working on it to work in Lazuli.

Lazuli was made by copying all of Chlorine code, patching everything that was Clojure-related to be Ruby-aware, implementing a nREPL server for Ruby with some special powers, and then customize some of the UI of Chlorine. This translates to “Lazuli is Chlorine, but for Ruby”. Even the UI is almost the same:

Chlorine and Lazuli UIs

But… I still need Chlorine to develop Lazuli – it’s codebase is all ClojureScript. So I decided on a bold move: to make Lazuli the “new version of Chlorine”.

Some changes had to made to Lazuli, then to Duck-REPLed, which means now Duck knows about Ruby code, and know how to extract top-blocks and blocks for Ruby code.

So, what to expect from Lazuli? In the next few days, I will publish the first version of it that supports Ruby and Clojure, and that will be the replacement of Chlorine. The first thing to know is that everything that I told about Chlorine/Next applies to Lazuli because it’s, well, the same code base – I did not delete anything to make support for Ruby, and basically every config and aspect that was made to be customizable in repl-tooling are still present in Lazuli and its companion library Tango. So, what Lazuli did was just take some extra effort to “patch” things that weren’t supposed to be replaced, like how to evaluate code, etc; but these are also fixed now too, and all Clojure and Ruby support share code, meaning, again, an improvement in Ruby support will also benefit Clojure – both languages are now considered “first-class”.

Lastly, I’ll also drop support for socket-repl, and mainly because socket-repl never got the attraction that was so hyped. Things that were supposed to be implemented, like upgradeable REPLs and tooling never really went anywhere, and none the promises were ever fulfilled. Some tools also tried to use the “prepl”, which is a programmable REPL that’s on Clojure core now, but these tools also went back and reverted to nREPL, because honestly, it’s simply better.

So, to summarize, in my opinion, the only place that socket-repl’s was successful was to discourage tooling authors and other developers to implement better nREPL clients, or to even implement nREPL servers – with its final destiny to be forgotten (as far as I know, nobody uses socket-REPL for anything nowadays, and that includes not only tooling but also to debug production code).

Ok, with this rant out of my chest, here’s what Lazuli improves: it have a faster render, and the reason for that is that I dropped Reagent – not because it was slow, but because, in the past, Reagent was used to update lots of places in the UI, and to handle some extreme cases that appeared because I decided to use UNREPL over Socket REPL – and in this specific scenarion Reagent made sense. In the newer version with nREPL, this doesn’t happen anymore, so Regent (well, actually, React) was kind of getting in my way.

But, the power to design things in hiccup and let it be transformed to a DOM is very powerful, so Lazuli still allows for custom renderers with Hiccup, but now these are converted directly to HTML elements instead of React. Interactive renderer also works, and it works by listening to the state atom and when something changes, updating the whole renderer at once. This is, obviously, slower than Reagent but honestly, speed was never an issue with interactive renderer, and because things are just DOM elements now, if one wants to have better control over specific elements the browser’s DOM API is the same – you can even, if you want, re-add React to the pipeline, which was not possible with the older version where everything was already React. This also simplifies, a lot, the usage of HTML components, NPM modules, and other stuff – it’s simply easier.

Also, Lazuli will have something that I am quite excited to add: the config file now works exactly the same as other editor windows connected to the REPL. This basically mean that when you are editing some config, you can evaluate code, and it won’t be evaluated in the Clojure REPL – it will be evaluated inside Pulsar, using the editor’s Javascript environment, and it’ll have all capabilities that the editor have – including autocomplete (for the config file and every function that you define inside of it!), goto definition, interactive renderer, etc. There will also be some “debug helpers” – for example, if you want to custom render something, now you can define some variable and it will be present in the config “environment”, meaning you can “REPL-Debug” your renderers! This is all a big effort to make customizable rendering and customization more “first-class” in Lazuli, and the best part – this will work for Ruby too (meaning you will be able to customize Ruby rendering!)

The future

Remember when I said, in some posts in the past, I want to add some FlowStorm debugger support? So, now Lazuli (because of Ruby) have a new concept called the “watch points”. A “watch point” is basically some “pre-saved binding” for Ruby. So, suppose you have the following code:

class User
  attr_accessor :name, :surname

  def full_name
    @name + @surname
  end
end

In Ruby, this defines a class with two attributes. To debug a code, you will need to instantiate a User, set the user.name and user.surname, then call user.full_name. This is obviously extremely tedious and completely eliminates the fun of a “REPL-Driven Development” approach. That’s why Lazuli have “watch points” – in Ruby, if some code interacts with User, and it somehow calls full_name, at line 4 you’ll have a “watch point” – meaning that everything that you evaluate at line 4 or 5 (this will change in the future, I want to add the “watch point begin” and “watch point end” to avoid “bleeding” the watch point to unrelated methods) will have all variable bindings, both local and global, saved and in scope – meaning that if you evaluate @name you will get a meaningful result and not the answer “this is undefined”.

For Clojure, it might be possible to implement “watch points” using the trace information that FlowStorm have. What this means in practice is: suppose you have the Clojure code:\

(defn handle-hello [req]
  {:status 200 :body (str "Hello, " (-> req :params :name))})

Suppose this code is not returning what you want. Usually, what we do, is to add a (def req req) to line 2, so we can “save” the req local variable as global. In Lazuli I want to make it so that you won’t need this – this tracing information is already present in FlowStorm, so we just need to somehow capture this info and use it. I’m still unsure if this can work, but if it can, we can get some deep levels of introspection never seen before!

So, with this… we say goodbye to Chlorine. It will be archived, but it’ll always be available if anyone wants to check how a (probably only) plug-in that uses Socket-REPL works. It still works, and probably it will ever work, with the Pulsar editor, now that Atom is gone.

And welcome Lazuli! Let’s evolve REPL-Driven development to the extreme, and bring our friends (Ruby, and maybe Python) together on this journey!