Fork me on GitHub
#clojure-dev
<
2020-10-25
>
ikitommi15:10:06

is there something to do to get https://clojure.atlassian.net/browse/CLJ-1814 resolved anytime soon? This effects how libraries are being designed. Is there a good way / sample code how to resolve this properly in user space?

ikitommi15:10:11

My use case is that I have ~5 core protocols in the lib (malli), need to check satisfies?at creation time. With huge nested (real-life) schema inferring, this can take seconds. Was thinking of adding marker methods into protocols like -isSchema? and implement the protocols for all things (`default` | Object), but feels bad. Memoization with ConcurrentHashMap and weak references? Use meta-data to mark the created instances?

Alex Miller (Clojure team)15:10:16

The entanglement with the metadata based instances makes things less straightforward there

Alex Miller (Clojure team)15:10:32

The whole idea with protocols is to just make the call and let polymorphism sort it out - generally the need to call satisfies is a sign that something is off

Alex Miller (Clojure team)16:10:04

Can you explain more why you need to check satisfies at creation time?

ikitommi16:10:50

here’s the culprit: https://github.com/metosin/malli/blob/master/src/malli/core.cljc#L993-L1010. It accepts a Schema protocol instance, IntoSchema protocol instance, a vector (the data syntax) or… anything. Schema and IntoSchema instances are implemented using reify. All help appreciated here.

Alex Miller (Clojure team)18:10:49

One helpful tool is to extend Object, and in it inspect the instance and then dynamically extend the protocol in the Object method for the newly understood concrete type. If all the instances are singletons via reify or metadata extension that won’t really help you

Alex Miller (Clojure team)18:10:28

But if you have say multiple layers of protocols it works quite well

Alex Miller (Clojure team)18:10:58

You only do the slow satisfies check once, after the dynamic extension the concrete type is known by the protocol and routes directly

ikitommi19:10:01

Interesting. thanks!

Ben Sless07:10:35

An example of what you proposed Alex can be found in Clojure Applied under Extending Protocols to Protocol?

Ben Sless07:10:31

Also, could another approach be instead of making schema a function make it part of a protocol, then add the different implementations for every case, i.e. every reify for Schema and IntoSchema, extend to Object and to Vector? A convenience in this case would be specifying default methods for protocols.

ikitommi10:10:58

the Extending Protocols to Protocol seems like a great match here. I actually didn’t realize that a static reify creates just one class. Most of the schemas are implementes as -simple-schema, and have a shared class:

(class (m/schema [:string {:title "a string"}]))
; => malli.core$_simple_schema$reify$reify__2320

(class (m/schema int?))
; => malli.core$_simple_schema$reify$reify__2320

(class (m/schema [:int {:min 1}]))
; => malli.core$_simple_schema$reify$reify__2320
Thanks both, I believe I can fix the perf issue with this, but voted also the issue.

Alex Miller (Clojure team)16:10:53

I can definitely bump this onto the radar for 1.11 though to think about

borkdude16:10:36

(Speaking about metadata: I'd like to bring https://clojure.atlassian.net/browse/CLJ-2568 to your attention as possibly interesting for 1.11 as well.)