Gert Goet

@eval

TIL #Clojure :reload-ing when :require-ing

eval

astronaut03.png

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.


Tags: #Clojure, #TIL, #Babashka