test-check

2017-04-26T12:46:12.804335Z

I'm working on a biginteger generator for test.check, presumably to be used by spec as well (directly or indirectly); the main decision to make is what the distribution should be. Here's what I've got so far, interested in any comments. https://gist.github.com/gfredericks/b6b59f1c531dc36017e45f2f0beeff9e

bbloom 2017-04-26T18:39:31.548604Z

let’s say i want to test some stateful thing by generating a sequence of actions to perform

bbloom 2017-04-26T18:39:42.552292Z

how do i use state to inform future generated actions?

bbloom 2017-04-26T18:41:06.582422Z

simple example: let’s say i want to test a growable array class with three methods: getLength, append, and getNth

bbloom 2017-04-26T18:41:24.588647Z

that was easy to test, but as soon as i wanted to test setNth, i was at a loss

bbloom 2017-04-26T18:41:40.594331Z

how do i make it so that setNth can check getLength first?

bbloom 2017-04-26T18:42:22.609723Z

my current hack is to just do modulo math at the time of applying the action - but that only works for this simple example & i have more complex stuff i want to test

bbloom 2017-04-26T18:45:06.669351Z

my best guest is to try defining generators recursively, deferred by an fmap or bind or something - but wasn’t sure if that was a hack or recommended or what

bbloom 2017-04-26T18:45:44.683343Z

ie parameterize them based on a model of the state

alexmiller 2017-04-26T18:46:40.704242Z

the general approach is to first build a (random) model, then produce the action list via fmap/bind

bbloom 2017-04-26T18:47:44.727291Z

not 100% sure i follow. is there a super simple example somewhere?

2017-04-26T18:49:28.764887Z

There's a whole lib for this kind of thing I think

2017-04-26T18:49:54.774350Z

https://github.com/czan/stateful-check

bbloom 2017-04-26T18:50:00.776714Z

i’m looking at two such libraries now, trying to see if they clarify things for me: stateful-check and states

2017-04-26T18:50:19.783549Z

I haven't used either, but I've reviewed stateful-check a bit and it seems solid

bbloom 2017-04-26T18:52:43.835892Z

there’s a non-trivial amount of code in this lib. i’ll study it, but i’m hoping to identify the essence of it.

2017-04-26T18:54:40.878136Z

@bbloom do you understand the idea of modeling the whole interaction ahead of time?

alexmiller 2017-04-26T18:55:21.892286Z

it’s been a while since I’ve looked at collection-check but I know it does a lot of this operation kind of thing (can’t remember if it’s stateful though)

bbloom 2017-04-26T18:56:38.921093Z

so i haven’t used test-check in anger at all, but i did successfully roll my own generative/simulation testing thing in go for a wire protocol. but that was a hierarchical temporal marckov model, i didn’t do shrinking, and i did all the validation later on the log file - worked mostly as a stress test for a long running system

bbloom 2017-04-26T18:57:28.939617Z

to get multiple tests, i didn’t have a backtracking generator type thing - instead just ran a bunch in parallel, since they were also testing limited hardware resources (ie physical devices)

2017-04-26T18:57:41.944120Z

I think the shrinking model is the biggest reason that the generate-everything-up-front approach works best

bbloom 2017-04-26T18:57:41.944563Z

so i have a bit of a grasp on the concepts, but no knowledge of the specific apis

bbloom 2017-04-26T18:58:03.952347Z

sooo now back to your question: i’m not sure what you mean “modeling the whole itneraction ahead of time”

2017-04-26T18:58:11.955111Z

the only viable alternative I've seen is the python/hypothesis approach which is extremely imperative and wildly different

bbloom 2017-04-26T18:58:16.956851Z

@alexmiller: i’m looking at collection-check, as what i’m doing is quite similar. thanks

2017-04-26T18:59:19.980384Z

@bbloom I mean the generator generates the entire intended interaction, e.g. - insert a - insert b - read 0, expect a - read 1, expect b - etc.

2017-04-26T18:59:32.985088Z

I believe this is essentially the idea behind stateful-check

2017-04-26T18:59:44.989957Z

it does all the wiring-up for you

bbloom 2017-04-26T19:00:10.999881Z

yeah, so i’m trying to test some java code that is stateful & i’m successfully generating a sequence of actions & applying them

bbloom 2017-04-26T19:00:44.012844Z

the tricky bit is the bit that stateful-check seems to address: using the state of the model in order inform the generators

2017-04-26T19:01:48.036461Z

"Why can't I call generators in the middle of my test?" is a common thing people run into; currently test.check doesn't try to give you a way to do that, but I'm not 100% convinced it can't be done

2017-04-26T19:02:00.040623Z

I've thought about it a lot; the shrinking is the hard part

bbloom 2017-04-26T19:02:38.054483Z

the fact that it’s asked for a lot leads me to wonder: is it a misunderstanding that leads to it being wanted? or it just hard to provide?

bbloom 2017-04-26T19:02:44.056933Z

ie should i be thinking about it differently?

alexmiller 2017-04-26T19:03:32.073995Z

We've done a number of sim testing projects at Cognitect that pushed this kind of thing really far

alexmiller 2017-04-26T19:04:05.085563Z

But there you don't really care about shrinking at all

bbloom 2017-04-26T19:04:51.101795Z

glad to hear that - since i had no use for shrinking in my sim test and wondered if i was just missing out

alexmiller 2017-04-26T19:05:15.110330Z

I'd ask @luke about some of those ideas - he has some libs that I think are oss that do statistical / stateful generative stuff

bbloom 2017-04-26T19:05:22.112577Z

this is the first time i wanted to test something that felt complex enough to justify test check and not complex enough to justify distributed sim testing

2017-04-26T19:05:25.113776Z

it should be possible to write a thingamajigger to call generators in the test deterministically, without doing any shrinking

alexmiller 2017-04-26T19:07:42.160522Z

In general with sim testing you want some simple (ish) model how users use the system such that you can generate realistic (ish) streams of random activity

bbloom 2017-04-26T19:08:27.175975Z

yeah - in my case, i simulated a police officer on patrol. turning cameras on and off, watching videos, driving to and from places with or without wifi, etc

bbloom 2017-04-26T19:08:39.179744Z

worked nicely

alexmiller 2017-04-26T19:09:00.187176Z

Yeah, exactly

alexmiller 2017-04-26T19:09:17.192722Z

Our domains were a lot more complicated :)

bbloom 2017-04-26T19:09:29.196922Z

i’d imagine

alexmiller 2017-04-26T19:09:54.205299Z

Cognitect does arch consult gigs, just saying :)

bbloom 2017-04-26T19:10:21.214698Z

🙂

2017-04-26T19:10:43.221791Z

...arch?

alexmiller 2017-04-26T19:11:27.236824Z

architecture

2017-04-26T19:11:45.242653Z

like...CPU chips? big buildings?

2017-04-26T19:11:53.245394Z

large-scale computer systems?

alexmiller 2017-04-26T19:11:59.247167Z

That

alexmiller 2017-04-26T19:12:12.251350Z

Really any scale :)

alexmiller 2017-04-26T19:12:23.255414Z

Architecture review

bbloom 2017-04-26T19:13:53.284572Z

i don’t work on that any more, but the team seems quite happy with the architecture i built for them - which is largely clojure/hickey-inspired, thank you very much 😉

bbloom 2017-04-26T19:16:13.328304Z

@gfredericks i don’t quite understand the api well enough yet to know if that code is useful to me

2017-04-26T19:16:26.332268Z

@bbloom I added an example

2017-04-26T19:16:44.338113Z

the idea is you generate one of these fs and then you can call it from the body of your property as many times as you want with arbitrary generators

2017-04-26T19:17:35.354413Z

it's a stateful function, whose state is randomness derived from the normal test.check source of randomness, so the whole thing should be deterministic as long as you don't call the function in nondeterministic ways

bbloom 2017-04-26T19:18:12.366392Z

i’m not sure i need something that sophisticated

2017-04-26T19:19:30.392723Z

so e.g., if you have your stateful java collection and want to generate a valid index, you'd call (f (gen/choose 0 (dec (.getLength thingamajig))))

2017-04-26T19:20:00.402933Z

and that call would return an index between 0 and (dec (.getLength thingamajig))

2017-04-26T19:20:18.409355Z

the sophistication is mostly to satisfy my own ideals of reproducibility

2017-04-26T19:20:40.416608Z

if you couldn't care less about that you can just call gen/generate and not bother with any of this

bbloom 2017-04-26T19:25:10.507121Z

so i don’t actually need to call .getLength on the real stateful object during test execution

bbloom 2017-04-26T19:25:19.510650Z

i just want to track some state in the model & use that to inform which actions to generate

bbloom 2017-04-26T19:25:51.521240Z

looking at collection-check, they seem to use clojure’s types as a model

bbloom 2017-04-26T19:26:15.528782Z

but zach does basically what i mentioned earlier: he generates wild indexes for nth/assoc and then fixes them up during execution

bbloom 2017-04-26T19:28:00.563694Z

how much am i expected to know about the rose tree stuff in order to make effective use of test-check? this stateful-check thing manipulates it quite a bit, plus has some gen-do monadic bind syntax etc that seems redundant w/ fmap, gen/let, etc

2017-04-26T19:28:41.576994Z

users don't normally need to care about the rose tree

2017-04-26T19:29:28.592746Z

my guess is that stateful-check's low-level internals are an attempt to shrink better than the naive shrinking you get by making a pile of binds

2017-04-26T19:29:46.598551Z

gen/bind is pretty dumb about shrinking

2017-04-26T19:30:11.606907Z

and stateful-check might be able to make assumptions that bind can't

2017-04-26T19:30:53.621434Z

maybe a simple example of generating a model with actions would be helpful

2017-04-26T19:31:28.633335Z

gimme a minute

bbloom 2017-04-26T19:31:43.638354Z

thanks for your help. greatly appreciated

bbloom 2017-04-26T19:31:47.639425Z

you too @alexmiller

bbloom 2017-04-26T19:34:05.685354Z

i’m still reading and trying to make sense of stateful-check, but some things concern me

bbloom 2017-04-26T19:34:29.693413Z

for example, command argument generation seems to attempt to implicitly and recursively lift values to be generators

bbloom 2017-04-26T19:34:45.698776Z

which seems like magic i don’t want, at least not before i am comfortable w/ the monadic api

bbloom 2017-04-26T19:35:20.710656Z

meanwhile, i’m failing to extract the essence from the core of the generate-commands* routine

2017-04-26T19:37:04.745365Z

yeah I've never liked the values-can-be-generators idea

2017-04-26T19:37:23.751369Z

in practice it's probably not too confusing

2017-04-26T19:37:41.757342Z

....maybe

bbloom 2017-04-26T19:37:56.762276Z

….maybe with a type system? 😛

2017-04-26T19:38:07.765838Z

yeah 🙂

2017-04-26T19:41:00.823637Z

could fancy it up to add the intermediate state at each step, or generate random assertions if you don't want to just compare the whole map

2017-04-26T19:41:12.827521Z

note that the gen/lets are where bind is happening

2017-04-26T19:42:12.847616Z

there might be stack problems with this; maybe that's another reason for stateful-check's complexity

2017-04-26T19:42:26.852946Z

e.g., if you wanted to generate 10000 actions you might have problems

bbloom 2017-04-26T19:42:33.855297Z

yeah - so i tried something like this and got a stackoverflow

bbloom 2017-04-26T19:42:59.864147Z

but thanks, this example is much closer to my intuition

2017-04-26T19:43:20.870992Z

I just generated 20,000 of these up to the normal max-size of 200 and got no exceptions

2017-04-26T19:43:49.880193Z

looks like the sizes don't get above 20 though

2017-04-26T19:43:52.881338Z

so that might be bad

2017-04-26T19:44:34.895503Z

that's probably why it's not SOing 🙂

bbloom 2017-04-26T19:44:44.898604Z

😛

2017-04-26T19:44:50.900820Z

to get larger you'd use gen/frequency instead of gen/one-of and tweak the weights

2017-04-26T19:45:02.904525Z

alternately you could generate a size up front and pass that through the recursion

bbloom 2017-04-26T19:45:02.904694Z

well you had equal assoc/dissoc frequency

bbloom 2017-04-26T19:45:07.906438Z

yeah

bbloom 2017-04-26T19:45:25.912581Z

i wasn’t doing any dissoc to reduce the size, so it grew faster

2017-04-26T19:46:02.924858Z

I think stack problems would be related to action-count, not the size of the state

bbloom 2017-04-26T19:46:36.935823Z

oh, hmm that’s right

bbloom 2017-04-26T19:46:47.939696Z

not sure what i did, since it’s like 5000 undos ago 🙂

bbloom 2017-04-26T19:48:10.966917Z

with respect to shrinking: how well does that work as things grow more complex?

bbloom 2017-04-26T19:48:35.975437Z

i’m somewhat skeptical it can work well for stateful tests - since each removed operation can potentially invalidate the remainder of the operations

2017-04-26T19:48:42.977981Z

that's exactly the problem

2017-04-26T19:49:15.989074Z

if you're using the naive bind structure in my example, what would happen is if test.check tries to shrink an earlier action it will generate a totally new set of following actions

2017-04-26T19:49:28.993631Z

it might be that in practice this isn't as bad as it sounds

2017-04-26T19:49:44.999060Z

I get almost 0 complaints about this

bbloom 2017-04-26T19:50:30.015033Z

which of course could mean 1) it’s perfect 2) no body is using it or 3) people don’t understand it well enough to complain about it without embarassing themselves like i’m doing now 🙂

2017-04-26T19:50:31.015112Z

I made a ticket about it at least, but don't know if it will lead anywhere https://dev.clojure.org/jira/browse/TCHECK-112

2017-04-26T19:50:43.019436Z

yeah exactly

2017-04-26T19:50:57.024050Z

2 isn't plausible; people are definitely using bind, at least via gen/let

2017-04-26T19:51:08.027810Z

in fact gen/let tends to encourage over using bind

2017-04-26T19:51:41.038743Z

i.e., using bind to combine two independent generators in a way that you could do with gen/tuple

bbloom 2017-04-26T19:52:23.052676Z

does tuple do “parallel” bind? ie it shrinks from any position fairly?

bbloom 2017-04-26T19:52:39.058352Z

and bind is “left biased”?

2017-04-26T19:53:02.066162Z

that...might be right

2017-04-26T19:53:08.067994Z

tuple shrinks each thing independently

2017-04-26T19:53:19.071834Z

you can always rewrite tuple with bind but not vice versa

bbloom 2017-04-26T19:53:22.072739Z

gotcha - so tuple is applicative

2017-04-26T19:53:32.076380Z

that's probably true

bbloom 2017-04-26T19:53:58.084731Z

ok - so i guess i’ve confirmed 1) i understand this and 2) i still have no idea how to use the API 😛 thanks

bbloom 2017-04-26T19:54:11.088748Z

i’ll keep playing with it

bbloom 2017-04-26T19:54:21.092255Z

will let you know how it turns out

2017-04-26T19:54:24.093322Z

oh well; do let me know if you have more questions or accusations

bbloom 2017-04-26T19:55:09.107480Z

heh, i hope the accusations thing was a joke and/or not about me. let me know if i failed to politely convey my gratitude!

2017-04-26T19:55:33.115382Z

oh yeah sorry, definitely a joke

bbloom 2017-04-26T20:36:05.936535Z

so shrinking only removes elements from the reproduction, right? no rewriting in any way?

bbloom 2017-04-26T20:39:50.009704Z

i’m attempting a “loose” approach to action generation - will leave state out of that part & use state in the code that “runs” the actions. that will screw up shrinking probably, but i’ll deal with that when i have trouble pinpointing an issue in the future

bbloom 2017-04-26T20:41:56.051420Z

so like if i were testing that random access stack example from before, actions may just be like “do 5 pushes, then randomly swap elements 8 times, then do two pops, etc….”

bbloom 2017-04-26T20:42:00.052645Z

and see how that does for me

bbloom 2017-04-26T21:02:17.461422Z

is there a generator that will (with high likelihood?) test boundary cases such as max/min integers? - the large-integer generator doesn’t seem to explicitly select those, so i guess i need to just use one-of or frequency to ensure those cases get covered (which is relevant to me b/c the code under test does some low level binary fiddling sometimes & i want to make sure it doesn’t screw that up)

bbloom 2017-04-26T21:54:46.351973Z

i’ve gotta say - the learning curve is relatively steep with this stuff

bbloom 2017-04-26T21:55:14.359124Z

but that might just be b/c i’m not comfortable using stuff w/o understanding it - and the monadic stuff makes it a bit opaque

bbloom 2017-04-26T21:57:57.401278Z

for example, it wasn’t immediately clear what a “property” really was under the hood (just a generator that produces a particular shape of data) or that it was safe to use side effecting asserts in such a property

bbloom 2017-04-26T21:58:12.404585Z

the readme shows an example that returns a boolean

bbloom 2017-04-26T21:58:30.409487Z

but if i tested each property individually, my tests would be far too slow

bbloom 2017-04-26T22:33:19.867135Z

spotted http://blog.colinwilliams.name/blog/2015/01/26/alternative-clojure-dot-test-integration-with-test-dot-check/ - which seems like an improvement

bbloom 2017-04-26T22:33:45.872153Z

while i’m rambling to myself (or whoever is listening?):

bbloom 2017-04-26T22:34:27.881050Z

i’m not quite happy with my attempts to generate action sequences yet - i’m getting more variability in parameters and less variability in the sequence of actions - i’m not sure how to influence that properly yet

bbloom 2017-04-26T22:34:59.888237Z

to use the stack example again, i’m getting a lot of tests that try just using different elements to push on the stack, but really what i want is a lot more tests that have longer sequences of pushes/pops/etc

bbloom 2017-04-26T22:38:48.932883Z

the internal generation strategy doesn’t appear to be documented anywhere. is it a top-down strategy? or a bottom-up one? or some hybrid? i can’t really tell easily, nor what the implications of that would be

2017-04-26T23:03:27.209956Z

Hi

2017-04-26T23:05:16.229023Z

https://clojurians.slack.com/archives/C0JKW8K62/p1493238965936535 If you're talking about something like the event modeling code I shared earlier, new events CAN be generated during shrinking

bbloom 2017-04-26T23:05:47.234527Z

how does that work?

2017-04-26T23:06:48.245155Z

So bind generates a value from one generator, then uses the user supplied function to create a new generator and generates a value from that

2017-04-26T23:06:57.246868Z

That much is straightforward?

bbloom 2017-04-26T23:07:01.247489Z

yeah

2017-04-26T23:07:15.249720Z

There are two ways to shrink from there

2017-04-26T23:07:46.255163Z

You can shrink the thing generated in the second step - that's the easy way

2017-04-26T23:09:04.267929Z

The hard way is to shrink the thing generated in the first step, because then you're obligated to call the user function with the new smaller value, which returns a potentiality entirely new generator that may have nothing to do with the original second-step value

2017-04-26T23:09:18.270386Z

In any case you can't assume they're related

2017-04-26T23:10:02.277957Z

So the only way to proceed is to generate something entirely new and probably unrelated from that new generator

2017-04-26T23:10:50.286158Z

I suppose you could at least use the same randomness, just in case that helps. I think test.check already does that

bbloom 2017-04-26T23:11:24.291775Z

i guess i don’t understand the shrinking process at all then - b/c what you’re saying here doesn’t make sense to me

bbloom 2017-04-26T23:12:06.298646Z

is it just that shrinking re-runs the generators in some “shrink mode” and can do whatever the hell it wants?

2017-04-26T23:12:13.299619Z

So shrinking is not an independent operation on generated values -- it's essentially done at generation time, by each generator in the composition

2017-04-26T23:12:42.304459Z

This is a difference from haskell as I understand it

bbloom 2017-04-26T23:13:18.310521Z

so is shrinking just “re-generate, but smaller”?

2017-04-26T23:13:25.311738Z

No

bbloom 2017-04-26T23:13:29.312327Z

i would have expected generate & shrink to be two methods on a protocol

2017-04-26T23:13:55.316734Z

It's not - a generator returns a Very Large lazy tree of values

2017-04-26T23:14:22.321406Z

The root is the generated value; each node's children are ways to shrink from the value at that node

2017-04-26T23:14:32.323041Z

The shrinking algorithm walks that tree

2017-04-26T23:14:54.326582Z

Higher order generators combine these trees

2017-04-26T23:15:10.329348Z

E.g., gen/fmap calls rose/fmap

bbloom 2017-04-26T23:15:15.330225Z

ah, see, my mental model was one of producer/consumer

bbloom 2017-04-26T23:15:20.331174Z

emitting generated values

bbloom 2017-04-26T23:15:28.332287Z

totally wrong apparently 😛

2017-04-26T23:15:59.337676Z

I can't remember if reid came up with it himself, but people don't seem to expect it

2017-04-26T23:16:17.340618Z

I think maybe he guessed the impl based on erlang's API

2017-04-26T23:16:51.345959Z

So perhaps john hughes is the source

2017-04-26T23:18:06.357902Z

My talk Purely Random is about a lot of these details and about how I converted test.check to use an immutable rng

bbloom 2017-04-26T23:18:20.360206Z

i might have to watch that

2017-04-26T23:19:18.369684Z

I wish I knew how to get slack to notify me of any activity in this channel

2017-04-26T23:19:43.373810Z

Maybe I just did that

bbloom 2017-04-26T23:19:51.375196Z

so if i do (gen/sample (gen/list gen/int)) for a large sample size - and take the max of the lengths of the lists, it seems to stop at 99

bbloom 2017-04-26T23:19:55.375828Z

i have no idea what controls that value

bbloom 2017-04-26T23:20:01.376712Z

and i have no idea how to go about figuring it out lol

2017-04-26T23:20:31.381879Z

There's a doc page in the repo on growth and shrinking

2017-04-26T23:21:15.389165Z

The other thing people don't usually expect is that growth and shrinking are not related

bbloom 2017-04-26T23:21:52.395388Z

the word “grow” does not appear in any of the .md files in the doc directory 😛

2017-04-26T23:22:08.398040Z

Should be in one of the titles

2017-04-26T23:22:19.400095Z

Growth

bbloom 2017-04-26T23:22:44.404116Z

nope - maybe it’s somewhere else? i’ve read all the docs in the repo that i could find, heh

2017-04-26T23:22:51.405318Z

Um

2017-04-26T23:22:55.405886Z

I'll find it

2017-04-26T23:23:41.413559Z

It's new, maybe you didn't have it locally

bbloom 2017-04-26T23:24:16.419362Z

ah, sorry - my bad. my last pull failed, i have code from december

bbloom 2017-04-26T23:24:33.422470Z

will read that

bbloom 2017-04-26T23:25:33.431979Z

lol, i had code checked out from ~2 days before you added that

2017-04-26T23:25:41.433292Z

Haha

bbloom 2017-04-26T23:33:55.514577Z

ok - that doc gave me a much better idea of how growth & size works. thank you!

bbloom 2017-04-26T23:34:03.515821Z

however, it didn’t give me much insight in to shrinking

bbloom 2017-04-26T23:34:16.517655Z

other than the fact that shrinking is unrelated to growth 😛

bbloom 2017-04-26T23:36:05.535262Z

also, i’m still not clear on how size is related to whether or not a generator is run

bbloom 2017-04-26T23:36:12.536146Z

for example, if i do this: (gen/sample (gen/vector (gen/resize 0 gen/int)))

bbloom 2017-04-26T23:36:37.540172Z

i get stuff like [0] multiple times, which i’d expect - but it suggests that the frequency of generator execution is unrelated to the size of the range of the generator

bbloom 2017-04-26T23:39:25.566253Z

in order to have any confidence… i guess i’m going to need to understand this rose tree thing….

bbloom 2017-04-26T23:41:52.588764Z

found this explanation: http://reiddraper.com/writing-simple-check/

2017-04-26T23:44:22.612665Z

gen/generate can give you better control than sample for playing with sizing

bbloom 2017-04-26T23:46:13.629842Z

ok, i think i understand the rose tree / shrinking now….

bbloom 2017-04-26T23:46:50.635902Z

Reid’s rationale suggests getting a shrinker “for free” from the generator, but i’m skeptical of that approach vs the two-method approach that apparently haskell uses

2017-04-26T23:49:26.659744Z

You're thinking a generator is an implementation of a protocol with one method for generating a value and another for shrinking it?

bbloom 2017-04-26T23:49:49.663207Z

i did think that, which is apparently true in QuickCheck according to Reid’s blog post

2017-04-26T23:50:26.668902Z

Consider how to implement fmap then

2017-04-26T23:50:55.673324Z

(gen/fmap str gen/nat), e.g.

2017-04-26T23:51:33.678959Z

How do you shrink "42" given a function that can shrink 42

bbloom 2017-04-26T23:53:21.695107Z

same way it does now? seems like it just divides by two in some complex round-about fashion

2017-04-26T23:53:35.697273Z

It's a string, not a number

bbloom 2017-04-26T23:53:55.700276Z

ah

bbloom 2017-04-26T23:54:06.701966Z

sorry, i misread

bbloom 2017-04-26T23:55:26.714292Z

(defn fmap [f x] (reify IGenerator (gen [this ctx] (f (gen x ctx))) (shrink [this ctx value] (f (shrink x ctx))))

bbloom 2017-04-26T23:55:40.716619Z

😛

bbloom 2017-04-26T23:57:00.728584Z

er sub that second value with x

bbloom 2017-04-26T23:57:05.729382Z

or second x with value

bbloom 2017-04-26T23:57:08.729662Z

you get the idea

bbloom 2017-04-26T23:57:20.731540Z

but yeah, i get my intuition doesn’t match reality

bbloom 2017-04-26T23:58:12.738990Z

but as far as i can tell, the public API of test.check doesn’t expose the rose tree stuff

bbloom 2017-04-26T23:58:22.740540Z

all the constructors for generators assume you return a value, not a rose tree

bbloom 2017-04-26T23:59:05.746570Z

which i guess that explains why stateful-check mucks around with internals a bit