Fork me on GitHub
#malli
<
2024-03-08
>
Henrik Larsson17:03:59

How can I construct a set spec that assert the present and absence of certain values. I am generating specs dynamically based on parsed expressions so I would need a dynamic way to construct my spec. What I am looking for is something like.

(mg/generate
 [:and
  [:set [:enum 1 2 3 4 5 6 7 8 9]] ; This is the full domain of the set, all combinations should be generated and then filtered based on predicated as described below 
  ; ex1 set should include 1 AND 4 but NOT 8 other values are ok
  ; ex2 set should include 1 AND 4 but NOT 8 other values NOT ok
  ; ex3 set should include 3 OR 4 but not both
  ])

Noah Bogart17:03:37

maybe this should be :or schemas, a union of different conforming values

Henrik Larsson17:03:32

No each rule I have now will restrict the set in some way and all the restrictions need to be valid for the set to be valid. I have the same setup for a map and it works great, however I can not find any good example how to restrict a set/collection using Malli. Or maybe I misunderstand what you mean, can you give some example code that would generate what I want using :or?

escherize17:03:19

For the example you gave, you use :fn schemas to invalidate certain shapes

1
escherize18:03:12

I filled in ex3 here:

(mc/validate
 [:and
  ;; This is the full domain of the set, all combinations should be generated and then filtered based on predicated as described below
  [:set [:enum 1 2 3 4 5 6 7 8 9]]
  
  ;; ex1 set should include 1 AND 4 but NOT 8 other values are ok
  ;; ex2 set should include 1 AND 4 but NOT 8 other values NOT ok
  ;; ex3 set should include 3 OR 4 but not both
  [:fn (fn [s] (not (and (contains? s 3) (contains? s 4))))]
  ]
 #{3 4})
;; => false

Henrik Larsson18:03:28

Is there any way to force more then 100 tries when you generate values, looking at the code I get the feeling that this value is not meant to be changed?

escherize18:03:47

for generating tricky datastructures, there are some tools, the simplest is supplying elements with :gen/elements (these all go on the and), or :gen/fmap which would be a function that can return valid sets for you

escherize18:03:00

I think :gen/gen can be a generator. hangon I’ll find the docs

Henrik Larsson18:03:26

The simplest would be just to be able to generate more values, the shape is not that tricky and all combinations would probably be generated for 5000 tries. So would be nice to just be able to increase the number of tries. Currently I do this by not wrapping the first clause in an and, instead I just generate a value and then filter if over the validation of the remaining schema validations. Then I can loop this until I find a match. Since I generate the schema dynamically it is nice to not have not to custom schemas. I am trying to keep them simple as possible and then rather just spend some more time on random generation.

Henrik Larsson18:03:33

As I understanding if I am to do what you say I would need to dynamically build but generators rather then schemas?

ikitommi07:03:55

Building a generator dynamically too would make sense here.

ikitommi07:03:33

If someone has a good proposal for a generator combinator thing in malli, happy to hear. E.g. where :and would collect generator part from it's children and construct a functioning generator out of that automatically.

Henrik Larsson08:03:12

I am now using m/generator to get the generator then I can use test.check instead. Combining that with malli specs has really been a nice fit for my use case so far.