Fork me on GitHub
Vincent Cantin04:03:30

if I understand correctly, the most natural way to implement the equivalent of spec/conform and spec/unform in Malli is to use a Transformer, isn't it?


are you interested in the branching information from s/conform?

Vincent Cantin14:03:15

yes, totally. I need it for my Vrac project.


Currently, there is no such thing. But one idea was that the m/explain also collected the explain info from the successfully validated values.


… that would contain all the needed branching information and could be used to produce “humanized” result like the one s/conform does.


also, would be easy to “unform” that as all the locations and values already exist there.

Vincent Cantin14:03:53

I am not totally convinced that the function explain should also be used for s/conform. The implementation might be similar, but explain and conform have different purposes.

Vincent Cantin14:03:47

That’s why I was wondering if it would be better to have a separate function for that, maybe via a Transformer.

Vincent Cantin14:03:24

I did not finish to play with Malli, not sure if the Transformer pattern is suitable for those 2 functions.


you should be able to do that with a transformer


Enjoyed the presentation, thanks!


I browsed the slides - is the Kondo integration a vision for the future or does it work today already? I’m super excited!


it was a 15min hack to check if that would work and it did! I just emitted a lint.edn which the fn description in the form clj-kondo understands it and hard-wired clj-kondo to read that file. I think there is betters ways to do this automatically. The m/defn is also not finished, but will be a 1:1 port from plumatic schema so cursive knows that too. Might take few (dev) days to get them cleaned up and pushed to master for testing.


Super cool! I’ve wanted to generate specs for GraphQL input objects which are closed maps, and being able to statically analyze functions that consume them. Looks like this might be possible soon!


Oh and I could probably also annotate React components that consume GraphQL...


Thanks for the great talk on ClojureD, @ikitommi!


I stumbled over an article that I figured people here might find interesting: QuickREST: Property-based Test Generation of OpenAPI-Described RESTful APIs The article discusses generating Clojure specs from OpenAPI specifications, and running generative testing against those. With Malli, I figure that process might become even simpler.


Conform/unform using explain would look something like this:

(def Schema
   [:a [:or pos-int? string?]]
   [:b string?]])

(m/explain Schema {:a 42, :"a"})
;{:schema [:map
;          [:a [:or pos-int? string?]]
;          [:b string?]]
; :value {:a 42, :"a"},
; :results [#Success{:path [1 1 1], :in [:a], :schema pos-int?, :value 42}
;           #Success{:path [2 1], :in [:b], :schema string?, :value "a"}]}

(-> Schema
    (m/explain {:a 42 :b "a"})
;{:a #Branch{1 42}
; :b "a"}

Vincent Cantin15:03:55

in my use case with spec, I encountered a few times the need to apply custom transformations during s/conform , that’s also why I would think that a Transformer would be better if it can be easily customized to produce the format I want in one pass.


@teodorlu looks interesting. need to read that when extra time.


yeah, transformers give you the single pass. looking forward to seeing if that helps over spec.


@borkdude the :keys doesn’t work with :ret values with clj-kondo yet?


on :args it seems to work, which is awesome 🙂


@ikitommi what :ret type are you using?


{:args [{:op :keys
         :req {:a {:op :keys
                   :req {:b {:op :keys
                             :req {:c :int}}}}}}]
 :ret {:op :keys
       :req {:b {:op :keys
                 :req {:c :int}}}}}


I don't think that's supported yet. As a fallback you can use :tag :map for now and post an issue about it.


maybe it works when you add :tag :map in that same map you pass to :ret now, so you can keep the code the same when it gets fixed?


will write an issue, the :tag :map makes it a map, but the keys are not followed, at least (:a my-fn) succeed despite the ret says there is no such key