Skip to content

Instantly share code, notes, and snippets.

@noprompt
Created March 16, 2015 04:13
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 noprompt/6d4aa092b0e2eb65c043 to your computer and use it in GitHub Desktop.
Save noprompt/6d4aa092b0e2eb65c043 to your computer and use it in GitHub Desktop.
complies? macro for ClojureScript. Verifies than an object not only satisfies a protocol but implements all of it.
(ns example.macros
(:require
[cljs.analyzer :as a]
[cljs.compiler :as c]))
(defmacro complies?
"True if x satisfies and implements all methods of a protocol.
Ex.
(defprotocol IFoo
(-foo [this]))
(deftype Foo []
IFoo
(-foo [this]))
(deftype Bar []
IFoo)
(complies? IFoo (Foo.))
;; => true
(complies? IFoo (Bar.))
;; => false
"
[psym x]
(let [pvar (cljs.analyzer/resolve-var
(dissoc &env :locals) psym)
pname (:name pvar)
pmeths (-> pvar :protocol-info :methods)
pprefix (cljs.core/protocol-prefix pname)
omkeys (mapcat
(fn [[pmeth psigs]]
(for [psig psigs]
(str pprefix (cljs.compiler/munge pmeth) "$arity$" (count psig))))
pmeths)]
`(boolean
(and (satisfies? ~psym ~x)
~@(for [omkey omkeys]
`(.. (type ~x) -prototype ~(symbol (str "-" omkey))))))))
@noprompt
Copy link
Author

Since ClojureScript (and Clojure) do not require a protocol implementor to fully implement a protocol, satisfies? can potentially lead to a false positive. complies? is an extension of satisfies? that does not suffer from this.

(defprotocol IFoo
  (-foo [this]))

(deftype Foo []
  IFoo
  (-foo [this]))

(deftype Bar []
  IFoo)

(complies? IFoo (Foo.))
;; => true
(satisfies? IFoo (Foo.))
;; => true

(complies? IFoo (Bar.))
;; => false
(satisfies? IFoo (Bar.))
;; => true

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment