TIL #Clojure :reload-ing when :require-ing
eval
Tags: #Clojure, #TIL, #Babashka
When using Babashka I often add libraries like babashka/fs and bahaska/http-client as separate dependencies despite them being shipped with Babashka.
This 'version pinning' allows for using vars that are not necessarily in the latest bb
, or (when creating something for public consumption) makes the code compatible with an older version of bb
.
What you shouldn't forget when adding a library that also ships with bb
, is to :reload
when requiring a namespace from said library:
(ns bb.my-project
(:require [clojure.string :as string]
[babashka.fs :as fs] :reload ;; <-
[clojure.set :as set]))
;; Use recently introduced var
(prn #'fs/gunzip)
Because you often see the reload-flag right after some namespace, like in the example above, I was under the impression that it reloads that specific namespace.
But nope, turns out that it ensures all namespaces in the require-clause are reloaded.
This can be demonstrated by using the :verbose
flag that Clojure supports (another TIL):
$ clojure -M -e "(require '[clojure.set] '[clojure.data])
(require '[clojure.set] :reload
'[clojure.data] :verbose)"
# prints:
# (clojure.core/load "/clojure/set")
# (clojure.core/load "/clojure/data")
Both set
and data
are reloaded. Without :reload
this wouldn't print anything.
The reason I ran into this: requiring a Babashka pod in a require-clause that contains a :reload
won't work:
;; bb.edn
{:paths ["bb"]
:pods {org.babashka/go-sqlite3 {:version "0.1.0"}}}
;; bb/my_project/db.clj
(ns my-project.db
(:require [pod.babashka.go-sqlite3 :as sqlite]
[babashka.fs :as fs] :reload))
(defn -main [& _args]
(prn (sqlite/query ":memory:" ["SELECT 1 + 1 AS sum"])))
The error is similar to what happens when requiring the pod without adding it to bb.edn
:
$ bb -m my-project.db
----- Error --------------------------------------------------------------------
Type: java.io.FileNotFoundException
Message: Could not locate pod/babashka/go_sqlite3.bb, pod/babashka/go_sqlite3.clj or pod/babashka/go_sqlite3.cljc on classpath.
Location: /Users/gert/projects/playground/bb/pods/bb/my_project/db.clj:2:3
----- Context ------------------------------------------------------------------
1: (ns my-project.db
2: (:require [pod.babashka.go-sqlite3 :as sqlite]
^--- Could not locate pod/babashka/go_sqlite3.bb, pod/babashka/go_sqlite3.clj or pod/babashka/go_sqlite3.cljc on classpath.
3: [babashka.fs :as fs] :reload))
So the fix is to have a separate require-clause containing the reload:
(ns my-project.db
(:require [pod.babashka.go-sqlite3 :as sqlite])
(:require :reload
[babashka.fs :as fs]))
The Clojure styleguide doesn't touch on it, but I find it helpful to put the :reload
right after :require
to make the intent of the separate clause clearer.
Thanks
šø Thanks to Michiel aka borkdude for instantly recognising what was going on and helping me out on Clojurians Slack.
If you like my content, please consider supporting me with a paid Subscription.