thanks all, i'll report back on how it goes!
@raymcdermott I can look your thing today/tomorrow.
@d.ian.b no inbuilt mapping for fn?
. But, you can:
(m/validate [:fn fn?] (partial +))
; => true
yeah
also:
(m/explain
[:=> [:cat [:* [:int {:gen/min -1000, :gen/max 1000}]]] :int]
(partial +)
{::m/function-checker mg/function-checker})
; => nil
I've made it right now haha
(m/explain
[:=> [:cat [:* [:int {:gen/min -1000, :gen/max 1000}]]] [:int {:min 1}]]
(partial +)
{::m/function-checker mg/function-checker})
;{:schema [:=> [:cat [:* [:int #:gen{:min -1000, :max 1000}]]] [:int {:min 1}]],
; :value #object[clojure.core$_PLUS_ 0x6edca2b1 "clojure.core$_PLUS_@6edca2b1"],
; :errors (#Error{:path [],
; :in [],
; :schema [:=> [:cat [:* [:int #:gen{:min -1000, :max 1000}]]] [:int {:min 1}]],
; :value #object[clojure.core$_PLUS_ 0x6edca2b1 "clojure.core$_PLUS_@6edca2b1"],
; :check {:total-nodes-visited 0,
; :depth 0,
; :pass? false,
; :result false,
; :result-data nil,
; :time-shrinking-ms 0,
; :smallest [()],
; :malli.generator/explain-output {:schema [:int {:min 1}],
; :value 0,
; :errors (#Error{:path [],
; :in [],
; :schema [:int {:min 1}],
; :value 0})}}})}
nice!
How I can use a register of the (m/function-schemas)
registry, onto m/validate
?
(get-in (m/function-schemas) [(quote *ns*) 'foo :schema])
@d.ian.b good question, atm, no easy way. Idea with the function registry is that there will be a instrument
kinda thing, that will wrap a) some b) all registered function schmaas like Orchestra - running input & output validation. Could be also used to emit generated data based only on the function definitions. Ideas and PRs welcome on that.
also, did a spike on infferring schemas from normal vars. you get useful guesses pretty easily, better with tools.analyzer & clj-kondo and I guess. really good with core.typed.
the new kw-varargs thing would work nicely with global registry, my guess is that the core team will plug into that with spec. e.g. given a function:
(defn doit [& {:domain.user/keys [id name]}] [id name])
… running (m/collect #'doit)
would infer a [:map :domain.user/id :domain.user/name]
out of it, register it as a malli function schma, after (m/instrument my-registry)
saying:
(doit :domain.user/id 1, :domain.user/name "kikka")
would cause it to run validation.given there is few hours extra time, I would write a sample code so that I could prove a point that it’s doable and :awesome:.
I don't seem to be able to merge maps with [:and]
constraints
(def x [:and [:map
[:start int?]
[:end int?]]
[:fn (fn [{:keys [start end]}]
(< start end))]])
=> #'user/x
(def y [:map
[:here int?]
[:there int?]])
=> #'user/y
(require '[malli.util :as mu])
=> nil
(mu/merge y x)
=> [:and [:map [:start int?] [:end int?]] [:fn #object[user$fn__4910 0x40fa91ef "user$fn__4910@40fa91ef"]]]
(mu/merge x y)
=> [:map [:here int?] [:there int?]]
is this expected?
and should merge combine the predicate in another predicate which and
s those if both maps have predicates?
it should invoke both functions ... maybe the order would not be predictable but I'll take that
probably the order of merge args
this could lead to funny problems, like what if you merge in a closed map in an open map, probably the resulting map should be open?
or should merge consider predicates and other properties like metadata, which is ignored in merge args?
(with-properties-of (merge x y) y)
merge takes the last as the 'winner' so I think that would be the most idiomatic
but I agree that merging things that are not maps is tricky
Plumatic dropped s/both
in favour of s/constrained
just because the first is not a good idea: “apple and fruit and a car” please. Currently :and
already kinda means “the first thing constrained with the rest” as we pick the generator from first and then constraint with the rest using gen/such-that
. Given that, we could make :and
mergable, would merge with the first and keep the rest as extra leaves of :and
? e.g.
[:map ::x]
[:map ::y]
; => [:map ::x ::y]
[:map ::x]
[:and [:map ::y] map?]
; => [:and [:map ::x ::y] map?]
[:and [:map ::x] map?]
[:map ::y]
; => [:and [:map ::x ::y] map?]
[:and [:map ::x] map?]
[:and [:map ::y] map?]
; => [:and [:map ::x ::y] map? map?]
[:and [:map ::x]]
map?
; => map?
would that be … more correct?having [:and [:map …] [:fn …]]
is quite common, having it non-mergable is a bummer.
if both maps have [:and [:map ..][fn...]
it could be rejected. If one has an [:and ...]
and the other doesn't can't you still merge the maps? I am probably under thinking it 🙂
Anyway to have a dynamic default ? Like if I want a default timestamp to be generated when runing decode with default-value-transformer ?
(malli/decode
[:map {:registry
{:inst (m/-simple-schema
{:type :inst
:pred inst?})}}
[:time :inst]
[:id1 :uuid]
[:id2 :uuid]]
{}
(mt/default-value-transformer
{:defaults {:inst (constantly (rand-int 100))
:uuid (constantly (char (rand-int 100)))}}))
Found something that works for me, but the default are generated once per type so for example if you need two different uuid it won't work.(mu/optional-keys
[:map {:registry
{:inst (m/-simple-schema
{:type :inst
:pred inst?})}}
[:time :inst]
[:id :uuid]])
This throw a java.lang.StackOverflowError