Skip to content

Commit

Permalink
[#537] (instance? clojure.lang.IAtom 1) returns true
Browse files Browse the repository at this point in the history
  • Loading branch information
borkdude authored Feb 25, 2021
1 parent 1702cae commit 47d621f
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 27 deletions.
45 changes: 26 additions & 19 deletions src/sci/impl/core_protocols.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@
(let [methods (types/getMethods ref)]
((get methods #?(:clj 'deref :cljs '-deref)) ref)))

(defmethod #?(:clj deref :cljs -deref) :default [ref]
(clojure.core/deref ref))
(def ideref-default
(defmethod #?(:clj deref :cljs -deref) :default [ref]
(clojure.core/deref ref)))

(defn deref*
([x]
Expand All @@ -40,7 +41,8 @@
:cljs
(vars/new-var
'cljs.core.IDeref
{:methods #{-deref}
{:protocol IDeref
:methods #{-deref}
:ns cljs-core-ns}
{:ns cljs-core-ns})))

Expand Down Expand Up @@ -113,24 +115,25 @@

;;;; Defaults

(defmethod #?(:clj swap :cljs -swap!) :default [ref f & args]
;; TODO: optimize arities
(apply clojure.core/swap! ref f args))
(def iatom-defaults
[(defmethod #?(:clj swap :cljs -swap!) :default [ref f & args]
;; TODO: optimize arities
(apply clojure.core/swap! ref f args))

(defmethod #?(:clj reset :cljs -reset!) :default [ref v]
(reset! ref v))
(defmethod #?(:clj reset :cljs -reset!) :default [ref v]
(reset! ref v))

#?(:clj
(defmethod compareAndSet :default [ref old new]
(compare-and-set! ref old new)))
#?(:clj
(defmethod compareAndSet :default [ref old new]
(compare-and-set! ref old new)))

#?(:clj
(defmethod swapVals :default [ref & args]
(apply swap-vals! ref args)))
#?(:clj
(defmethod swapVals :default [ref & args]
(apply swap-vals! ref args)))

#?(:clj
(defmethod resetVals :default [ref v]
(reset-vals! ref v)))
#?(:clj
(defmethod resetVals :default [ref v]
(reset-vals! ref v)))])

;;;; Re-routing

Expand Down Expand Up @@ -169,15 +172,17 @@
:cljs
(vars/new-var
'cljs.core.ISwap
{:methods #{-swap!}
{:protocol ISwap
:methods #{-swap!}
:ns cljs-core-ns}
{:ns cljs-core-ns})))

#?(:cljs
(def reset-protocol
(vars/new-var
'cljs.core.IReset
{:methods #{-reset!}
{:protocol IReset
:methods #{-reset!}
:ns cljs-core-ns}
{:ns cljs-core-ns})))

Expand All @@ -191,3 +196,5 @@
{:ns clj-lang-ns})))

;;;; end IAtom

(def defaults (set (conj iatom-defaults ideref-default)))
34 changes: 27 additions & 7 deletions src/sci/impl/protocols.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
(:refer-clojure :exclude [defprotocol extend-protocol
extend extend-type reify satisfies?
extends? implements?])
(:require [sci.impl.multimethods :as mms]
(:require [sci.impl.core-protocols :as core]
[sci.impl.multimethods :as mms]
[sci.impl.types :as types]
[sci.impl.utils :as utils]
[sci.impl.vars :as vars]))
Expand Down Expand Up @@ -105,13 +106,31 @@
~atype ~(second meth) ~@(nnext meth)))
meths)))) proto+meths))))

;; IAtom can be implemented as a protocol on reify and defrecords in sci

(defn find-matching-non-default-method [protocol obj]
(boolean (some #(when-let [m (get-method % (types/type-impl obj))]
(let [ms (methods %)
default (get ms :default)]
(not (identical? m default))))
(:methods protocol))))

(defn satisfies? [protocol obj]
(if #?(:clj (instance? sci.impl.types.IReified obj)
:cljs (clojure.core/satisfies? types/IReified obj))
(if-let [obj-type (types/getInterface obj)]
(= protocol obj-type)
false)
(boolean (some #(get-method % (types/type-impl obj)) (:methods protocol)))))
(if #?(:clj (instance? sci.impl.types.IReified obj)
:cljs (clojure.core/satisfies? types/IReified obj))
(when-let [obj-type (types/getInterface obj)]
(= protocol obj-type))
;; can be record that is implementing this protocol
;; or a type like String, etc. that implements a protocol via extend-type, etc.
#?(:cljs (let [p (:protocol protocol)]
(or
(and p
(condp = p
IDeref (cljs.core/satisfies? IDeref obj)
ISwap (cljs.core/satisfies? ISwap obj)
IReset (cljs.core/satisfies? IReset obj)))
(find-matching-non-default-method protocol obj)))
:clj (find-matching-non-default-method protocol obj))))

(defn instance-impl [clazz x]
(cond
Expand All @@ -125,6 +144,7 @@
(if-let [c (:class clazz)]
;; this is a protocol which is an interface on the JVM
(or (satisfies? clazz x)
;; this is the fallback because we excluded defaults for the core protocols
(instance? c x))
(satisfies? clazz x))])
;; could we have a fast path for CLJS too? please let me know!
Expand Down
4 changes: 3 additions & 1 deletion test/sci/core_protocols_test.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,9 @@
(deftest instance-test
(is (true? (eval* "(instance? clojure.lang.IDeref (atom 0))")))
(is (true? (eval* "(defrecord Foo [x] clojure.lang.IDeref (deref [this] x))
(instance? clojure.lang.IDeref (->Foo 1))")))))
(instance? clojure.lang.IDeref (->Foo 1))")))
(is (true? (eval* "(instance? clojure.lang.IAtom (atom nil))")))
(is (false? (eval* "(instance? clojure.lang.IAtom 1)")))))

#?(:cljs
(deftest satisfies-test
Expand Down

0 comments on commit 47d621f

Please sign in to comment.