malli

https://github.com/metosin/malli :malli:
robert-stuttaford 2021-03-22T07:00:21.137200Z

haha just my luck, a week after i get started, the docs improve ๐Ÿ˜† it looks great!

robert-stuttaford 2021-03-22T07:01:33.138700Z

btw @ikitommi i ended up chunking the results of sets of 100 item m/provide into an atom and then m/union ing them together afterward. i ended up with a fair bit of [:or [:or [:or ....]]] but that was easy to remedy with a postwalk ๐Ÿ™‚

ikitommi 2021-03-22T07:33:22.144Z

@robert-stuttaford good to hear. the right solution is to add few new Protocols (e.g. Inferrer and Provider) and attach the latter to the reified IntoSchemas: each instance know how to construct itself given a value and options. Internally using type/class-based lookup tables, it should be 2-4 orders of magnitude faster, and pluggable. Have an pre-initial draft somewhere ๐Ÿ™‚

robert-stuttaford 2021-03-22T07:57:41.144200Z

that sounds sane!

Panel 3000 2021-03-22T10:06:39.145Z

(malli/encode
  [:or [:map
           [:password string?]
           [:password2 string?]]
     [:fn {:error/message "passwords don't match"}
      '(fn [{:keys [password password2]}]
         (= password password2))]]
  {:password "ok"}
  (mt/key-transformer {:encode  name}))
=> {:password "ok"}
(malli/encode
  [:and [:map
           [:password string?]
           [:password2 string?]]
     [:fn {:error/message "passwords don't match"}
      '(fn [{:keys [password password2]}]
         (= password password2))]]
  {:password "ok"}
  (mt/key-transformer {:encode  name}))
=> {"password" "ok"} Why doesn't encode work on the first one but does on the second, related to using :or instead of :and I guess.

ikitommi 2021-03-22T10:11:24.145900Z

I believe neither of the examples doesnโ€™t do anything, they are just just no-op.

ikitommi 2021-03-22T10:12:08.146100Z

oh, wait.

nilern 2021-03-22T10:14:04.146800Z

encode does not validate and those inputs are invalid, so nasal demons

ikitommi 2021-03-22T10:15:24.147700Z

actually:

(-transformer [this transformer method options]
            (let [this-transformer (-value-transformer transformer this method options)]
              (if (seq children)
                (let [transformers (mapv #(or (-transformer % transformer method options) identity) children)
                      validators (mapv -validator children)]
                  (-intercepting this-transformer
                                 (if (= :decode method)
                                   (fn [x]
                                     (reduce-kv
                                       (fn [x i transformer]
                                         (let [x* (transformer x)]
                                           (if ((nth validators i) x*) (reduced x*) x)))
                                       x transformers))
                                   (fn [x]
                                     (reduce-kv
                                       (fn [x i validator] (if (validator x) (reduced ((nth transformers i) x)) x))
                                       x validators)))))
                (-intercepting this-transformer))))

nilern 2021-03-22T10:15:25.148Z

Although in terms of the implementation I think both of those should be no-ops

ikitommi 2021-03-22T10:15:47.148500Z

it does validate and the first one fails -> doesnโ€™t transform

nilern 2021-03-22T10:18:01.150300Z

Ah yes so :or and seqex schemas validate when encoding because they have to pick a branch but nothing else (e.g. :and does) But that is implementation detail

nilern 2021-03-22T10:23:53.152500Z

The recommended flow is first validate, then encode if ok, else explain Ceterum censeo, maybe some day we will have a thing that does all of that in one pass like Plumatic coerce

Panel 3000 2021-03-22T10:25:23.153Z

Thanks for the explanations !

nilern 2021-03-22T10:45:24.154300Z

This is my pet peeve and now I finally added an issue https://github.com/metosin/malli/issues/404 But it is a big decision and feature

robert-stuttaford 2021-03-22T14:18:25.154900Z

is it possible for malli to report all errors instead of stopping on the first one?

nilern 2021-03-22T14:19:21.155600Z

explain(er) should report all the errors. What are you seeing / not seeing?

robert-stuttaford 2021-03-22T14:20:54.156100Z

ah, apologies. i'm using explain -> with-spell-checking -> humanize

robert-stuttaford 2021-03-22T14:21:20.156500Z

seems humanize is dropping it

ikitommi 2021-03-22T14:21:29.156700Z

really? should not.

robert-stuttaford 2021-03-22T14:23:19.157100Z

lemme make reaaaal sure first, don't want to waste your time ๐Ÿ˜…

robert-stuttaford 2021-03-22T14:29:51.158300Z

ok, it's not humanize. in the explain, the last error that i get is a :malli.core/input-remaining . when i fix that issue, then i get a new previously unreported error further down (happens to be the same issue: a kv-pair that should be one map level up)

robert-stuttaford 2021-03-22T14:30:06.158700Z

so, is there some way have the whole sequence reported on at once?

robert-stuttaford 2021-03-22T14:31:41.160100Z

basically, i'd like to use malli specs to provide a structural EDN linter in a web-based cms code editor. but for this one thing, it's working, beautifully

robert-stuttaford 2021-03-22T14:33:57.162300Z

i am getting other errors from other collections, but the same issue manifests, in that i only get one error per such collection. i guess the question i'm asking is, how do i make :sequential and/or :+ check all elements

ikitommi 2021-03-22T14:34:09.162700Z

Oh, the partial seqexp, no reporting / transformation on partially matched sequences atm. https://github.com/metosin/malli/issues/387

robert-stuttaford 2021-03-22T14:34:34.163300Z

ok so this is because i'm using :+ rather than :sequential?

ikitommi 2021-03-22T14:35:40.164800Z

not sure, could you give an example with actual and expected result?

robert-stuttaford 2021-03-22T14:36:24.166Z

ok, i switched to sequential, and this is the reason - thank you!

nilern 2021-03-22T14:36:37.166300Z

You should use e.g. :sequential instead of :*/`:+` if you can

robert-stuttaford 2021-03-22T14:36:46.166600Z

i can live without ensuring the sequence has at least one item

nilern 2021-03-22T14:37:04.167200Z

[:sequential {:min 1} :int]

๐Ÿ˜… 1
๐ŸŽ‰ 2
robert-stuttaford 2021-03-22T14:40:43.170400Z

ok rad i've got what i need. malli is lovely

robert-stuttaford 2021-03-22T14:41:24.171600Z

now to use edamame's edn metadata stuff to wire up go-to-line links

nilern 2021-03-22T14:42:05.172300Z

In general the seqex engine can't "resynchronize" after an erroneous element. We could have some optimizer that recognizes [:+ :int] and transforms it to [:sequential {:min 1} :int] but it would add complexity and unpredictability...

robert-stuttaford 2021-03-22T14:42:39.172500Z

totally fair

ikitommi 2021-03-22T14:45:22.173300Z

did a malli + edamame poc some time ago, canโ€™t recall was it any good, but pushed just as a gist: https://gist.github.com/ikitommi/0e5c4e48d8aeb7dd176128856ecdacb5

ikitommi 2021-03-22T14:45:52.173800Z

something @borkdude was asking I think.

borkdude 2021-03-22T14:47:36.174200Z

what I tried to do there is "complect" error message with locations directly

borkdude 2021-03-22T14:48:10.174900Z

but I think you can just do explain and then look up the vals in the paths which have locations (or maybe they are already in the errors) and then postwalk the result to add the locations to the error messages

robert-stuttaford 2021-03-22T14:50:37.175200Z

that second thing is what i'm doing

robert-stuttaford 2021-03-22T14:56:48.175900Z

yeah @ikitommi your gist is the beautiful general version of the ugly hack i'm busy writing. going to start again with yours, thank you

nilern 2021-03-22T14:57:35.176400Z

That code looks familiar :thinking_face:

borkdude 2021-03-22T14:58:19.176900Z

@robert-stuttaford I think the second thing is probably the easiest?

ikitommi 2021-03-22T15:02:07.178300Z

looking at the code, it has transduce, I never use that. I canโ€™t recall the origins of the code, just from one of the zillion scratch files. could be @nilern s work too :thinking_face:

nilern 2021-03-22T15:06:37.180300Z

Looking at the Metosin Slack history I did the unwrap and collect bits and remarked that the prewalk makes it O(n^2) but it only matters if there are large map keys or set members which hardly ever happens...

nilern 2021-03-22T15:08:28.180800Z

Anyway it took a while to write so it would be cool if it found some use