This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
- # announcements (3)
- # babashka (6)
- # beginners (84)
- # biff (1)
- # cider (7)
- # cljsrn (1)
- # clojure (18)
- # clojure-australia (3)
- # clojure-dev (21)
- # clojure-france (1)
- # clojure-spec (6)
- # clojurescript (78)
- # datomic (34)
- # emacs (5)
- # exercism (32)
- # graalvm (1)
- # helix (2)
- # hyperfiddle (3)
- # lsp (36)
- # malli (4)
- # missionary (3)
- # off-topic (54)
- # re-frame (14)
- # releases (2)
- # sql (31)
- # vim (9)
If I only use
reify to implement a Protocol, is it safe to replace
satisfy? calls with
instance?. Seems to work at least and much cleaner than the cache-pattern using
Object. Few recent library cases on hacking around the slowness:
• Lacinia: https://github.com/walmartlabs/lacinia/pull/371
• Malli: https://github.com/metosin/malli/issues/513
Why are you even using a protocol then and not just definterface or whatever?
satisfies? is never going to be a fast path. The whole idea with polymorphic dispatch is to lean on the fast polymorphic dispatch - make the call and implement the appropriate fallbacks. So I guess, I’d first ask why you can’t avoid satisfies in the first place.
Good point. in malli, most protocols are internal and could be interfaces too (haven't done interfaces with cljs, guess they work the same?), but some need to be polymorphic. Thinking of checking first the instance (fast path) and fallbacking to normal
satifies?. Lacinia is doing the same (https://github.com/walmartlabs/lacinia/blob/64307df2ccb1443eff06aa33ed5cc17bef0e679e/src/com/walmartlabs/lacinia/resolve.clj#L205-L226). Having fast satisfies? in core might speed up a lot of clojure libs & apps.
It’s been a while, but Iirc cljs does not have definterface. You can lean on closure compiler interface annotations for some type checking but implementation is just adding methods to Object in your deftype. I agree re having fast satisfies, I’ve been bitten by this before. Are you aware of ztellman’s version?https://github.com/clj-commons/manifold/blob/f32f4e9f7ecd3a3d83ec59b0288d1519f64d4a13/src/manifold/utils.clj#L76
In most cases, code that is slow because of satisfies should not be using satisfies
Why do you need satisfies?
It’s extensible just based on protocols. I meant in what scenario is satisfies needed over just making a polymorphic call?
oh, basically for two things:
1. offer a
satisfies? -like helper
2. offer an api that doesn’t throw if called with something that doesn’t
;; 1 (defn schema? [x] (#?(:clj instance?, :cljs implements?) malli.core.Schema x)) ;; 2 (defn entries ([?schema] (entries ?schema nil)) ([?schema options] (if-let [schema (schema ?schema options)] (if (#?(:clj instance?, :cljs implements?) malli.core.MapSchema schema) (-entries schema)))))
could add a default impl for 2, but then coudn’t do 1 with it (as everything satisfies it)
could add a https://github.com/metosin/sieppari/blob/develop/src/sieppari/async.cljc#L9 to protocols to solve 1 & 2, but doing that with 10+ protocols is a lot of boilerplate
tried the satisfies-cache you mentioned earlier, but it’s also a lot of boilerplate for all protocols.
why do you need to do 1?
ad-hoc polymorphism I guess: branching with a predicate with
clojure.walk/prewalk with special logic for all things that satisfy etc.
cond-cases could all modelled purely with new protocols, but they add a lot of js bundle size on cljs, trying to find balance between protocols and
I'm trying to get at the heart of the problem, feel like we're finally getting close
the ad-hoc case look useually like this:
that could be implemented with a new protocol, but would lead to huge amount of protocols.
;; 1 (cond (ref? x) ... (schema? x) ... (vector? x) ... :else ...)
are those ...'s all doing the same action that could be a different protocol?