made with Findka

Refreshing namespaces breaks *data-readers*

12 June 2019

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.

There's more where that came from if you subscribe to my newsletter.