Last active
January 4, 2016 01:59
-
-
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
;; 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