Hotload libraries in Clojure Projectsλ︎
When starting a REPL process the dependencies listed in the project deps.edn
file are added to the class path. To add further dependencies the REPL has to be restarted to include new libraries added to the deps.edn
file.
Practicalli REPL Reloaded workflow allows new dependencies to be added to a running REPL process, negating the need to restart the REPL process which would loose the current REPL state.
Hotload REPLλ︎
Start a REPL with an alias that includes the add-libs
library.
Start a terminal REPL with the :repl/reloaded
alias and connect
Run a Clojure REPL from the editor (jack-in command) configured with the :dev/reloaded
alias or :lib/hotload
alias in an Editor jack-in command or other REPL startup command.
Alternatively, run a Terminal REPL and connect the editor to that REPL process (connect command)
Practicalli REPL Reloaded Configuration
Rich Comment Blockλ︎
Use a rich comment block or a dev/user.clj
file to require the clojure.tools.deps.alpha.repl
namespace and write add-libs
expressions to hot-load libraries.
A rich comment block ensures add-libs
code is only evaluated manually by a developer.
(comment
(require '[clojure.tools.deps.alpha.repl :refer [add-libs]])
(add-libs '{http-kit/http-kit {:mvn/version "2.5.1"}})
)
Hotload Exampleλ︎
Create a web server from scratch, serving pages generated from hiccup, with all libraries hot-loaded as the code is being written. Demonstrates that it is possible to write an application when only starting the REPL once.
(comment
;; run REPL with :lib/hotload alias
(require '[clojure.tools.deps.alpha.repl :refer [add-libs]])
;; hotload the libraries required for the server
(add-libs
'{http-kit/http-kit {:mvn/version "2.5.1"}})
;; => (http-kit/http-kit)
;; Require the namespace from the http-kit library
(require '[org.httpkit.server :as app-server])
;; Define a handler for http requests
(defn welcome-page
[request]
{:status 200
:body "Welcome to the world of Clojure CLI hotloading"
:headers {}})
;; Start the application server with the handler
(app-server/run-server #'welcome-page {:port (or (System/getenv "PORT") 8888)})
;; Visit http://localhost:8888/ to see the welcome-page
;; Hotload Hiccup to generate html for the welcome page
(add-libs '{hiccup/hiccup {:mvn/version "2.0.0-alpha2"}})
(require '[hiccup.core :as hiccup])
(require '[hiccup.page :as hiccup-page])
;; Create a page template
(defn page-template [content]
(hiccup-page/html5
{:lang "en"}
[:head (hiccup-page/include-css "https://cdn.jsdelivr.net/npm/bulma@0.9.0/css/bulma.min.css")]
[:body
[:section {:class "hero is-info"}
[:div {:class "hero-body"}
[:div {:class "container"}
[:h1 {:class "title"} (:title content) ]
[:p {:class "subtitle"} (:sub-title content)]]]]]))
;; Check the page template returns HTML
(page-template {:title "Hotload Libraries in the REPL"
:sub-title "REPL driven development enables experimentation with designs"})
;; redefine the welcome page to call the page template
(defn welcome-page
[request]
{:status 200
:body (page-template {:title "Hotload Libraries in the REPL"
:sub-title "REPL driven development enables experimentation with designs"})
:headers {}})
;; Visit http://localhost:8888/ and refresh the page to see the new welcome-page
)
Using add-libs with project deps.edn
A project deps.edn
file can also be used to hotload libraries with add-lib
. This has the advantage that newly added libraries become part of the normal project dependency configuration.
Add a namespace definition to the deps.edn
file to help editors understand the deps.edn
file is being used for code. Use the #_
comment reader macro with the namespace definition to only evaluate this code manually as a developer.
Add the add-libs
expression after the :deps
key so that it is easy to slurp in the existing and new dependencies as a single hash-map. Use the comment reader macro #_
to only evaluate this code manually.
To hotload, remove the #_
temporarily and slurp in the hash-map of dependencies, placing a '
at the start of the hash-map. Add the name and version of libraries to hotload in the hash-map. Evaluate the add-libs
expression which should return a list of new namespaces added.
Once hotload has finished, barf the hash-maps of dependencies from the add-libs
expression, removing the '
. Add the #_
to the add-libs
expression and save the file.
The hotloaded libraries are now available by requiring their namespaces. If the REPL is restarted, the new dependencies will be included in the Classpath as they are now part of the project configuration.
;; ---------------------------------------
;; Project Configuration with Hotload
;; ---------------------------------------
;; Hotload requires
#_(ns deps.edn
(:require [clojure.tools.deps.alpha.repl :refer [add-libs]]))
;; Project configuration
{:paths
["src" "resources"]
:deps
#_ (add-libs)
{org.clojure/clojure {:mvn/version "1.10.1"}
http-kit/http-kit {:mvn/version "2.5.1"}
hiccup/hiccup {:mvn/version "2.0.0-alpha2"}}
:aliases {}
Live Coding videoλ︎
See the REPL driven development video by Sean Corfield for this technique.
Jump to 23 minutes into the video to see this form of hotload in action.
Rich-comment-hotload
Practicalli Clojure LSP Config includes the
rich-comment-hotload
snippet which adds a rich comment block with a require forclojure.tools.deps.alpha
and anadd-libs
expression, making it very quick to add this code.deps-maven
anddeps-git
snippets help ensure the correct syntax is used for theadd-libs
expression for each library dependency to be added.