Fork me on GitHub
#clojure-spec
<
2017-07-16
>
lwhorton22:07:41

what’s the proper way to spec protocols? i’m assuming I can just spec some client fn instead of the protocol? but I’m having a conceptual issue about how to have multiple implementations of the protocol with this approach:

;; protocols.cljs
(defprotocol IMyProto
  (-foo [this v]))

;; client.cljs
(defn foo [this v]
  (protocols/-foo this v))

;; impl-A.cljs
(defrecord AImpl []
  IMyProto
  (-foo [this v] ...))

(defn foo [this v] (-foo this v)) ;; this is the fn I would instrument for A types

;; impl-B.cljs
(defrecord BImpl []
  IMyProto
  (-foo [this v] ...))

(defn foo [this v] (-foo this v)) ;; this is the fn I would instrument for B types
in client.cljs I want to be able to call protocols/foo instead of prototols/-foo, because I can instrument (defn foo...). But this breaks the purpose of a protocol - how do I know which implementation to invoke from the client a-impl/foo or b-impl/foo? What’s the proper way to organize these guys so I can stest/check 'foo?

lwhorton22:07:55

I suppose I could turn it on its head so that client calls always invoke (protocols/foo this v), and each impl points to an internal function IMyProto (foo [this v] (-foo this v))? But this doesn’t seem friendly, particularly once the records start having fields and if any protocol fn needs to refer back to those fields.

lwhorton22:07:43

*or perhaps I should re-apply s/fdef to the protocol’s foo when I actually want to run my checks? (s/fdef protocols/foo ::args (s/cat :type ::a-type), then re-def with ::b-type?

seancorfield23:07:51

Protocols are open for extension -- so you can't write a spec for all possible argument types. I think you could write a multi-spec for foo -- where the defmulti discriminator function returns the type of the first argument?