Nice tweet @mikethompson :) https://twitter.com/wazound/status/1367303612028751872
I hear similar sentiments in the latest #defnpodcast - there they called it the Osborne effect. https://en.wikipedia.org/wiki/Osborne_effect#:~:text=The%20Osborne%20effect%20is%20a,announcing%20a%20future%20product%20prematurely.
I have the exact same problem with babashka: waiting for spec2 so not including spec1: and this can result in years of having nothing perhaps
I didn't hear that podcast, but yeah, I feel like Clojure needs an outcome. We're stuck in something of a twilight zone. And this is an important issue. And right now Malli is just better. Unless there is something pending with spec ... it feels like we need permission to get on with making Malli "it"
In some way, no matter how good your library is, you can't compete with core since people will just believe that "core is better" no matter what you do, which isn't fair maybe. But "better" is probably not the right way to describe it: spec and malli have different approaches. spec is more of an RDF approach where each unique attribute name describes a schema
I'm the same as you: should re-frame support spec or Malli. I've held off for a long time.
Why should re-frame have to choose here? Can't this be decided in some add-on lib? Why do you need to address this concern in re-frame itself?
I could
Re-frame events are already associated with globally namespaced keywords which maybe aligns well with spec
But if ever spec is ratified as the one true way for Clojure, I'd like for it to be the one true way for re-frame too
Anyways
Maybe it can be made pluggable
Reitit supports all three so the application can choose.
Pluggable solution is probably also quite important in Cljs apps, because either Spec or Malli both add about 100KB JS to the output bundle (before gzip).
I was surprised by this recently when I checked project output report in app that uses Malli, but re-chain uses Spec internally: https://github.com/ingesolvoll/re-chain/issues/6
Interesting discussion. About Malli cljs-size. The core has been developed DCE in mind, you can make a Malli bundle with just validation of strings, numbers, maps, vector and sets and it’s few kilos zipped. Currently, many extensions (humanized errors, generation, json schema etc) are implemented using multimethods, so pulling anything out of those, makes it much bigger.
The Code Size Expression Problem.
here’s the bare-bones malli.
@mikethompson what would the spec/malli integration look like? do you need something?
And Reitit Malli coercion is built for Ring model, so it includes lots of features that are unnecessary for frontend routing. When we get around to implementing separate frontend coercion, it will drop dependency on some unused parts.
Like the json schema generation parts.
^i'm not opposed to the json schema stuff but it does feel odd that it isn't a separate artifact
It is separate namespace on Malli. On Reitit coercion impl. the parameter validation and generating the json-schemas for routes is closely related so they are on the same module currently.
yes, there should be a lite-version of reitit coercion, with just the encoding & decoding part - without throwing exceptions. Frontend would use that: simple, pure and small.
malli is designed so that malli.core
is the essential ns, all others are optional (ok, there is nowadays malli.impl
too). It has been easier to optimize the whole as everyhing is in single repo.
Hi everyone, I’m not sure if this is a bug, but I’m running into a quirk with the validation and error reporting of :catn
schemas
For example if you had a schema like:
[:catn
[:amount [:fn {:error/fn '(fn [{:keys [value]} _] (str "Received value " value ", should be > 0"))
:decode/string malli.transform/-string->double}
'#(> % 0)]]
[:type [:enum "A" "B" "C"]]]
If I try and (malli.core/explain schema [1.0 "D"])
, and humanize it, the error I get back is "Received value 1.0, should be > 0"
, rather than "should be one of "A", "B", or "C"
Isn't the idea of :catn
that you provide names for the schema elements?
Else you should just use :cat
Fixed the example, I do intend to use :catn
to provide names for the schema elements
@arundilipan seems to work:
(-> [:catn
[:amount [:fn {:error/fn '(fn [{:keys [value]} _] (str "Received value " value ", should be > 0"))
:decode/string malli.transform/-string->double}
'#(> % 0)]]
[:type [:enum "A" "B" "C"]]]
(m/explain [1.0 "D"])
(malli.error/humanize))
; => [nil ["should be either A, B or C"]]
should :catn
not give back error messages by name?
error messages by name?
{:amount nil :type ["should be ..."]}
That’s for :map i think
are the positions in :cat
always unambiguous? I know for spec they are certainly not with s/cat
hmm. not sure. the current humanize
just mimics the raw result, not parsed one.
but the info is there:
(-> [:catn
[:amount [:double {:min 0}]]
[:type [:enum "A" "B" "C"]]]
(m/explain [1.0 "D"]))
;{:schema [:catn [:amount [:double {:min 0}]] [:type [:enum "A" "B" "C"]]],
; :value [1.0 "D"],
; :errors (#Error{:path [:type 0], :in [1], :schema [:enum "A" "B" "C"], :value "D"})}
e.g. it’s a sequence, errors are positioned by :in
.
maybe for tools like expound it would be nice to have the humanized error along with the path?
ah I see
so using the explain output you can get to the error message by path
using get-in
or so
for map, the explain info is:
(-> [:map
[:amount [:double {:min 0}]]
[:type [:enum "A" "B" "C"]]]
(m/explain {:amount 1.0
:type "D"}))
;{:schema [:map [:amount [:double {:min 0}]] [:type [:enum "A" "B" "C"]]],
; :value {:amount 1.0, :type "D"},
; :errors (#Error{:path [:type 0], :in [:type], :schema [:enum "A" "B" "C"], :value "D"})}
but yes, the branch name is available in the error data for :catn
, to be printed nicely etc.
@arundilipan merged a humanized error for :double
, so this works now too:
(-> [:catn
[:amount [:double {:min 0}]]
[:type [:enum "A" "B" "C"]]]
(m/explain [1.0 "D"])
(malli.error/humanize))
; => => [nil ["should be either A, B or C"]]
user=> (require '[malli.error :as e])
nil
user=> (require '[malli.core :as m])
nil
user=> (e/humanize (m/explain [:cat int? int? [:? int?] [:? string?]] [1 2 :foo]))
[nil nil ["should be an int" "should be a string" "unknown error"]]
Looks legit, except maybe for the "unknown error"?That's probably better expressed as:
(m/explain [:cat int? int? [:orn [:x int?] [:y string?]]] [1 2 :foo])
Just wanted to see what malli would make of itgood catch @borkdude, the regex error types didn’t have humanized forms. fixed in master:
(-> [:cat int? int?]
(m/explain [1])
(me/humanize))
; => [nil ["end of input"]]
(-> [:cat int? int?]
(m/explain [1 2 3])
(me/humanize))
; => [nil nil ["input remaining"]]
(-> [:cat int? int? [:? int?] [:? string?]]
(m/explain [1 2 :foo])
(me/humanize))
; => [nil nil ["should be an int" "should be a string" "input remaining"]]
nice!
with :or
:
(-> [:cat int? int? [:or int? string?]]
(m/explain [1 2 :foo])
(me/humanize))
; => [nil nil ["should be an int" "should be a string"]]
yeah, that already worked right?