Hi,

I'm observing an incredibly weird bug in my program where this happens on 
the REPL

(use 'myapp.some.stuff)
(def x (->Record 1))
(type x) ; => myapp.some.stuff.Record
(instance? myapp.some.stuff.Record x) ; => false
(instance? (type x) x) ; => true

Somewhere the namespace gets reloaded, and there actually exist two classes 
called Record, with the same name, in the same namespace, and the ->Record 
constructor uses one version, but the REPL resolves another version. Is 
there any way I can tell how this came about?

Another example, where the problem is more obvious:

(defrecord Foo [x]) ; => user.Foo
(def f1 (->Foo 1))
(defrecord Foo [x]) ; => user.Foo
(def f2 (->Foo 2))
(type f1) ; => user.Foo
(type f2) ; => user.Foo
(= user.Foo (type f1)) ; => false
(= user.Foo (type f2)) ; => true
(instance? Foo f1) ; => false
(instance? Foo f2) ; => true

"Foo" on the REPL resolves to the second version, but f1 still uses the 
first, which still claims to be named the same.
This makes things very hard to debug, when i.e. hot reloading some code and 
see things break in mysterious ways.
Usually things resolve themselves when restarting the app, taking care to 
only load each namespace once, but I somehow created a situation where this 
doesn't help either.

When duplicating classes by reloading code, all metadata is identical, i.e. 
source location etc; except if you aquire a pointer to the old version of 
the class instance it's not identical to the new version.

Ideally I'd like some kind of *warn-on-redefinition* flag that prints a 
stacktrace which lead to a class being redefined over an existing one.

I tried adding a check to a custom version of defrecord:

(defmacro defrecord*
  [name & args]
  (let [ns-part (namespace-munge *ns*)
        classname (symbol (str ns-part "." name))]
    (try (resolve classname)
         ; exists: fail.
         (throw (IllegalArgumentException. (str "Will not redefine " 
classname)))
         (catch ClassNotFoundException e
           ; doesn't exist: go ahead.
           `(let []
              ; as before
              (defrecord ~name ~@args)
              ; name?
              (defn ~(symbol (str name '?))
                [x#] (instance? ~classname x#))
              ; return class
              ~classname)))))

But this causes all attempts to load the code to fail, (use 
'myapp.some.stuff) now always throws the "Will not redefine" exception, 
even in a fresh REPL.
For some reason everything appears to be loaded twice -- I thought using 
(ns .. (:require [...])) made sure to only load the namespace if it isn't 
already loaded, I'm not using :reload / :force-reload?!
And (use 'myapp.some.stuff :verbose) only logs a single clojure.core/load 
as expected; the error is actually thrown after loading clojure.zip?! I 
assume it's because the compiler runs in a separate thread from the loader?

(clojure.core/load "/clojure/zip")

CompilerException java.lang.IllegalArgumentException: Will not redefine...



TL;DR are there more tracing options to figure out what is being 
(re-)loaded?


Cheers,

-- 
pascal

-- 
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clojure@googlegroups.com
Note that posts from new members are moderated - please be patient with your 
first post.
To unsubscribe from this group, send email to
clojure+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
--- 
You received this message because you are subscribed to the Google Groups 
"Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to clojure+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to