Fork me on GitHub
#clojure-spec
<
2022-07-28
>
Ben Sless08:07:06

This is a bit more relevant to generators, but how can I use the generated result from one generator as the baseline for another? I want to be able yo generate sequences representing events in time or request and response, and for all of them to share an ID, for example

lassemaatta08:07:02

or perhaps fmap

lassemaatta08:07:18

https://www.youtube.com/watch?v=F4VZPxLZUdA presentation by Gary Fredericks gives nice examples on how to compose generators

Ben Sless08:07:12

Just guessing because I haven't looked yet, but bind sounds exactly right, thanks! My intuition is from what it means for implementing logic engines, where bind is akin to conjunction

Ben Sless08:07:40

So let's just run this through, I have a generator for maps, and I want some values from g1 to continue through g2, most brutal way would be merging these values on top of what comes out of g2, but there surely is a better way

lassemaatta08:07:05

I'm no expert on the matter, but I remember once doing something like (->> (gen/tuple random-id-gen seq-of-requests-gen) (fmap (fn [[id reqs]] (map #(assoc % :id id) reqs)))) when I just wanted to "give me some random requests with the same random id" etc.

pppaul14:07:19

tuple into fmap is also a solution i used for a map generator

pppaul14:07:14

i made a recursive map generator based on example data so my gen looks like (fmap (tuple (map (elements elements)))) and basically you can replace any elements with another fmap (self similar structure)

pppaul14:07:36

i saw bind when looking for my solution, but i didn't see how it was going to be better than fmap

pppaul15:07:48

merging values from previous generators is ok, those other gens are able to shrink, fmap is a bit inefficient, but maybe you could make a new generator that merging for your problem. so long as your fmap isn't doing a lot of work, i don't think it'll be a problem

Ben Sless09:07:21

Is there a way to recover the keys from a map generator before it runs?

pppaul14:07:10

i don't know what that means. recover something before it runs doesn't even sound like it makes sense logically

Ben Sless14:07:46

If I have a map generator, can I know what keys it will generate before running it?

pppaul14:07:30

(gen/map (gen/elements ...list-of-keys) (gen/elements ...list-of-vals))

Ben Sless14:07:16

I mean going the other way, I already have the map generator, I want to take it apart

pppaul14:07:19

there is also (gen/hash-map)

pppaul14:07:02

i don't get it. are you saying that you are using someone else's gen, that has fixed keys, and you can't see what keys those are from their code?

lassemaatta14:07:49

I was browsing through the generator code today and I got the impression that generator instances are opaque so you can't for example access the source spec. or at least that's my understanding of it

pppaul14:07:01

you could make a data structure that creates gens, then put that on as meta-data. but you would be creating a wrapper around the whole gen library at that point

Ben Sless15:07:03

It was a matter of efficiency. If I want to use bind to inject values from one generator to the next, there's no reason to generate those values in the second generator as well, right?

lassemaatta17:07:01

this is just a guess (and perhaps not a good idea), but if you really want to do that kind of optimization (e.g. the value is really expensive to generate and you know the qualified kw you want to replace) then perhaps you could leverage the overrides arg of clojure.spec.alpha/gen :thinking_face: for example something not completely unlike this: (bind random-id-gen (fn [id] (c.s.a/gen ::coll-of-maps {::id (fn [] (return id))}))), where you don't want to execute the default generator for ::id for each map

Ben Sless18:07:03

It's not critical, maybe I'm overly concerned about stepping into pathological situations

Mark Wardle08:08:07

I simply have functions that return generators that wrap another generator and replace the values in the generated map (using fmap) with the explicitly defined ones. Eg see https://github.com/wardle/hermes/blob/main/src/com/eldrix/hermes/rf2.clj I have lots of cases where I need to be careful to use the same identifier in many generated structures.

👍 1