-
Notifications
You must be signed in to change notification settings - Fork 219
Robust :and
parser, add :andn
#1182
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
:and
parser, add :andn
:and
parser, add :andn
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some questions / comments. Not confident enough yet to approve.
; :flat [#malli.core.Tag{:key :name, :value "x"} | ||
; #malli.core.Tag{:key :id, :value 1} | ||
; #malli.core.Tag{:key :name, :value "y"} | ||
; #malli.core.Tag{:key :id, :value 2}]}} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah, this behaviour makes sense, I get it
(if (-ref-schema? this) | ||
(-parser-info (-deref this)) | ||
(when (-> this -parent -type-properties ::simple-parser) | ||
{:simple-parser true})))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what's the need for both ParserInfo and -type-properties ::simple-parser
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could get away with just ParserInfo
. It seemed neater at the time to have it at the type-level for trivial types.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since all the schemas are supposed to have -parser-info
, we feel like the method should be in Schema
. Also, we feel like -type-properties ::simple-parser
makes the feature harder to understand, so we'd prefer you drop that.
The default impl for ParserInfo is neat, but makes following the logic harder for future maintainers.
(defmethod accept :orn [_ s children _] | ||
(let [children (map last children) | ||
base (-base s children)] | ||
(assoc base :x-anyOf children))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thanks for adding this missing case!
(reduced ::invalid) | ||
(cond-> acc | ||
(not simple) (conj v'))))) | ||
(if simple x []) x)] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't understand the changes around here. Is there a corresponding test?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since we know the child has a simple parser, we don't need to rebuild the result, since it means the results of parsing is either ::invalid
or x
.
The "then" branch of malli.parser/ensure-parser-type
tests this. For example, [:vector ::HOLE]
with ::HOLE
being a simple parsing schema like :any
is expected to be a simple parser (expected-simple == true
) so any mg/sample
s we take of it will {un}parse
back to the identical sampled value.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
aha, this is the optimisation you mention in the PR description, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, specifically for :map-of
, -collection-schema
, and :map
's default value.
Letting @ikitommi have a look as well. |
Thanks for looking @opqdonut. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Went through this in a session with some people from Metosin. We really like the change, and would like to get it in! Thanks for your effort.
Please move -parser-info
to Schema
and consider the suggestions we had for :parse
.
To opt-out of parsing any further levels of this schema, use the `:parse :none` property. | ||
|
||
```clojure | ||
(m/parse [:and {:parse 0} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is reserving the top-level :parse
key for a specific purpose. Also, "parse" on its own is not very descriptive. We propose using a :parse/
ns (just like :gen/
for example). How about :parse/transforming-child-index
or :parse/index
or :parse/child
?
The error `:malli.core/and-schema-multiple-transforming-parsers` is thrown if the transforming | ||
parser cannot be picked automatically. This usually means that multiple conjuncts | ||
will transform their input or a false-positive has occurred because the underlying schema | ||
does not implement `malli.core/ParserInfo`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about defaulting to the first transforming parser? That's kind of what we do for json-schema generation etc. Or do you think it would trip up users?
(-set [this key value] (-set-assoc-children this key value))))))) | ||
(-set [this key value] (-set-assoc-children this key value)) | ||
ParserInfo | ||
(-parser-info [_] {:simple-parser (every? (comp :simple-parser -parser-info) children)})))))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[nit] use -comp
for perf & cljs bundle size reasons
(if (-ref-schema? this) | ||
(-parser-info (-deref this)) | ||
(when (-> this -parent -type-properties ::simple-parser) | ||
{:simple-parser true})))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since all the schemas are supposed to have -parser-info
, we feel like the method should be in Schema
. Also, we feel like -type-properties ::simple-parser
makes the feature harder to understand, so we'd prefer you drop that.
The default impl for ParserInfo is neat, but makes following the logic harder for future maintainers.
(-set [this key value] (-set-entries this key value)))))))) | ||
(-set [this key value] (-set-entries this key value)) | ||
ParserInfo | ||
(-parser-info [_] {:simple-parser (every? (comp :simple-parser -parser-info peek) (-entry-children entry-parser))}))))))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[nit] -comp
here as well
@@ -1170,18 +1307,20 @@ | |||
form (delay (-simple-form parent properties children -form options)) | |||
cache (-create-cache options) | |||
validate-limits (-validate-limits min max) | |||
simple-parser (delay (every? (comp :simple-parser -parser-info) children)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[nit] -comp
(-set [this key value] (-set-assoc-children this key value)))))))) | ||
(-set [this key value] (-set-assoc-children this key value)) | ||
ParserInfo | ||
(-parser-info [_] {:simple-parser (every? (comp :simple-parser -parser-info) children)}))))))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[nit] -comp
Close #1166
Close #1173
This tightens up
-parser
for:and
in several ways.The essential insight is that there are two kinds of parsers, I'm calling transforming (e.g.,
:orn
,-collection-schema
) and simple (e.g.,:any
,-simple-schema
). Simple parsers return identical input on success. Everything else is transforming.In all cases I've seen so far, it's possible to accurately predict whether a parser is simple based on its schema. With this information, we can now improve
:and
's parser by::and
This automatically handles
[:and S [:fn ..]]
and makes it more robust, as:fn
is now passed the input value instead of the parsed value and the conjuncts can be in any order.Extras:
Adds a new schema
:andn
for when you really want multiple transforming parsers in a conjunction. It reparses the input for each conjunct and returns in a Tags. Unparser only unparses the leftmost child, which enables users to transform the unparsed results by removing the other results.We can now more aggressively optimize simple (un)parsers upfront to not build a result when it will be identical to the input.
Includes a fix for #1173 by bumping up the
:max-tries
for generating distinct vectors.