Skip to content

Instantly share code, notes, and snippets.

@jacobobryant
Last active March 25, 2022 16:06
Show Gist options
  • Save jacobobryant/bcc6bef1fb9c5803115e2514f8d85fa1 to your computer and use it in GitHub Desktop.
Save jacobobryant/bcc6bef1fb9c5803115e2514f8d85fa1 to your computer and use it in GitHub Desktop.
refreshing namespaces breaks *data-readers*

If you call clojure.tools.namespace.repl/refresh before requiring any namespaces referenced in data_readers.clj, then *data-readers* gets messed up somehow. The readers you define in data_readers.clj won't work even if you require their namespaces. For example:

$ find -type f
./src/foo/core.clj
./src/data_readers.clj
./src/user.clj
./deps.edn
$ for f in $(find -type f); do echo $f:; cat $f; echo; done
./src/foo/core.clj:
(ns foo.core)

(defn foo-reader [x]
  (Long/parseLong x))

./src/data_readers.clj:
{foo/bar foo.core/foo-reader}

./src/user.clj:
(ns user
  (:require [clojure.tools.namespace.repl :refer [refresh]]))

(defn good []
  (require 'foo.core)
  (read-string "#foo/bar \"123\"") ; works
  (refresh)
  (read-string "#foo/bar \"123\"")) ; still works

(defn bad []
  (refresh)
  (require 'foo.core)
  (read-string "#foo/bar \"123\"")) ; doesn't work

./deps.edn:
{:deps {org.clojure/tools.namespace {:mvn/version "0.2.11"}}}

$ clj -e "(good)"
:reloading (user foo.core)
123
$ clj -e "(bad)"
:reloading (user foo.core)
Exception in thread "main" java.lang.IllegalStateException: Attempting to call unbound fn: #'foo.core/foo-reader
	at clojure.lang.Var$Unbound.throwArity(Var.java:45)
	at clojure.lang.AFn.invoke(AFn.java:32)
	at clojure.lang.Var.invoke(Var.java:384)
	at clojure.lang.LispReader$CtorReader.readTagged(LispReader.java:1448)
	at clojure.lang.LispReader$CtorReader.invoke(LispReader.java:1427)
	at clojure.lang.LispReader$DispatchReader.invoke(LispReader.java:846)
	at clojure.lang.LispReader.read(LispReader.java:285)
	at clojure.lang.LispReader.read(LispReader.java:216)
	at clojure.lang.LispReader.read(LispReader.java:205)
	at clojure.lang.RT.readString(RT.java:1874)
	at clojure.lang.RT.readString(RT.java:1869)
	at clojure.core$read_string.invokeStatic(core.clj:3815)
	at clojure.core$read_string.invoke(core.clj:3805)
	at user$bad.invokeStatic(user.clj:13)
	at user$bad.invoke(user.clj:10)
	at user$eval535.invokeStatic(NO_SOURCE_FILE:1)
	at user$eval535.invoke(NO_SOURCE_FILE:1)
	at clojure.lang.Compiler.eval(Compiler.java:7176)
	at clojure.lang.Compiler.eval(Compiler.java:7131)
	at clojure.core$eval.invokeStatic(core.clj:3214)
	at clojure.main$eval_opt.invokeStatic(main.clj:465)
	at clojure.main$eval_opt.invoke(main.clj:459)
	at clojure.main$initialize.invokeStatic(main.clj:485)
	at clojure.main$null_opt.invokeStatic(main.clj:519)
	at clojure.main$null_opt.invoke(main.clj:516)
	at clojure.main$main.invokeStatic(main.clj:598)
	at clojure.main$main.doInvoke(main.clj:561)
	at clojure.lang.RestFn.applyTo(RestFn.java:137)
	at clojure.lang.Var.applyTo(Var.java:705)
	at clojure.main.main(main.java:37)

I'm not sure why this happens, but you can fix it by requiring any namespaces in data_readers.clj from your user.clj file (or whatever your repl entrypoint is). As long as you require them before the first call to refresh, they'll work.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment