Fork me on GitHub
#malli
<
2022-10-14
>
robert-stuttaford06:10:27

where can i learn about how to write generators for complex :multi specs? currently butting my head against this:

(mg/sample ;; OK
   [:map [:db/id {:optional true}]])

  (mg/sample ;; OK
   [:multi {:dispatch :type}
    [:some.type/value
     [:map
      [:some/key :string]]]])

  (mg/sample ;; Couldn't satisfy such-that predicate after 100 tries.
   [:and
    [:map [:db/id {:optional true}]]
    [:multi {:dispatch :type}
     [:some.type/value
      [:map
       [:some/key :string]]]]])

pithyless10:10:56

Two things come to mind: 1. I think your :multi map needs to include the dispatch key (otherwise it won't be generated correctly) 2. Is it feasible for you to merge the multi map? This would work:

(mg/sample
    [:multi {:dispatch :type}
     [:some.type/value
      (mu/merge 
        [:map
         [:type [:= :some.type/value]]
         [:some/key :string]]
        [:map
         [:db/id :string]])]])

💡 1
pithyless10:10:50

Nevertheless, subscribing to this thread in hope that there is a better way to solve this problem (have encountered it as well).

robert-stuttaford10:10:55

thanks that gives me some things to try 😅

Stig Brautaset15:10:01

This is a topic I’m interested in too. I tried to spec out the complete state of a board game (a toy project), and it was very easy to get into a situation where the generation failed.

robert-stuttaford07:10:40

ok so it seems i can't use mu/union because of cyclic dependencies between my specs; so it looks like generating data is off the table for me here. just in case there's something i'm missing, if you've any advice @U055NJ5CC, i'd greatly appreciate it 🙂

Ben Sless05:10:42

You can break cyclic dependencies with a ref spec

robert-stuttaford07:10:49

i am using ref specs - the issue is that to use mu/union, the keyword specs you give it have to be registered already, which forces a specific code loading order, but i can't ensure that order because the spec is for an entity that links to other entities of the same spec, linked-list type 'next-item' / 'parent-item' stuff. the specs work as validation (with :ref specs); it just doesn't work to generate

Ben Sless07:10:33

Huh, weird. Want to send the complete schema (or minimal repro)? I think I managed that once

robert-stuttaford07:10:58

apex spec is :multi. all of the internal impls all a shared base :map spec, and some are that base plus some extra :map spec. the base :map spec has :refs to the apex :multi spec. then, after all that, i am also wrapping this in a generic Datomic id handling system, where whenever i specify a :db.type/ref, there's a wrapper spec that allows either a long, a tempid, a map with a long or tempid, or an :and of :map :db/id optional and whatever the actual spec was; in this case, the apex multi spec. all of this validates, but it doesn't generate 😬 i suspect what i want is conceptually not possible, but i don't know enough about malli or the domain it operates in to know for sure.

robert-stuttaford07:10:43

i can provide pseudocode if that was too eye-watering to reason through 🙂

Ben Sless08:10:18

Have you tried using a schema schema to wrap it all together?

Ben Sless08:10:39

e.g. [:schema {:registry everything-you-just-said} ::apex]?

robert-stuttaford08:10:28

ah, i am incrementally building this up with calls to swap! on an atom that has been given to (mr/set-default-registry! (mr/mutable-registry *registry)) . i'll work up a minimal repro version of what i said without that and with [:schema {:registry ...} ::apex] and see how it goes!

ikitommi15:10:06

Hi, some comments on this: • :and generator generates on the first and just validates the generated result with the validators of the rest of the childs -> not likely to match • I was sure that there was an example on :multi + gen in README, but it seems there is not! but for now, there is no logic to merge the dispatch types into the schemas, so you have to do it yourself. Here’s an example from README:

[:multi {:dispatch :type
         :decode/string #(update % :type keyword)}
 [:sized [:map [:type [:= :sized]] [:size int?]]]
 [:human [:map [:type [:= :human]] [:name string?] [:address [:map [:country keyword?]]]]]]
• should find time to work on https://github.com/metosin/malli/issues/264, would help here and I think this is really important anyway • if all childs are maps and the :dispatch value is a Keyword or a String, there could be a helper to push the key + value into the maps. this helper could be used in gen, json-schema etc. namespaces to make it work correctly • I’m thinking :and is bad (like Plumatic Team found out) and there should be something like :constrained instead. • sorry @robert-stuttaford, I guess this didn’t help you, but that’s something for the background. Looking forward to your repro + Ben’s solution 🙂

robert-stuttaford15:10:27

thanks @U055NJ5CC, this is useful info!

Ben Sless16:10:12

Based on some experience playing with kanren, should and implement a bind of one generator to the next?

Ben Sless17:10:17

Not sure if it's viable, but a schema can describe how it can return a generator from a seed value to build up with bind. That way, for example, a map schema would use merge

1
ikitommi15:10:06

Hi, some comments on this: • :and generator generates on the first and just validates the generated result with the validators of the rest of the childs -> not likely to match • I was sure that there was an example on :multi + gen in README, but it seems there is not! but for now, there is no logic to merge the dispatch types into the schemas, so you have to do it yourself. Here’s an example from README:

[:multi {:dispatch :type
         :decode/string #(update % :type keyword)}
 [:sized [:map [:type [:= :sized]] [:size int?]]]
 [:human [:map [:type [:= :human]] [:name string?] [:address [:map [:country keyword?]]]]]]
• should find time to work on https://github.com/metosin/malli/issues/264, would help here and I think this is really important anyway • if all childs are maps and the :dispatch value is a Keyword or a String, there could be a helper to push the key + value into the maps. this helper could be used in gen, json-schema etc. namespaces to make it work correctly • I’m thinking :and is bad (like Plumatic Team found out) and there should be something like :constrained instead. • sorry @robert-stuttaford, I guess this didn’t help you, but that’s something for the background. Looking forward to your repro + Ben’s solution 🙂