Fast Clojure dependency hot-loading
source link: https://www.eigenbahn.com/2020/05/06/fast-clojure-deps-auto-reload
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
Fast Clojure dependency hot-loading
May 6, 2020About Clojure
Clojure is great. I love it.
But like most great things it has its downsides.
One of which is the startup time.
There are workarounds to this1 but the lost common solution is to try avoiding as much possible to restart your process.
That’s not too much of an issue as Clojure was built with interactive development in mind (hello Mr REPL) and additional tooling (such as CIDER) push things a step further.
But there is one case when you still usually have to restart the REPL: when adding dependencies.
Pomegranate
Pomegranate is a library that allows to import dependencies without having to restart your process.
You of course first need to have it in your project dependencies2:
(defproject pomegranate-test "0.1.0-SNAPSHOT"
;; ...
:dependencies [[org.clojure/clojure "1.10.1"]
[clj-commons/pomegranate "1.2.0"]])
Then, let’s say you wanted to import clj-http
. In your REPL, you just have to do the following:
(use '[cemerick.pomegranate :only (add-dependencies)])
(add-dependencies :coordinates '[[clj-http "3.10.1"]]
:repositories (merge cemerick.pomegranate.aether/maven-central
{"clojars" "https://clojars.org/repo"}))
youWereExpectingMeToTypeAllThis.png
That’s a big hunk of text to copy paste…
What could we do to make our life easier?
Mhm, wait a second, we are using Emacs with CIDER, don’t we?
Let’s hack something quick in elisp!
Emacs helper command
(defun prf/cider/send-to-repl (sexp &optional eval ns)
"Send SEXP to Cider Repl. If EVAL is t, evaluate it.
Optionally, we can change namespace by specifying NS."
(cider-switch-to-repl-buffer ns)
(goto-char cider-repl-input-start-mark)
(delete-region (point) (point-max))
(save-excursion
(insert sexp)
(when (equal (char-before) ?\n)
(delete-char -1)))
(when eval
(cider-repl--send-input t)))
(defun prf/clj/pomegranate-dep (dep)
"Format a Clojure Pomegranate dependency import for DEP."
(concat
(format
"%s"
;; NB: this is clojure!
`(use '[cemerick.pomegranate :only (add-dependencies)]))
(s-replace-all
`(("\\." . ".")
("mydep" . ,dep))
(format
"%S"
;; NB: this is clojure!
`(add-dependencies :coordinates '[mydep]
:repositories (merge cemerick.pomegranate.aether/maven-central
{"clojars" "https://clojars.org/repo"}))))))
(defun prf/cider/inject-pomegranate-dep (&optional dep ns)
"Auto-import DEP in the current Clojure Repl using Pomegranate.
Optionally, we can change namespace by specifying NS."
(interactive)
(setq dep (or dep (read-string "Dep: ")))
(prf/cider/send-to-repl (prf/clj/pomegranate-dep dep) t ns))
Now, from any project buffer (Clojure source file of REPL), we can just:
M-x prf/cider/inject-pomegranate-dep
Dep: [clj-http "3.10.1"]
And this should auto-switch to our running REPL buffer and let Pomegranate do its magic!
Alternative implementation: cljr-refactor
Thye excellent clj-refactor package provides the command cljr-hotload-dependency
(<prefix> + hd
) that does the same thing. It also provides command add-project-dependency
(<prefix> + ap
) that does the same but also adds it to your project.clj file.
Notes
Like compiling to a GraalVM native image or using the AppCDS feature of commercial Oracle JVM. ↩
This is unecessary when using the CIDER REPL in Emacs, as Pomegranate already gets side-loaded in this case ↩
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK