Fork me on GitHub
#malli
<
2020-03-06
>
teodorlu10:03:35

Just pasted a huge JSON sequence into https://malli.io and got a nice, readable schema out, with optional values, sequences and everything in place. What a pleasure! Thanks! 🙏

đź‘Ť 4
mike_ananev10:03:44

Hi! I'm trying to mu/merge two specs [:map ...] and [:multi ... ]. Why I always have only one of them in a result? Is there any examples?

ikitommi10:03:58

@mike1452 they are of different types and the last one wins (like with clojure.core/merge)

ikitommi10:03:26

but, the merge is far from perfect, not sure what happens if one merges two multis for example.

ikitommi10:03:15

i would assume that they get merged together, but not sure if the impl has a special condition just for :map (should be for MapSchemas, which both multi and map are)

ikitommi10:03:06

… with that change, could actually merge those togerher. not sure if that’s a good idea thou :thinking_face:

mike_ananev10:03:27

another question: when I generate multi spec, why I have random values in dispatch type instead of those values that in multi spec?

(def mumap
  [:multi {:dispatch :db/shared-channel}
   ["folder" [:map [:db/shared-channel string?] [:db/shared-channel-folder string?]]]
   ["web" [:map [:db/shared-channel string?] [:db/shared-channel-url string?]]]])

mike_ananev10:03:12

instead of "folder" and "web" in shared channel i have random values. I expect only these two values

ikitommi10:03:35

the generator is picked from the multi branch, which doesn’t limit the key.

ikitommi10:03:54

try:

[:map [:db/shared-channel [:= "folder"]] [:db/shared-channel-folder string?]]

đź‘Ť 4
mike_ananev10:03:03

It helps, thanks!

ikitommi10:03:47

as the dispatch can be anything, it’s non-trivial to use the branch name in a generator automatically

mike_ananev10:03:25

I have data structure in which first half is always the same and second half is multi-spec based on :db/shared-channel value

(def sample1 {:db/type                  "h2",
              :h2/user                  "sa",
              :h2/filename              "./boxdb",

              :db/shared-channel        "folder",
              :db/shared-channel-folder "dev/resources/shared"})

(def sample2 {:db/type               "h2",
              :h2/user               "sa",
              :h2/filename           "./boxdb",

              :db/shared-channel     "web",
              :db/shared-channel-url ""})

mike_ananev10:03:57

I did multi spec

(def mumap
  [:multi {:dispatch :db/shared-channel}
   ["folder" [:map [:db/shared-channel [:= "folder"]] [:db/shared-channel-folder string?]
              [:db/type [:and string? [:fn {:error/message "should not be empty string"} not-empty-string?]]]
              [:h2/filename {:default "./boxdb"} string?]
              [:h2/user {:default "sa"} string?]]]
   ["web" [:map [:db/shared-channel [:= "web"]] [:db/shared-channel-url string?]
           [:db/type [:and string? [:fn {:error/message "should not be empty string"} not-empty-string?]]]
           [:h2/filename {:default "./boxdb"} string?]
           [:h2/user {:default "sa"} string?]]]])

mike_ananev10:03:35

Question: how to avoid duplication of first half? (merge is not work like in spec1)

mike_ananev10:03:41

I need something like:

[:map
 [:db/type [:and string? [:fn {:error/message "should not be empty string"} not-empty-string?]]]
 [:h2/filename {:default "./boxdb"} string?]
 [:h2/user {:default "sa"} string?]
 [:multi {:dispatch :db/shared-channel}
  ["folder" [:map [:db/shared-channel [:= "folder"]] [:db/shared-channel-folder string?]]]
  ["web" [:map [:db/shared-channel [:= "web"]] [:db/shared-channel-web string?]]]]]

ikitommi10:03:01

many ways to do that, one would be to create a basemap, and merge that in all branches:

(def BaseMap
  [:map
   [:db/type [:and string? [:fn {:error/message "should not be empty string"} not-empty-string?]]]
   [:h2/filename {:default "./boxdb"} string?]
   [:h2/user {:default "sa"} string?]])

(def Multi
  [:multi {:dispatch :db/shared-channel}
    ["folder" (mu/merge BaseMap [:map [:db/shared-channel [:= "folder"]] [:db/shared-channel-folder string?]])]
    ["web" (mu/merge BaseMap [:map [:db/shared-channel [:= "web"]] [:db/shared-channel-web string?]]])]])

ikitommi11:03:57

the oldest non-resolved issue in malli: https://github.com/metosin/malli/issues/14

ikitommi11:03:55

kinda like select in spec2.

teodorlu11:03:58

https://malli.io seems to fail to infer the schema of values containing instants. Example value:

{:some-date #inst "2020-02-02"}
Is this expected behavior?

ikitommi11:03:32

I would not expect that. from a repl:

(require '[malli.provider :as mp])

(mp/provide [{:some-date #inst "2020-02-02"}])
; => [:map [:some-date inst?]]

đź‘Ť 4
Vincent Cantin11:03:37

@ikitommi for https://github.com/metosin/malli/issues/180, do we want to also add support for :+ :* :? and some other operators? in the :cat driven sequence?

Vincent Cantin11:03:29

I hope to start this issue if I have some time this weekend.

ikitommi11:03:20

that would be awesome!

Vincent Cantin11:03:47

(if I have time)

ikitommi11:03:05

did an intial spike, using the net.cgrand/seqexp , something like:

ikitommi11:03:17

(def registry
  (merge
    default-registry
    {:cat (cat-schema :cat)
     :| (cat-schema :|)
     :* (regex-schema :* se/*)
     :*? (regex-schema : se/*?)
     :+ (regex-schema :+ se/+)
     :+? (regex-schema :+? se/+?)
     :? (regex-schema :? se/?)
     :?? (regex-schema :?? se/??)
     :repeat (regex-schema :repeat? se/repeat)
     :repeat? (regex-schema :repeat? se/repeat?)}))

ikitommi11:03:39

didn’t get very far thou.

Vincent Cantin11:03:42

Could you push this to a branch ?

ikitommi11:03:30

actually, that’s about it. here are the codes I was playing with:

(require '[net.cgrand.seqexp :as se])

(se/exec
  (se/cat
    (se/as :x int?)
    (se/as :y int?)
    (se/as :rest2 (se/* string?)))
  [1 2 "kikka" "kukka"])
; {:rest (), :match (1 2 "kikka" "kukka"), :x (1), :y (2), :rest2 ("kikka" "kukka")}

[:cat
 [:x int?]
 [:y int?]
 [:rest [:* string?]]]

(se/exec-tree
  (se/cat
    (se/*
      (se/as [:opts]
             (se/cat
               (se/as [:opts :x] string?)
               (se/| (se/as [:opts :s] string?) (se/as [:opts :b] boolean?))))))
  ["-server" "foo" "-verbose" true "-user" "joe"])

(se/exec
  (se/cat
    (se/as :x (se/repeat 1 int?))
    (se/as :y int?)
    (se/as :restz (se/* string?)))
  [1 2 "kikka" "kukka"])
; {:rest (), :match (1 2 "kikka" "kukka"), :x (1), :y (2)}

(se/*
  (se/as [:sections]
         (se/cat :h1
                 (se/as [:sections :ps]
                        (se/+ :p)))))

ikitommi11:03:00

just updated https://github.com/cgrand/seqexp/issues/11, which is about using seqexp here

ikitommi11:03:17

not going to do anything for this for now, so all yours 🙂

Vincent Cantin11:03:27

I will start by writing tests

Vincent Cantin11:03:45

.. then the implementation to make them pass

ikitommi11:03:26

sure, doesn’t do anything thou

borkdude11:03:04

why not just "steal" the impl from spec?

teodorlu11:03:43

Using malli.provider/provide does the trick! Thanks. I was surprised by what happened when I forgot to provide a sequence:

(mp/provide {:some-date #inst "2020-02-02"})
;; => [:vector some?]
com.github.metosin/malli {:git/url ""
                            :sha "9db4ff998b641d4a0cff4eb6d772fd0cb5d3b56c"}

ikitommi12:03:51

Maps turn into sequences in Clojure, so it’s a sequence of a vector of keyword? and inst? , which both are some?

(seq {:some-date #inst "2020-02-02"})
;; => ([:some-date #inst"2020-02-02T00:00:00.000-00:00"])

teodorlu11:03:54

Ah, that makes sense. Thanks.

ikitommi11:03:01

taking the impl from spec is always an option.

ikitommi11:03:48

last time I checked, the regexs where implemented using regular hashmaps in spec, would need at least some wrapping, don’t want to reserve maps for any one schema (family) type.

ikitommi11:03:00

grand’s lib is 6y+ old, so “prior art” for this and all libs by Christophe have been great, also perf).