Skip to content

Instantly share code, notes, and snippets.

@stuartsierra
Last active January 4, 2016 01:59
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save stuartsierra/8551970 to your computer and use it in GitHub Desktop.
Save stuartsierra/8551970 to your computer and use it in GitHub Desktop.
Small demonstration of the errors that can result from reevaluating type or protocol definitions in Clojure
;; Demonstration of how re-evaluating type/protocol
;; definitions can lead to confusing errors in Clojure.
(set! *warn-on-reflection* true)
;; Create a new type Foo
(deftype Foo [x])
;; Create an instance of that type
(def a-foo (Foo. 1))
;; Maybe create a type-hinted function
(defn use-foo [^Foo foo]
(prn (.x foo)))
;; Now suppose we re-evaluate the definition of Foo,
;; perhaps in a REPL or editor
(deftype Foo [x])
;; Now look what happens!
(prn (type a-foo)) ;; user.Foo
(prn (instance? Foo a-foo)) ;; false ?!
(prn (= Foo (type a-foo))) ;; false ?!
(use-foo a-foo)
;; java.lang.ClassCastException:
;; user.Foo cannot be cast to user.Foo
;; What's going on?
;; The object bound to a-foo is an instance of the *old*
;; class Foo we defined on line 7. Clojure's dynamic class
;; loader allows us to create a new class named Foo on line
;; 19, but as far as the JVM is concerned it's not the same
;; class.
;; In general, whenever you have an error of the form
;; "X cannot be cast to X", check for the possibility
;; that X has been redefined and you are using an instance
;; that was created using the old definition.
;; This is further complicated by ahead-of-time (AOT)
;; compilation, because the AOT-compiled version of a type
;; or protocol will often get loaded earlier than the
;; source-code containing it.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment