It has been a little over a year since the release of nREPL 0.6. Moreover, it has been over nine months since I wrote that nREPL 0.7 was getting a native EDN transport. So, where the heck is going with nREPL? Is it stuck again so soon after its revival? I can assure it that’s definitely not the case and despite the lack of new releases nREPL has been making some good progress. I’m also extremely happy to announce that nREPL 0.7 is finally here! COVID-19 did a lot of bad things, but it also cleared my busy schedule rather dramatically, so lately I’ve been trying to catch up on all of my important projects. There has to be something good coming out of this horrible madness, right?

So, what’s new and noteworthy? I already wrote about the EDN transport, so I guess I can skip that part. There are two other big additions:

  • Clients can now inject code remotely in a running server on demand. This probably doesn’t make any sense to most of you, but it’s insanely cool and powerful. We’re calling this feature “side-loading”, following the example of unrepl, which pioneered it.
  • nREPL responses are now covered with clojure.spec.

Let me expand a bit on the sideloader, as I think it’s the most interesting addition in 0.7. In simple terms it will allow a client to provide missing code on demand. Here’s a short example:

;; -> init sideloading
{:op      "sideloader-start"
 :id      "1"
 :session "x"}

;; -> try to require a missing namespace
{:op      "eval"
 :id      "2"
 :session "x"
 :code    (quote (require '[foo.bar :as bar])
                 (bar/qaz))}
;; <- lookup for foo.bar
{:id      "1"
 :session "x"
 :status  :sideloader-lookup
 :type    "resource"
 :name    "foo/bar.clj"}
;; -> providing resource
{:id      "3"
 :session "x"
 :op      "sideloader-provide"
 :type    "resource"
 :name    "foo/bar.clj"
 :content "<base64 package>"}
;; <- ack of provided resource
{:id      "3"
 :session "x"
 :status  :done}
;; <- result of eval
{:id      "2"
 :session "x"
 :value   "Qaz"
 :status  :done}

What’s happening here? The client tries to require foo.bar, but the library is not available locally. That’s why nREPL asks the client to provide the missing library. The client responds with a Base64 encoded payload that’s the missing resource in question, this gets loaded in nREPL and the evaluation continues. The potential to utilize this is huge, because now clients can enhance running servers (e.g. with code completion) without the need to include additional dependencies when booting nREPL. I wanted to implement this in CIDER first, but I was crazy busy the past few months, so there’s a good chance that some other client author is going to be beat me to it. That is totally fine! Please, beat me to this and build something cool with the sideloader!1 There’s a bit of documentation about it here.

There have been other small tweaks and improvements in the latest release as well. For all the gory details you should check out the release notes. One thing that’s not covered there is that nREPL’s documentation got some love, so it’s better than ever. Still, there’s a lot of room for improvement, especially when it comes to the protocol’s specification, so it’s easier for folks to build alternative implementations and new clients.

I already have some (modest) plans for nREPL 0.8 - the main focus there is going to be the addition of built-in completion and info ops, so that clients would have access to even more useful functionality out-of-the-box. You can read more about this here. There are also plenty of other open tickets, so if you’d like to help out you’ll have your pick of some mighty fine options (many of which are (somewhat) beginner friendly).

I’m hoping that the improvements in nREPL 0.7 and the planned improvements for nREPL 0.8 are going to contribute to the creation of more and better clients for nREPL in the years to come.

I’ll also use this post to share some cool general updates about nREPL:

  • Chris Badahdah is working on a ClojureScript port of nREPL.
  • Michiel Borkent is working on a Babashka port of nREPL. His basic nREPL implementation is also a good example of how easy it is to implement the nREPL protocol.
  • Maurício Szabo is considering adding nREPL support to Chlorine.
  • Oliver Caldwell is considering adding nREPL support to Conjure.
  • Pratik Karki announced at IN/Clojure that LightTable will be dropping their custom nREPL and adopting the modern nREPL + everything in CIDER’s Orchard.

Such developments make me extremely happy and reaffirm my belief that nREPL will continue to play a central part in the future of Clojure’s development tooling (and hopefully beyond Clojure). By the way, you might also want to check out my recent IN/Clojure presentation on that topic.

Time to wrap it up now. Special thanks go to Shen Tian for his work on the EDN transport and the sideloader. Christophe Grand also deserves a lot of credit of coming up with the sideloader in the first place and building a prototype for nREPL. Thanks a lot, guys! You rock!

That’s all I have for you today! Keep hacking!

P.S. If you haven’t spent all of your money on toilet paper, you might consider supporting nREPL via GitHub Sponsors and OpenCollective.

  1. A couple of days after I wrote this post vim-iced became the first editor that added support for the side-loading functionality! You can find more details here