specify! implicitly relies on the provided object to be extensible so that protocol implementations can be provided. Here is a quick demonstration from a REPL.
Note that reify does not document this requirement either. This leads to confusing situations where non-extensible objects returned by JavaScript libraries (e.g. React) are unable to be extended and the user has little clue why.
Similarly, set! silently fails when objects are not extensible.
Per https://ask.clojure.org/index.php/12898/specify-and-set-silently-fail-if-object-is-not-extensible
specify!
implicitly relies on the provided object to be extensible so that protocol implementations can be provided. Here is a quick demonstration from a REPL.cljs.user> (def obj #js {:current nil}) [#js {:current nil}] cljs.user> (js/Object.preventExtensions obj) {"current" nil} cljs.user> (specify! obj IDeref (-deref [^js this] (.-current this))) {"current" nil}
Note that
reify
does not document this requirement either. This leads to confusing situations where non-extensible objects returned by JavaScript libraries (e.g. React) are unable to be extended and the user has little clue why.Similarly,
set!
silently fails when objects are not extensible.cljs.user> (set! (.-foo obj) "bar") "bar" cljs.user> obj {"current" nil}
goog.object/set
is able to detect this and throw an error.cljs.user> (goog.object/set obj "foo" "bar") Execution error (TypeError) at (<cljs repl>:1). Cannot add property foo, object is not extensible :repl/exception!